From 33794f2f06a24716da6d7ef348a53bd743cfaabb Mon Sep 17 00:00:00 2001 From: Jeff Hanson Date: Thu, 2 Dec 2021 12:24:02 +1300 Subject: [PATCH] Update vignettes (#216) Update vignettes Rebuild sites Tweak grammar in some docs --- DESCRIPTION | 2 +- NEWS.md | 7 + R/ConservationProblem-proto.R | 4 +- R/Parameter-proto.R | 2 +- R/ScalarParameter-proto.R | 2 +- R/add_absolute_targets.R | 2 +- R/add_binary_decisions.R | 6 +- R/add_boundary_penalties.R | 24 +- R/add_cbc_solver.R | 4 +- R/add_connectivity_penalties.R | 16 +- R/add_contiguity_constraints.R | 16 +- R/add_cplex_solver.R | 6 +- R/add_cuts_portfolio.R | 6 +- R/add_default_decisions.R | 2 +- R/add_default_portfolio.R | 2 +- R/add_default_solver.R | 2 +- R/add_extra_portfolio.R | 4 +- R/add_feature_contiguity_constraints.R | 12 +- R/add_feature_weights.R | 14 +- R/add_gap_portfolio.R | 9 +- R/add_gurobi_solver.R | 12 +- R/add_linear_constraints.R | 14 +- R/add_linear_penalties.R | 14 +- R/add_locked_in_constraints.R | 6 +- R/add_locked_out_constraints.R | 4 +- R/add_loglinear_targets.R | 4 +- R/add_mandatory_allocation_constraints.R | 6 +- R/add_manual_bounded_constraints.R | 6 +- R/add_manual_locked_constraints.R | 8 +- R/add_manual_targets.R | 6 +- R/add_max_cover_objective.R | 8 +- R/add_max_features_objective.R | 2 +- R/add_max_phylo_div_objective.R | 2 +- R/add_max_phylo_end_objective.R | 2 +- R/add_max_utility_objective.R | 4 +- R/add_min_largest_shortfall_objective.R | 4 +- R/add_min_set_objective.R | 6 +- R/add_min_shortfall_objective.R | 2 +- R/add_neighbor_constraints.R | 14 +- R/add_proportion_decisions.R | 2 +- R/add_relative_targets.R | 2 +- R/add_semicontinuous_decisions.R | 6 +- R/add_shuffle_portfolio.R | 2 +- R/add_top_portfolio.R | 4 +- R/boundary_matrix.R | 6 +- R/compile.R | 2 +- R/connectivity_matrix.R | 8 +- R/constraints.R | 2 +- R/decisions.R | 2 +- R/deprecated.R | 2 +- R/eval_boundary_summary.R | 2 +- R/eval_connectivity_summary.R | 2 +- R/eval_cost_summary.R | 4 +- R/eval_feature_representation_summary.R | 9 +- R/eval_n_summary.R | 4 +- R/eval_rare_richness_importance.R | 2 +- R/eval_replacement_importance.R | 10 +- R/eval_target_coverage_summary.R | 35 +- R/feature_abundances.R | 10 +- R/feature_names.R | 2 +- R/importance.R | 8 +- R/marxan_boundary_data_to_matrix.R | 2 +- R/marxan_problem.R | 4 +- R/number_of_features.R | 2 +- R/number_of_planning_units.R | 2 +- R/number_of_total_units.R | 2 +- R/number_of_zones.R | 2 +- R/package.R | 2 +- R/penalties.R | 4 +- R/planning_unit_solution_status.R | 2 +- R/portfolios.R | 6 +- R/presolve_check.R | 18 +- R/problem.R | 52 +- R/rij_matrix.R | 2 +- R/run_calculations.R | 4 +- R/solve.R | 20 +- R/zone_names.R | 2 +- R/zones.R | 2 +- README.Rmd | 21 +- README.md | 96 +- _pkgdown.yml | 39 +- docs/404.html | 34 +- docs/CONTRIBUTING.html | 194 +- .../calibrating_trade-offs_tutorial.html | 892 +++ .../figure-html/unnamed-chunk-11-1.png | Bin 0 -> 283572 bytes .../figure-html/unnamed-chunk-12-1.png | Bin 0 -> 258928 bytes .../figure-html/unnamed-chunk-13-1.png | Bin 0 -> 179871 bytes .../figure-html/unnamed-chunk-16-1.png | Bin 0 -> 239957 bytes .../figure-html/unnamed-chunk-19-1.png | Bin 0 -> 92283 bytes .../figure-html/unnamed-chunk-28-1.png | Bin 0 -> 105009 bytes .../figure-html/unnamed-chunk-29-1.png | Bin 0 -> 198670 bytes .../figure-html/unnamed-chunk-4-1.png} | Bin .../figure-html/unnamed-chunk-4-2.png | Bin 0 -> 149368 bytes .../figure-html/unnamed-chunk-5-1.png} | Bin .../figure-html/unnamed-chunk-6-1.png | Bin 0 -> 179871 bytes .../figure-html/unnamed-chunk-7-1.png | Bin 0 -> 186690 bytes docs/articles/connectivity_tutorial.html | 668 ++ .../figure-html/unnamed-chunk-13-1.png | Bin 0 -> 10713 bytes .../figure-html/unnamed-chunk-15-1.png | Bin 0 -> 15152 bytes .../figure-html/unnamed-chunk-17-1.png | Bin 0 -> 15373 bytes .../figure-html/unnamed-chunk-20-1.png | Bin 0 -> 14468 bytes .../figure-html/unnamed-chunk-22-1.png | Bin 0 -> 15036 bytes .../figure-html/unnamed-chunk-24-1.png | Bin 0 -> 16626 bytes .../figure-html/unnamed-chunk-26-1.png | Bin 0 -> 18023 bytes .../figure-html/unnamed-chunk-28-1.png | Bin 0 -> 16483 bytes .../figure-html/unnamed-chunk-30-1.png | Bin 0 -> 16028 bytes .../figure-html/unnamed-chunk-32-1.png | Bin 0 -> 16839 bytes .../figure-html/unnamed-chunk-35-1.png | Bin 0 -> 16667 bytes .../figure-html/unnamed-chunk-37-1.png | Bin 0 -> 16399 bytes .../figure-html/unnamed-chunk-41-1.png | Bin 0 -> 16744 bytes .../figure-html/unnamed-chunk-43-1.png | Bin 0 -> 16944 bytes .../figure-html/unnamed-chunk-5-1.png | Bin 0 -> 21714 bytes .../figure-html/unnamed-chunk-7-1.png | Bin 0 -> 23415 bytes .../figure-html/unnamed-chunk-9-1.png | Bin 0 -> 86640 bytes .../header-attrs-2.11/header-attrs.js | 12 - ...on.html => gurobi_installation_guide.html} | 216 +- .../figure-html/unnamed-chunk-9-1.png | Bin docs/articles/index.html | 197 +- docs/articles/management_zones_tutorial.html | 1010 +++ .../figure-html/unnamed-chunk-10-1.png} | Bin .../figure-html/unnamed-chunk-11-1.png} | Bin .../figure-html/unnamed-chunk-3-1.png} | Bin .../figure-html/unnamed-chunk-4-1.png} | Bin .../figure-html/unnamed-chunk-5-1.png} | Bin .../figure-html/unnamed-chunk-6-1.png} | Bin .../figure-html/unnamed-chunk-7-1.png} | Bin .../figure-html/unnamed-chunk-8-1.png} | Bin .../figure-html/unnamed-chunk-9-1.png} | Bin docs/articles/package_overview.html | 1814 +++++ .../figure-html/unnamed-chunk-27-1.png | Bin .../figure-html/unnamed-chunk-3-1.png | Bin .../figure-html/unnamed-chunk-4-1.png | Bin 0 -> 17240 bytes .../figure-html/unnamed-chunk-45-1.png | Bin .../figure-html/unnamed-chunk-53-1.png} | Bin .../figure-html/unnamed-chunk-54-1.png | Bin 0 -> 17952 bytes .../figure-html/unnamed-chunk-55-1.png} | Bin .../figure-html/unnamed-chunk-56-1.png | Bin 0 -> 18755 bytes .../figure-html/unnamed-chunk-6-1.png | Bin docs/articles/prioritizr.html | 2153 ++---- .../figure-html/unnamed-chunk-11-1.png | Bin 0 -> 51914 bytes .../figure-html/unnamed-chunk-12-1.png | Bin 0 -> 373806 bytes .../figure-html/unnamed-chunk-4-1.png | Bin 16910 -> 203981 bytes .../figure-html/unnamed-chunk-4-2.png | Bin 0 -> 150098 bytes .../figure-html/unnamed-chunk-5-1.png | Bin 0 -> 37098 bytes .../figure-html/unnamed-chunk-55-2.png | Bin 17142 -> 0 bytes .../figure-html/unnamed-chunk-57-1.png | Bin 18448 -> 0 bytes .../figure-html/unnamed-chunk-7-1.png | Bin 0 -> 178311 bytes .../figure-html/unnamed-chunk-8-1.png | Bin 0 -> 27210 bytes .../figure-html/unnamed-chunk-8-2.png | Bin 0 -> 24838 bytes .../figure-html/unnamed-chunk-9-1.png | Bin 0 -> 130720 bytes .../header-attrs-2.11/header-attrs.js | 12 - docs/articles/publication_record.html | 100 +- .../header-attrs-2.11/header-attrs.js | 12 - docs/articles/saltspring.html | 393 - .../figure-html/unnamed-chunk-12-1.png | Bin 46805 -> 0 bytes .../figure-html/unnamed-chunk-3-1.png | Bin 18113 -> 0 bytes .../figure-html/unnamed-chunk-4-1.png | Bin 51918 -> 0 bytes .../figure-html/unnamed-chunk-5-1.png | Bin 159103 -> 0 bytes .../figure-html/unnamed-chunk-9-1.png | Bin 40935 -> 0 bytes .../header-attrs-2.11/header-attrs.js | 12 - .../figure-html/min_set_pu_n_1-1.png | Bin 110483 -> 0 bytes ...et_pu_n_1_with_high_boundary_penalty-1.png | Bin 98286 -> 0 bytes ...set_pu_n_1_with_low_boundary_penalty-1.png | Bin 94962 -> 0 bytes .../figure-html/min_set_pu_n_2-1.png | Bin 105310 -> 0 bytes ...et_pu_n_2_with_high_boundary_penalty-1.png | Bin 90881 -> 0 bytes ...set_pu_n_2_with_low_boundary_penalty-1.png | Bin 105882 -> 0 bytes .../figure-html/min_set_pu_n_3-1.png | Bin 91480 -> 0 bytes ...et_pu_n_3_with_high_boundary_penalty-1.png | Bin 81678 -> 0 bytes ...set_pu_n_3_with_low_boundary_penalty-1.png | Bin 81678 -> 0 bytes .../figure-html/min_set_pu_n_4-1.png | Bin 82199 -> 0 bytes .../min_set_pu_n_4_fast_only-1.png | Bin 68962 -> 0 bytes ...et_pu_n_4_with_high_boundary_penalty-1.png | Bin 96433 -> 0 bytes ...gh_boundary_penalty_and_fast_solvers-1.png | Bin 89257 -> 0 bytes ...set_pu_n_4_with_low_boundary_penalty-1.png | Bin 90256 -> 0 bytes ...ow_boundary_penalty_and_fast_solvers-1.png | Bin 90147 -> 0 bytes .../figure-html/min_short_pu_n_1-1.png | Bin 86229 -> 0 bytes ...rt_pu_n_1_with_high_boundary_penalty-1.png | Bin 96286 -> 0 bytes ...ort_pu_n_1_with_low_boundary_penalty-1.png | Bin 94359 -> 0 bytes ...n_short_pu_n_2 with boundary penalty-1.png | Bin 86710 -> 0 bytes .../figure-html/min_short_pu_n_2-1.png | Bin 86399 -> 0 bytes ...ort_pu_n_2_with_low_boundary_penalty-1.png | Bin 81233 -> 0 bytes .../figure-html/min_short_pu_n_3-1.png | Bin 80362 -> 0 bytes ...rt_pu_n_3_with_high_boundary_penalty-1.png | Bin 86357 -> 0 bytes ...ort_pu_n_3_with_low_boundary_penalty-1.png | Bin 81221 -> 0 bytes .../figure-html/min_short_pu_n_4-1.png | Bin 94219 -> 0 bytes ...nd_boundary_penalty_and_fast_solvers-1.png | Bin 79267 -> 0 bytes ...rt_pu_n_4_with_high_boundary_penalty-1.png | Bin 85810 -> 0 bytes ...ort_pu_n_4_with_low_boundary_penalty-1.png | Bin 92473 -> 0 bytes .../overall_average_run_times-1.png | Bin 39737 -> 0 bytes .../header-attrs-2.11/header-attrs.js | 12 - ..._benchmark.html => solver_benchmarks.html} | 276 +- .../figure-html/min_set_pu_n_1-1.png | Bin 0 -> 110560 bytes ...et_pu_n_1_with_high_boundary_penalty-1.png | Bin 0 -> 98416 bytes ...set_pu_n_1_with_low_boundary_penalty-1.png | Bin 0 -> 95118 bytes .../figure-html/min_set_pu_n_2-1.png | Bin 0 -> 105350 bytes ...et_pu_n_2_with_high_boundary_penalty-1.png | Bin 0 -> 91128 bytes ...set_pu_n_2_with_low_boundary_penalty-1.png | Bin 0 -> 105994 bytes .../figure-html/min_set_pu_n_3-1.png | Bin 0 -> 91610 bytes ...et_pu_n_3_with_high_boundary_penalty-1.png | Bin 0 -> 81964 bytes ...set_pu_n_3_with_low_boundary_penalty-1.png | Bin 0 -> 81964 bytes .../figure-html/min_set_pu_n_4-1.png | Bin 0 -> 82423 bytes .../min_set_pu_n_4_fast_only-1.png | Bin 0 -> 69269 bytes ...et_pu_n_4_with_high_boundary_penalty-1.png | Bin 0 -> 96530 bytes ...gh_boundary_penalty_and_fast_solvers-1.png | Bin 0 -> 89427 bytes ...set_pu_n_4_with_low_boundary_penalty-1.png | Bin 0 -> 90377 bytes ...ow_boundary_penalty_and_fast_solvers-1.png | Bin 0 -> 90272 bytes .../figure-html/min_short_pu_n_1-1.png | Bin 0 -> 86361 bytes ...rt_pu_n_1_with_high_boundary_penalty-1.png | Bin 0 -> 96522 bytes ...ort_pu_n_1_with_low_boundary_penalty-1.png | Bin 0 -> 94505 bytes ...n_short_pu_n_2 with boundary penalty-1.png | Bin 0 -> 87006 bytes .../figure-html/min_short_pu_n_2-1.png | Bin 0 -> 86473 bytes ...ort_pu_n_2_with_low_boundary_penalty-1.png | Bin 0 -> 81463 bytes .../figure-html/min_short_pu_n_3-1.png | Bin 0 -> 80436 bytes ...rt_pu_n_3_with_high_boundary_penalty-1.png | Bin 0 -> 86497 bytes ...ort_pu_n_3_with_low_boundary_penalty-1.png | Bin 0 -> 81404 bytes .../figure-html/min_short_pu_n_4-1.png | Bin 0 -> 94371 bytes ...nd_boundary_penalty_and_fast_solvers-1.png | Bin 0 -> 79532 bytes ...rt_pu_n_4_with_high_boundary_penalty-1.png | Bin 0 -> 85919 bytes ...ort_pu_n_4_with_low_boundary_penalty-1.png | Bin 0 -> 92592 bytes .../overall_average_run_times-1.png | Bin 0 -> 39856 bytes docs/articles/tasmania.html | 548 -- .../figure-html/unnamed-chunk-11-1.png | Bin 251354 -> 0 bytes .../figure-html/unnamed-chunk-12-1.png | Bin 19054 -> 0 bytes .../figure-html/unnamed-chunk-12-2.png | Bin 160172 -> 0 bytes .../figure-html/unnamed-chunk-7-1.png | Bin 180711 -> 0 bytes .../figure-html/unnamed-chunk-8-1.png | Bin 179873 -> 0 bytes .../header-attrs-2.11/header-attrs.js | 12 - docs/articles/zones.html | 998 --- .../header-attrs-2.11/header-attrs.js | 12 - docs/authors.html | 269 +- docs/extra.css | 18 +- docs/index.html | 139 +- docs/news/index.html | 1209 ++- docs/package-logo.png | Bin 11401 -> 0 bytes docs/pkgdown.css | 63 +- docs/pkgdown.js | 2 +- docs/pkgdown.yml | 17 +- docs/reference/ArrayParameter-class.html | 270 +- docs/reference/Collection-class.html | 262 +- .../reference/ConservationModifier-class.html | 286 +- docs/reference/ConservationProblem-class.html | 415 +- docs/reference/Constraint-class.html | 187 +- docs/reference/Decision-class.html | 187 +- docs/reference/MiscParameter-class.html | 243 +- docs/reference/Objective-class.html | 180 +- docs/reference/OptimizationProblem-class.html | 288 +- .../OptimizationProblem-methods.html | 336 +- docs/reference/Parameter-class.html | 237 +- docs/reference/Parameters-class.html | 249 +- docs/reference/Penalty-class.html | 187 +- docs/reference/Portfolio-class.html | 235 +- docs/reference/Rplot002.png | Bin 7144 -> 7406 bytes docs/reference/ScalarParameter-class.html | 257 +- docs/reference/Solver-class.html | 265 +- docs/reference/Target-class.html | 187 +- docs/reference/add_absolute_targets.html | 434 +- docs/reference/add_binary_decisions.html | 317 +- docs/reference/add_boundary_penalties.html | 544 +- docs/reference/add_cbc_solver.html | 455 +- .../add_connectivity_penalties-1.png | Bin 46650 -> 48210 bytes .../add_connectivity_penalties-2.png | Bin 69188 -> 70437 bytes .../add_connectivity_penalties-3.png | Bin 42288 -> 44760 bytes .../add_connectivity_penalties-4.png | Bin 28389 -> 30305 bytes .../add_connectivity_penalties-6.png | Bin 42101 -> 45099 bytes .../reference/add_connectivity_penalties.html | 886 +-- .../reference/add_contiguity_constraints.html | 596 +- docs/reference/add_cplex_solver.html | 349 +- docs/reference/add_cuts_portfolio.html | 365 +- docs/reference/add_default_decisions.html | 214 +- docs/reference/add_default_solver.html | 238 +- docs/reference/add_extra_portfolio.html | 317 +- .../add_feature_contiguity_constraints-2.png | Bin 23783 -> 23784 bytes .../add_feature_contiguity_constraints.html | 544 +- docs/reference/add_feature_weights.html | 650 +- docs/reference/add_gap_portfolio.html | 342 +- docs/reference/add_gurobi_solver.html | 467 +- docs/reference/add_linear_constraints.html | 617 +- docs/reference/add_linear_penalties-1.png | Bin 18945 -> 19282 bytes docs/reference/add_linear_penalties-2.png | Bin 18697 -> 19067 bytes docs/reference/add_linear_penalties-3.png | Bin 18469 -> 18814 bytes docs/reference/add_linear_penalties.html | 575 +- .../reference/add_locked_in_constraints-2.png | Bin 13323 -> 13423 bytes .../reference/add_locked_in_constraints-3.png | Bin 13433 -> 13535 bytes docs/reference/add_locked_in_constraints.html | 585 +- .../add_locked_out_constraints-2.png | Bin 13323 -> 13423 bytes .../add_locked_out_constraints-3.png | Bin 13323 -> 13423 bytes .../reference/add_locked_out_constraints.html | 581 +- docs/reference/add_loglinear_targets.html | 347 +- docs/reference/add_lsymphony_solver.html | 332 +- .../add_mandatory_allocation_constraints.html | 307 +- .../add_manual_bounded_constraints-2.png | Bin 20277 -> 20345 bytes .../add_manual_bounded_constraints.html | 463 +- .../add_manual_locked_constraints-2.png | Bin 20558 -> 20622 bytes .../add_manual_locked_constraints.html | 463 +- docs/reference/add_manual_targets.html | 562 +- docs/reference/add_max_cover_objective.html | 398 +- .../reference/add_max_features_objective.html | 363 +- .../add_max_phylo_div_objective.html | 570 +- .../add_max_phylo_end_objective.html | 563 +- docs/reference/add_max_utility_objective.html | 347 +- .../add_min_largest_shortfall_objective.html | 358 +- docs/reference/add_min_set_objective.html | 319 +- .../add_min_shortfall_objective.html | 361 +- docs/reference/add_neighbor_constraints.html | 471 +- docs/reference/add_proportion_decisions.html | 319 +- docs/reference/add_relative_targets.html | 428 +- docs/reference/add_rsymphony_solver.html | 318 +- .../add_semicontinuous_decisions.html | 337 +- docs/reference/add_shuffle_portfolio.html | 379 +- docs/reference/add_top_portfolio.html | 325 +- docs/reference/adjacency_matrix.html | 356 +- docs/reference/array_parameters.html | 486 +- docs/reference/as.html | 211 +- docs/reference/as.list.html | 205 +- docs/reference/binary_stack.html | 245 +- docs/reference/boundary_matrix.html | 350 +- docs/reference/branch_matrix.html | 237 +- docs/reference/category_layer.html | 247 +- docs/reference/category_vector.html | 265 +- docs/reference/compile.html | 260 +- docs/reference/connectivity_matrix.html | 559 +- docs/reference/constraints.html | 318 +- docs/reference/decisions.html | 286 +- docs/reference/distribute_load.html | 498 +- docs/reference/eval_boundary_summary.html | 721 +- docs/reference/eval_connectivity_summary.html | 689 +- docs/reference/eval_cost_summary.html | 598 +- .../eval_feature_representation_summary-2.png | Bin 16214 -> 16601 bytes .../eval_feature_representation_summary.html | 935 ++- docs/reference/eval_ferrier_importance.html | 508 +- docs/reference/eval_n_summary.html | 593 +- .../eval_rare_richness_importance.html | 514 +- .../eval_replacement_importance.html | 696 +- .../eval_target_coverage_summary.html | 869 +-- docs/reference/fast_extract.html | 301 +- docs/reference/feature_abundances.html | 466 +- docs/reference/feature_names.html | 235 +- docs/reference/importance.html | 307 +- docs/reference/index.html | 6810 ++++++++++++++--- docs/reference/intersecting_units.html | 346 +- docs/reference/is.html | 195 +- docs/reference/loglinear_interpolation-1.png | Bin 25518 -> 25515 bytes docs/reference/loglinear_interpolation.html | 348 +- .../marxan_boundary_data_to_matrix-1.png | Bin 14622 -> 14650 bytes .../marxan_boundary_data_to_matrix.html | 323 +- docs/reference/marxan_problem.html | 547 +- docs/reference/matrix_parameters.html | 353 +- docs/reference/misc_parameter.html | 903 +-- docs/reference/new_id.html | 228 +- docs/reference/new_optimization_problem.html | 207 +- docs/reference/new_waiver.html | 215 +- docs/reference/number_of_features.html | 239 +- docs/reference/number_of_planning_units.html | 231 +- docs/reference/number_of_total_units.html | 263 +- docs/reference/number_of_zones.html | 245 +- docs/reference/objectives.html | 354 +- docs/reference/parameters.html | 238 +- docs/reference/penalties.html | 334 +- docs/reference/pipe.html | 235 +- docs/reference/portfolios.html | 346 +- docs/reference/pproto.html | 260 +- .../predefined_optimization_problem.html | 297 +- docs/reference/presolve_check.html | 423 +- docs/reference/print.html | 254 +- docs/reference/prioritizr-deprecated.html | 277 +- docs/reference/prioritizr.html | 220 +- docs/reference/problem-1.png | Bin 47489 -> 47408 bytes docs/reference/problem-4.png | Bin 13323 -> 13423 bytes docs/reference/problem-5.png | Bin 22359 -> 22699 bytes docs/reference/problem.html | 973 ++- docs/reference/proximity_matrix.html | 362 +- docs/reference/rij_matrix.html | 930 +-- docs/reference/run_calculations.html | 328 +- docs/reference/scalar_parameters.html | 424 +- docs/reference/show.html | 222 +- docs/reference/sim_data.html | 421 +- docs/reference/simulate_cost.html | 284 +- docs/reference/simulate_data.html | 268 +- docs/reference/simulate_species.html | 284 +- docs/reference/solve-2.png | Bin 16214 -> 16601 bytes docs/reference/solve.html | 843 +- docs/reference/solvers.html | 375 +- docs/reference/summaries.html | 394 +- docs/reference/targets.html | 290 +- docs/reference/tee.html | 247 +- docs/reference/tibble-methods.html | 266 +- docs/reference/write_problem.html | 297 +- docs/reference/zone_names.html | 239 +- docs/reference/zones.html | 371 +- docs/sitemap.xml | 19 +- inst/doc/calibrating_trade-offs_tutorial.Rmd | 678 ++ inst/doc/calibrating_trade-offs_tutorial.html | 868 +++ inst/doc/connectivity_tutorial.Rmd | 487 ++ inst/doc/connectivity_tutorial.html | 642 ++ .../doc/gurobi_installation_guide.Rmd | 16 +- ...on.html => gurobi_installation_guide.html} | 20 +- .../doc/management_zones_tutorial.Rmd | 23 +- ...es.html => management_zones_tutorial.html} | 425 +- inst/doc/package_overview.Rmd | 905 +++ inst/doc/package_overview.html | 1704 +++++ inst/doc/prioritizr.Rmd | 966 +-- inst/doc/prioritizr.html | 1933 ++--- inst/doc/publication_record.Rmd | 4 +- inst/doc/publication_record.html | 4 +- inst/doc/saltspring.Rmd | 212 - inst/doc/saltspring.html | 390 - ...er_benchmark.Rmd => solver_benchmarks.Rmd} | 64 +- ..._benchmark.html => solver_benchmarks.html} | 92 +- inst/doc/tasmania.Rmd | 225 - inst/doc/tasmania.html | 537 -- man/ConservationProblem-class.Rd | 4 +- man/Parameter-class.Rd | 2 +- man/ScalarParameter-class.Rd | 2 +- man/add_absolute_targets.Rd | 4 +- man/add_binary_decisions.Rd | 6 +- man/add_boundary_penalties.Rd | 24 +- man/add_cbc_solver.Rd | 12 +- man/add_connectivity_penalties.Rd | 18 +- man/add_contiguity_constraints.Rd | 14 +- man/add_cplex_solver.Rd | 14 +- man/add_cuts_portfolio.Rd | 6 +- man/add_default_decisions.Rd | 4 +- man/add_default_solver.Rd | 4 +- man/add_extra_portfolio.Rd | 6 +- man/add_feature_contiguity_constraints.Rd | 14 +- man/add_feature_weights.Rd | 14 +- man/add_gap_portfolio.Rd | 11 +- man/add_gurobi_solver.Rd | 12 +- man/add_linear_constraints.Rd | 18 +- man/add_linear_penalties.Rd | 16 +- man/add_locked_in_constraints.Rd | 8 +- man/add_locked_out_constraints.Rd | 8 +- man/add_loglinear_targets.Rd | 6 +- man/add_lsymphony_solver.Rd | 8 +- man/add_mandatory_allocation_constraints.Rd | 8 +- man/add_manual_bounded_constraints.Rd | 6 +- man/add_manual_locked_constraints.Rd | 8 +- man/add_manual_targets.Rd | 6 +- man/add_max_cover_objective.Rd | 10 +- man/add_max_features_objective.Rd | 6 +- man/add_max_phylo_div_objective.Rd | 6 +- man/add_max_phylo_end_objective.Rd | 6 +- man/add_max_utility_objective.Rd | 6 +- man/add_min_largest_shortfall_objective.Rd | 8 +- man/add_min_set_objective.Rd | 6 +- man/add_min_shortfall_objective.Rd | 6 +- man/add_neighbor_constraints.Rd | 16 +- man/add_proportion_decisions.Rd | 6 +- man/add_relative_targets.Rd | 4 +- man/add_rsymphony_solver.Rd | 8 +- man/add_semicontinuous_decisions.Rd | 10 +- man/add_shuffle_portfolio.Rd | 4 +- man/add_top_portfolio.Rd | 6 +- man/boundary_matrix.Rd | 6 +- man/compile.Rd | 2 +- man/connectivity_matrix.Rd | 8 +- man/constraints.Rd | 2 +- man/decisions.Rd | 2 +- man/eval_boundary_summary.Rd | 16 +- man/eval_connectivity_summary.Rd | 10 +- man/eval_cost_summary.Rd | 4 +- man/eval_feature_representation_summary.Rd | 11 +- man/eval_ferrier_importance.Rd | 2 +- man/eval_n_summary.Rd | 6 +- man/eval_rare_richness_importance.Rd | 4 +- man/eval_replacement_importance.Rd | 12 +- man/eval_target_coverage_summary.Rd | 37 +- man/feature_abundances.Rd | 10 +- man/feature_names.Rd | 2 +- man/importance.Rd | 8 +- man/marxan_boundary_data_to_matrix.Rd | 2 +- man/marxan_problem.Rd | 4 +- man/number_of_features.Rd | 2 +- man/number_of_planning_units.Rd | 2 +- man/number_of_total_units.Rd | 2 +- man/number_of_zones.Rd | 2 +- man/penalties.Rd | 4 +- man/portfolios.Rd | 6 +- man/presolve_check.Rd | 18 +- man/prioritizr-deprecated.Rd | 2 +- man/prioritizr.Rd | 2 +- man/problem.Rd | 50 +- man/rij_matrix.Rd | 2 +- man/run_calculations.Rd | 4 +- man/solve.Rd | 18 +- man/write_problem.Rd | 2 +- man/zone_names.Rd | 2 +- man/zones.Rd | 2 +- pkgdown/extra.css | 18 +- vignettes/calibrating_trade-offs_tutorial.Rmd | 678 ++ vignettes/connectivity_tutorial.Rmd | 487 ++ .../gurobi_installation_guide.Rmd | 16 +- .../management_zones_tutorial.Rmd | 23 +- vignettes/package_overview.Rmd | 905 +++ vignettes/prioritizr.Rmd | 966 +-- vignettes/publication_record.Rmd | 4 +- vignettes/references.bib | 313 + vignettes/saltspring.Rmd | 212 - ...er_benchmark.Rmd => solver_benchmarks.Rmd} | 64 +- vignettes/tasmania.Rmd | 225 - 500 files changed, 40422 insertions(+), 42453 deletions(-) create mode 100644 docs/articles/calibrating_trade-offs_tutorial.html create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-11-1.png create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-12-1.png create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-13-1.png create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-16-1.png create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-19-1.png create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-28-1.png create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-29-1.png rename docs/articles/{tasmania_files/figure-html/unnamed-chunk-3-1.png => calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-4-1.png} (100%) create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-4-2.png rename docs/articles/{tasmania_files/figure-html/unnamed-chunk-4-1.png => calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-5-1.png} (100%) create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-6-1.png create mode 100644 docs/articles/calibrating_trade-offs_tutorial_files/figure-html/unnamed-chunk-7-1.png create mode 100644 docs/articles/connectivity_tutorial.html create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-13-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-15-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-17-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-20-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-22-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-24-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-26-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-28-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-30-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-32-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-35-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-37-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-41-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-43-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-5-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-7-1.png create mode 100644 docs/articles/connectivity_tutorial_files/figure-html/unnamed-chunk-9-1.png delete mode 100644 docs/articles/gurobi_installation_files/header-attrs-2.11/header-attrs.js rename docs/articles/{gurobi_installation.html => gurobi_installation_guide.html} (75%) rename docs/articles/{gurobi_installation_files => gurobi_installation_guide_files}/figure-html/unnamed-chunk-9-1.png (100%) create mode 100644 docs/articles/management_zones_tutorial.html rename docs/articles/{zones_files/figure-html/unnamed-chunk-9-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-10-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-10-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-11-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-2-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-3-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-3-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-4-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-4-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-5-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-5-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-6-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-6-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-7-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-7-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-8-1.png} (100%) rename docs/articles/{zones_files/figure-html/unnamed-chunk-8-1.png => management_zones_tutorial_files/figure-html/unnamed-chunk-9-1.png} (100%) create mode 100644 docs/articles/package_overview.html rename docs/articles/{prioritizr_files => package_overview_files}/figure-html/unnamed-chunk-27-1.png (100%) rename docs/articles/{prioritizr_files => package_overview_files}/figure-html/unnamed-chunk-3-1.png (100%) create mode 100644 docs/articles/package_overview_files/figure-html/unnamed-chunk-4-1.png rename docs/articles/{prioritizr_files => package_overview_files}/figure-html/unnamed-chunk-45-1.png (100%) rename docs/articles/{prioritizr_files/figure-html/unnamed-chunk-55-1.png => package_overview_files/figure-html/unnamed-chunk-53-1.png} (100%) create mode 100644 docs/articles/package_overview_files/figure-html/unnamed-chunk-54-1.png rename docs/articles/{prioritizr_files/figure-html/unnamed-chunk-56-1.png => package_overview_files/figure-html/unnamed-chunk-55-1.png} (100%) create mode 100644 docs/articles/package_overview_files/figure-html/unnamed-chunk-56-1.png rename docs/articles/{prioritizr_files => package_overview_files}/figure-html/unnamed-chunk-6-1.png (100%) create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-11-1.png create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-12-1.png create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-4-2.png create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-5-1.png delete mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-55-2.png delete mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-57-1.png create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-7-1.png create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-8-1.png create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-8-2.png create mode 100644 docs/articles/prioritizr_files/figure-html/unnamed-chunk-9-1.png delete mode 100644 docs/articles/prioritizr_files/header-attrs-2.11/header-attrs.js delete mode 100644 docs/articles/publication_record_files/header-attrs-2.11/header-attrs.js delete mode 100644 docs/articles/saltspring.html delete mode 100644 docs/articles/saltspring_files/figure-html/unnamed-chunk-12-1.png delete mode 100644 docs/articles/saltspring_files/figure-html/unnamed-chunk-3-1.png delete mode 100644 docs/articles/saltspring_files/figure-html/unnamed-chunk-4-1.png delete mode 100644 docs/articles/saltspring_files/figure-html/unnamed-chunk-5-1.png delete mode 100644 docs/articles/saltspring_files/figure-html/unnamed-chunk-9-1.png delete mode 100644 docs/articles/saltspring_files/header-attrs-2.11/header-attrs.js delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_1-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_1_with_high_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_1_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_2-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_2_with_high_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_2_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_3-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_3_with_high_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_3_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_4-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_4_fast_only-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_4_with_high_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_4_with_high_boundary_penalty_and_fast_solvers-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_4_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_set_pu_n_4_with_low_boundary_penalty_and_fast_solvers-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_1-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_1_with_high_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_1_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_2 with boundary penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_2-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_2_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_3-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_3_with_high_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_3_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_4-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_4_and_boundary_penalty_and_fast_solvers-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_4_with_high_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/min_short_pu_n_4_with_low_boundary_penalty-1.png delete mode 100644 docs/articles/solver_benchmark_files/figure-html/overall_average_run_times-1.png delete mode 100644 docs/articles/solver_benchmark_files/header-attrs-2.11/header-attrs.js rename docs/articles/{solver_benchmark.html => solver_benchmarks.html} (80%) create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_1-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_1_with_high_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_1_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_2-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_2_with_high_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_2_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_3-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_3_with_high_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_3_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_4-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_4_fast_only-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_4_with_high_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_4_with_high_boundary_penalty_and_fast_solvers-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_4_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_set_pu_n_4_with_low_boundary_penalty_and_fast_solvers-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_1-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_1_with_high_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_1_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_2 with boundary penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_2-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_2_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_3-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_3_with_high_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_3_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_4-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_4_and_boundary_penalty_and_fast_solvers-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_4_with_high_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/min_short_pu_n_4_with_low_boundary_penalty-1.png create mode 100644 docs/articles/solver_benchmarks_files/figure-html/overall_average_run_times-1.png delete mode 100644 docs/articles/tasmania.html delete mode 100644 docs/articles/tasmania_files/figure-html/unnamed-chunk-11-1.png delete mode 100644 docs/articles/tasmania_files/figure-html/unnamed-chunk-12-1.png delete mode 100644 docs/articles/tasmania_files/figure-html/unnamed-chunk-12-2.png delete mode 100644 docs/articles/tasmania_files/figure-html/unnamed-chunk-7-1.png delete mode 100644 docs/articles/tasmania_files/figure-html/unnamed-chunk-8-1.png delete mode 100644 docs/articles/tasmania_files/header-attrs-2.11/header-attrs.js delete mode 100644 docs/articles/zones.html delete mode 100644 docs/articles/zones_files/header-attrs-2.11/header-attrs.js delete mode 100644 docs/package-logo.png create mode 100644 inst/doc/calibrating_trade-offs_tutorial.Rmd create mode 100644 inst/doc/calibrating_trade-offs_tutorial.html create mode 100644 inst/doc/connectivity_tutorial.Rmd create mode 100644 inst/doc/connectivity_tutorial.html rename vignettes/gurobi_installation.Rmd => inst/doc/gurobi_installation_guide.Rmd (88%) rename inst/doc/{gurobi_installation.html => gurobi_installation_guide.html} (99%) rename vignettes/zones.Rmd => inst/doc/management_zones_tutorial.Rmd (70%) rename inst/doc/{zones.html => management_zones_tutorial.html} (88%) create mode 100644 inst/doc/package_overview.Rmd create mode 100644 inst/doc/package_overview.html delete mode 100644 inst/doc/saltspring.Rmd delete mode 100644 inst/doc/saltspring.html rename inst/doc/{solver_benchmark.Rmd => solver_benchmarks.Rmd} (77%) rename inst/doc/{solver_benchmark.html => solver_benchmarks.html} (99%) delete mode 100644 inst/doc/tasmania.Rmd delete mode 100644 inst/doc/tasmania.html create mode 100644 vignettes/calibrating_trade-offs_tutorial.Rmd create mode 100644 vignettes/connectivity_tutorial.Rmd rename inst/doc/gurobi_installation.Rmd => vignettes/gurobi_installation_guide.Rmd (88%) rename inst/doc/zones.Rmd => vignettes/management_zones_tutorial.Rmd (70%) create mode 100644 vignettes/package_overview.Rmd delete mode 100644 vignettes/saltspring.Rmd rename vignettes/{solver_benchmark.Rmd => solver_benchmarks.Rmd} (77%) delete mode 100644 vignettes/tasmania.Rmd diff --git a/DESCRIPTION b/DESCRIPTION index 8f0ff20d9..e279d18da 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: prioritizr Type: Package -Version: 7.1.1.4 +Version: 7.1.1.5 Title: Systematic Conservation Prioritization in R Description: Systematic conservation prioritization using mixed integer linear diff --git a/NEWS.md b/NEWS.md index 843a54e96..00444d87b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,10 @@ +# prioritizr 7.1.1.5 + +- The _Tasmania tutorial_ has been reworked into the _Getting started_ tutorial. This tutorial now provides short introduction to using the package. +- The _Salt Spring Island tutorial_ has been reworked into the _Connectivity tutorial_. This tutorial now explores different approaches for incorporating connectivity. +- The _prioritizr_ vignette has been renamed to the _Package overview_ vignette. +- New _Calibrating trade-offs tutorial_ showcasing methods for running calibration analyses. It outlines blended and hierarchical approaches for generating a set of different prioritizations based on different parameters. It also covers different approaches for selecting a candidate prioritization based on different trade-offs. + # prioritizr 7.1.1.4 - Update tests to reduce run time and pass given slightly different results diff --git a/R/ConservationProblem-proto.R b/R/ConservationProblem-proto.R index 025cf060f..7f85bb1d0 100644 --- a/R/ConservationProblem-proto.R +++ b/R/ConservationProblem-proto.R @@ -9,13 +9,13 @@ NULL #' #' This class is used to represent conservation planning problems. A #' conservation planning problem has spatially explicit planning units. -#' A prioritization involves making a decision on each planning unit (e.g. is +#' A prioritization involves making a decision on each planning unit (e.g., is #' the planning unit going to be turned into a protected area?). Each #' planning unit is associated with a cost that represents the cost incurred #' by applying the decision to the planning unit. The problem also has a set #' of representation targets for each feature. Further, it also has #' constraints used to ensure that the solution meets additional -#' objectives (e.g. certain areas are locked into the solution). Finally, +#' objectives (e.g., certain areas are locked into the solution). Finally, #' a conservation planning problem---unlike an optimization problem---also #' requires a method to solve the problem. **This class represents a #' planning problem, to actually build and then solve a planning problem, diff --git a/R/Parameter-proto.R b/R/Parameter-proto.R index 74ba78b1a..0ca2204d7 100644 --- a/R/Parameter-proto.R +++ b/R/Parameter-proto.R @@ -22,7 +22,7 @@ NULL #' \item{$default}{`numeric` vector of default values.} #' #' \item{$class}{`character` name of the class that the values inherit -#' from (e.g. `"integer"`.} +#' from (e.g., `"integer"`.} #' #' \item{$lower_limit}{`numeric` vector specifying the minimum #' permitted value for each element in `$value`.} diff --git a/R/ScalarParameter-proto.R b/R/ScalarParameter-proto.R index 5fcefca18..0a6940b8a 100644 --- a/R/ScalarParameter-proto.R +++ b/R/ScalarParameter-proto.R @@ -22,7 +22,7 @@ NULL #' \item{$default}{`numeric` scalar default value.} #' #' \item{$class}{`character` name of the class that `$value` should -#' inherit from (e.g. `integer`).} +#' inherit from (e.g., `integer`).} #' #' \item{$lower_limit}{`numeric` scalar value that is the minimum value #' that `$value` is permitted to be.} diff --git a/R/add_absolute_targets.R b/R/add_absolute_targets.R index 4b0ac7eea..ea5dd9e93 100644 --- a/R/add_absolute_targets.R +++ b/R/add_absolute_targets.R @@ -9,7 +9,7 @@ NULL #' planning units for which their summed feature values are equal to or greater #' than 10. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param targets Object that specifies the targets for each feature. #' See the Targets format section for more information. diff --git a/R/add_binary_decisions.R b/R/add_binary_decisions.R index fb5c193b8..0abf8c693 100644 --- a/R/add_binary_decisions.R +++ b/R/add_binary_decisions.R @@ -9,16 +9,16 @@ NULL #' the planning unit to include in a protected area network. If no decision is #' added to a problem then this decision class will be used by default. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @details Conservation planning problems involve making decisions on planning -#' units. These decisions are then associated with actions (e.g. turning a +#' units. These decisions are then associated with actions (e.g., turning a #' planning unit into a protected area). Only a #' single decision should be added to a `ConservationProblem` object. #' Note that if multiple decisions are added to a problem object, then the #' last one to be added will be used. #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the decisions added +#' @return Object (i.e., [`ConservationProblem-class`]) with the decisions added #' to it. #' #' @seealso diff --git a/R/add_boundary_penalties.R b/R/add_boundary_penalties.R index ed492f3ab..36e651e3e 100644 --- a/R/add_boundary_penalties.R +++ b/R/add_boundary_penalties.R @@ -7,11 +7,11 @@ NULL #' that spatially clump planning units together based on the overall #' boundary length (perimeter). #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param penalty `numeric` penalty that is used to scale the importance #' of selecting planning units that are spatially clumped together compared -#' to the main problem objective (e.g. solution cost when the argument to +#' to the main problem objective (e.g., solution cost when the argument to #' `x` has a minimum set objective per [add_min_set_objective()]). #' Higher `penalty` values prefer solutions with a higher degree of spatial #' clumping, and smaller `penalty` values prefer solutions with a smaller @@ -36,19 +36,19 @@ NULL #' allocated to the same zone. Cell values must range between 1 and -1, where #' negative values favor solutions that spread out planning units. The default #' argument to `zones` is an identity -#' matrix (i.e. a matrix with ones along the matrix diagonal and zeros +#' matrix (i.e., a matrix with ones along the matrix diagonal and zeros #' elsewhere), so that penalties are incurred when neighboring planning units #' are not assigned to the same zone. If the cells along #' the matrix diagonal contain markedly smaller values than those found #' elsewhere in the matrix, then solutions are preferred that surround #' planning units with those allocated to different zones -#' (i.e. greater spatial fragmentation). +#' (i.e., greater spatial fragmentation). #' #' @param data `NULL`, `data.frame`, `matrix`, or `Matrix` #' object containing the boundary data. These data describe the total #' amount of boundary (perimeter) length for each planning unit, #' and the amount of boundary (perimeter) length shared between different -#' planning units (i.e. planning units that are adjacent to each other). +#' planning units (i.e., planning units that are adjacent to each other). #' See the Data format section for more information. #' #' @details @@ -75,14 +75,14 @@ NULL #' Note that the boundary data must be supplied #' using one of the other formats below if the planning unit data #' in the argument to `x` do not explicitly contain spatial information -#' (e.g. planning unit data are a `data.frame` or `numeric` class).} +#' (e.g., planning unit data are a `data.frame` or `numeric` class).} #' #' \item{`data` as a `matrix`/`Matrix` object}{where rows and columns represent #' different planning units and the value of each cell represents the #' amount of shared boundary length between two different planning units. #' Cells that occur along the matrix diagonal represent the amount of #' exposed boundary associated with each planning unit that has -#' no neighbor (e.g. these value might pertain to boundaries along a +#' no neighbor (e.g., these value might pertain to boundaries along a #' coastline).} #' #' \item{`data` as a `data.frame` object}{with the columns `"id1"`, @@ -91,7 +91,7 @@ NULL #' column contains the amount of shared boundary length between these #' two planning units. #' This format follows the the standard *Marxan* format for boundary -#' data (i.e. per the "bound.dat" file).} +#' data (i.e., per the "bound.dat" file).} #' #' } #' @@ -101,11 +101,11 @@ NULL #' (indexed by \eqn{i} or \eqn{j}), \eqn{Z} represent #' the set of management zones (indexed by \eqn{z} or \eqn{y}), and #' \eqn{X_{iz}}{Xiz} represent the decision -#' variable for planning unit \eqn{i} for in zone \eqn{z} (e.g. with binary +#' variable for planning unit \eqn{i} for in zone \eqn{z} (e.g., with binary #' values one indicating if planning unit is allocated or not). Also, let #' \eqn{p} represent the argument to `penalty`, \eqn{E_z}{Ez} represent the #' argument to `edge_factor`, \eqn{B_{ij}}{Bij} represent the matrix argument -#' to `data` (e.g. generated using [boundary_matrix()]), and +#' to `data` (e.g., generated using [boundary_matrix()]), and #' \eqn{W_{zz}}{Wzz} represent the matrix argument to `zones`. #' #' \deqn{ @@ -121,7 +121,7 @@ NULL #' benefit and not minimize some measure of cost, the term \eqn{p} is #' replaced with \eqn{-p}. #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the penalties +#' @return Object (i.e., [`ConservationProblem-class`]) with the penalties #' added to it. #' #' @seealso @@ -214,7 +214,7 @@ NULL #' #' # create zone matrix which favors clumping planning units in zones 1 and 2 #' # together, and favors planning units in zone 3 being spread out -#' # (i.e. negative clumping) +#' # (i.e., negative clumping) #' zm10 <- diag(3) #' zm10[3, 3] <- -1 #' print(zm10) diff --git a/R/add_cbc_solver.R b/R/add_cbc_solver.R index dfa799ea7..6192a4158 100644 --- a/R/add_cbc_solver.R +++ b/R/add_cbc_solver.R @@ -22,7 +22,7 @@ NULL #' Although formal benchmarks examining the performance of this solver for #' conservation planning problems have yet to be completed, preliminary #' analyses suggest that it performs much faster than the other open-source -#' solvers (i.e. [add_rsymphony_solver()], [add_rsymphony_solver()]), and +#' solvers (i.e., [add_rsymphony_solver()], [add_rsymphony_solver()]), and #' so we recommend using this solver if the *Gurobi* and *IBM CPLEX* solvers #' are unavailable. #' @@ -45,7 +45,7 @@ NULL #' #' @inheritSection add_gurobi_solver Start solution format #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the solver +#' @return Object (i.e., [`ConservationProblem-class`]) with the solver #' added to it. #' #' @seealso diff --git a/R/add_connectivity_penalties.R b/R/add_connectivity_penalties.R index ae779e413..da7c90a1a 100644 --- a/R/add_connectivity_penalties.R +++ b/R/add_connectivity_penalties.R @@ -6,11 +6,11 @@ NULL #' Add penalties to a conservation planning [problem()] to favor #' solutions that select planning units with high connectivity between them. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param penalty `numeric` penalty that is used to scale the importance #' of selecting planning units with strong connectivity between them compared -#' to the main problem objective (e.g. solution cost when the argument to +#' to the main problem objective (e.g., solution cost when the argument to #' `x` has a minimum set objective set using #' [add_min_set_objective()]). Higher `penalty` values #' can be used to obtain solutions with a high degree of connectivity, @@ -26,7 +26,7 @@ NULL #' the level of connectivity between planning units allocated to the #' same zone. Cell values must lay between 1 and -1, where negative #' values favor solutions with weak connectivity. The default argument to -#' `zones` is an identity matrix (i.e. a matrix with ones along the +#' `zones` is an identity matrix (i.e., a matrix with ones along the #' matrix diagonal and zeros elsewhere), so that planning units are #' only considered to be connected when they are allocated to the same zone. #' This argument is required when the argument to `data` is a @@ -72,7 +72,7 @@ NULL #' *Marxan* format. The data can be used to denote symmetric or #' asymmetric relationships between planning units. By default, #' input data is assumed to be symmetric unless asymmetric data is -#' also included (e.g. if data is present for planning units 2 and 3, then +#' also included (e.g., if data is present for planning units 2 and 3, then #' the same amount of connectivity is expected for planning units 3 and 2, #' unless connectivity data is also provided for planning units 3 and 2). #' If the argument to `x` contains multiple zones, then the columns @@ -86,7 +86,7 @@ NULL #' containing four-dimensions where cell values #' indicate the strength of connectivity between planning units #' when they are assigned to specific management zones. The first two -#' dimensions (i.e. rows and columns) indicate the strength of +#' dimensions (i.e., rows and columns) indicate the strength of #' connectivity between different planning units and the second two #' dimensions indicate the different management zones. Thus #' the `data[1, 2, 3, 4]` indicates the strength of @@ -101,7 +101,7 @@ NULL #' (indexed by \eqn{i} or \eqn{j}), \eqn{Z} represent the set #' of management zones (indexed by \eqn{z} or \eqn{y}), and \eqn{X_{iz}}{Xiz} #' represent the decision variable for planning unit \eqn{i} for in zone -#' \eqn{z} (e.g. with binary +#' \eqn{z} (e.g., with binary #' values one indicating if planning unit is allocated or not). Also, let #' \eqn{p} represent the argument to `penalty`, \eqn{D} represent the #' argument to `data`, and \eqn{W} represent the argument @@ -177,7 +177,7 @@ NULL #' } #' # create a symmetric connectivity matrix where the connectivity between #' # two planning units corresponds to their spatial proximity -#' # i.e. planning units that are further apart share less connectivity +#' # i.e., planning units that are further apart share less connectivity #' centroids <- rgeos::gCentroid(sim_pu_polygons, byid = TRUE) #' d_matrix <- (1 / (as(dist(centroids@coords), "Matrix") + 1)) #' @@ -220,7 +220,7 @@ NULL #' # find if planning units are adjacent #' if (adjacent_units[i, j]) { #' # find if planning units lay north and south of each other -#' # i.e. they have the same x-coordinate +#' # i.e., they have the same x-coordinate #' if (centroids@coords[i, 1] == centroids@coords[j, 1]) { #' if (centroids@coords[i, 2] > centroids@coords[j, 2]) { #' # if i is north of j add 10 units of connectivity diff --git a/R/add_contiguity_constraints.R b/R/add_contiguity_constraints.R index 435a34175..d31dfee63 100644 --- a/R/add_contiguity_constraints.R +++ b/R/add_contiguity_constraints.R @@ -7,12 +7,12 @@ NULL #' that all selected planning units are spatially connected with each other #' and form a single contiguous unit. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param zones `matrix` or `Matrix` object describing the #' connection scheme for different zones. Each row and column corresponds #' to a different zone in the argument to `x`, and cell values must -#' contain binary `numeric` values (i.e. one or zero) that indicate +#' contain binary `numeric` values (i.e., one or zero) that indicate #' if connected planning units (as specified in the argument to #' `data`) should be still considered connected if they are allocated to #' different zones. The cell values along the diagonal @@ -21,7 +21,7 @@ NULL #' arguments to `zones` must be symmetric, and that a row or column has #' a value of one then the diagonal element for that row or column must also #' have a value of one. The default argument to `zones` is an identity -#' matrix (i.e. a matrix with ones along the matrix diagonal and zeros +#' matrix (i.e., a matrix with ones along the matrix diagonal and zeros #' elsewhere), so that planning units are only considered connected if they #' are both allocated to the same zone. #' @@ -44,16 +44,16 @@ NULL #' #' \item{`data` as a `NULL` value}{indicating that connection data should be #' calculated automatically using the [adjacency_matrix()] function. -#' This is the default argument. +#' This is the default argument. #' Note that the connection data must be manually defined #' using one of the other formats below when the planning unit data -#' in the argument to `x` is not spatially referenced (e.g. +#' in the argument to `x` is not spatially referenced (e.g., #' in `data.frame` or `numeric` format).} #' #' \item{`data` as a `matrix`/`Matrix` object}{where rows and columns represent #' different planning units and the value of each cell indicates if the #' two planning units are connected or not. Cell values should be binary -#' `numeric` values (i.e. one or zero). Cells that occur along the +#' `numeric` values (i.e., one or zero). Cells that occur along the #' matrix diagonal have no effect on the solution at all because each #' planning unit cannot be a connected with itself.} #' @@ -66,7 +66,7 @@ NULL #' or not. This data can be used to describe symmetric or #' asymmetric relationships between planning units. By default, #' input data is assumed to be symmetric unless asymmetric data is -#' also included (e.g. if data is present for planning units 2 and 3, then +#' also included (e.g., if data is present for planning units 2 and 3, then #' the same amount of connectivity is expected for planning units 3 and 2, #' unless connectivity data is also provided for planning units 3 and 2).} #' @@ -76,7 +76,7 @@ NULL #' In early versions, this function was named as the #' `add_connected_constraints()` function. #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the constraints +#' @return Object (i.e., [`ConservationProblem-class`]) with the constraints #' added to it. #' #' @seealso diff --git a/R/add_cplex_solver.R b/R/add_cplex_solver.R index 3331b67cb..1932d317f 100644 --- a/R/add_cplex_solver.R +++ b/R/add_cplex_solver.R @@ -18,12 +18,12 @@ NULL #' @details #' [*IBM CPLEX*](https://www.ibm.com/analytics/cplex-optimizer) is a #' commercial optimization software. It is faster than -#' the available open source solvers (e.g. [add_lpsymphony_solver()] and +#' the available open source solvers (e.g., [add_lpsymphony_solver()] and #' [add_rsymphony_solver()]. #' Although formal benchmarks examining the performance of this solver for #' conservation planning problems have yet to be completed, preliminary #' analyses suggest that it performs slightly slower than the *Gurobi* -#' solver (i.e. [add_gurobi_solver()]). +#' solver (i.e., [add_gurobi_solver()]). #' We recommend using this solver if the *Gurobi* solver is not available. #' Licenses are available for the *IBM CPLEX* software to academics at no cost #' (see ). @@ -39,7 +39,7 @@ NULL #' export CPLEX_BIN="/opt/ibm/ILOG/CPLEX_Studio128/cplex/bin/x86-64_linux/cplex" #' ``` #' Note that you may need to change the version -#' number in the file path (i.e. `"CPLEX_Studio128"`). For more information +#' number in the file path (i.e., `"CPLEX_Studio128"`). For more information #' on installing the pkg{cplexAPI} package, please see the #' [official installation instructions for the package](https://github.com/cran/cplexAPI/blob/master/inst/INSTALL). #' diff --git a/R/add_cuts_portfolio.R b/R/add_cuts_portfolio.R index c79cd99af..6df40d638 100644 --- a/R/add_cuts_portfolio.R +++ b/R/add_cuts_portfolio.R @@ -9,7 +9,7 @@ NULL #' [add_gap_portfolio()] when the *Gurobi* software is not #' available. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param number_solutions `integer` number of attempts to generate #' different solutions. Defaults to 10. @@ -22,11 +22,11 @@ NULL # #' @section Notes: #' In early versions (< 4.0.1), this function was only compatible with -#' *Gurobi* (i.e. [add_gurobi_solver()]). To provide functionality with +#' *Gurobi* (i.e., [add_gurobi_solver()]). To provide functionality with #' exact algorithm solvers, this function now adds constraints to the #' problem formulation to generate multiple solutions. #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the portfolio +#' @return Object (i.e., [`ConservationProblem-class`]) with the portfolio #' added to it. #' #' @seealso diff --git a/R/add_default_decisions.R b/R/add_default_decisions.R index 93cfd0b5c..e31a51973 100644 --- a/R/add_default_decisions.R +++ b/R/add_default_decisions.R @@ -7,7 +7,7 @@ NULL #' [problem()]. The default types are binary and are added using #' the [add_binary_decisions()] function. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @inherit add_binary_decisions return #' diff --git a/R/add_default_portfolio.R b/R/add_default_portfolio.R index 2d83bf234..5f86fecd0 100644 --- a/R/add_default_portfolio.R +++ b/R/add_default_portfolio.R @@ -6,7 +6,7 @@ NULL #' Generate a portfolio for a conservation planning [problem()] #' that contains a single solution. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @seealso #' See [portfolios] for an overview of all functions for adding a portfolio. diff --git a/R/add_default_solver.R b/R/add_default_solver.R index 445c47d28..c46af02ae 100644 --- a/R/add_default_solver.R +++ b/R/add_default_solver.R @@ -10,7 +10,7 @@ NULL #' run time and solution quality of some of the available solvers when applied #' to different sized datasets. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param ... arguments passed to the solver. #' diff --git a/R/add_extra_portfolio.R b/R/add_extra_portfolio.R index f5fa72628..7d105ee76 100644 --- a/R/add_extra_portfolio.R +++ b/R/add_extra_portfolio.R @@ -10,10 +10,10 @@ NULL #' but does not provide any guarantees on the number of solutions, or #' the quality of solutions. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @details This strategy for generating a portfolio requires problems to -#' be solved using the *Gurobi* software suite (i.e. using +#' be solved using the *Gurobi* software suite (i.e., using #' [add_gurobi_solver()]. Specifically, version 8.0.0 (or greater) #' of the \pkg{gurobi} package must be installed. #' diff --git a/R/add_feature_contiguity_constraints.R b/R/add_feature_contiguity_constraints.R index 7a21e79aa..01ec9e52a 100644 --- a/R/add_feature_contiguity_constraints.R +++ b/R/add_feature_contiguity_constraints.R @@ -13,13 +13,13 @@ NULL #' feature to ensure that all features can disperse through out the areas #' designated for their conservation. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param zones `matrix`, `Matrix` or `list` object describing #' the connection scheme for different zones. For `matrix` or #' and `Matrix` arguments, each row and column corresponds #' to a different zone in the argument to `x`, and cell values must -#' contain binary `numeric` values (i.e. one or zero) that indicate +#' contain binary `numeric` values (i.e., one or zero) that indicate #' if connected planning units (as specified in the argument to #' `data`) should be still considered connected if they are allocated to #' different zones. The cell values along the diagonal @@ -32,7 +32,7 @@ NULL #' be a `list` of `matrix` or `Matrix` objects that shows the #' specific scheme for each feature using the conventions described above. #' The default argument to `zones` is an identity -#' matrix (i.e. a matrix with ones along the matrix diagonal and zeros +#' matrix (i.e., a matrix with ones along the matrix diagonal and zeros #' elsewhere), so that planning units are only considered connected if they #' are both allocated to the same zone. #' @@ -73,13 +73,13 @@ NULL #' as potentially dispersible for all features. #' Note that the connection data must be manually defined #' using one of the other formats below when the planning unit data -#' in the argument to `x` is not spatially referenced (e.g. +#' in the argument to `x` is not spatially referenced (e.g., #' in `data.frame` or `numeric` format).} #' #' \item{`data` as a`matrix`/`Matrix` object}{where rows and columns represent #' different planning units and the value of each cell indicates if the #' two planning units are connected or not. Cell values should be binary -#' `numeric` values (i.e. one or zero). Cells that occur along the +#' `numeric` values (i.e., one or zero). Cells that occur along the #' matrix diagonal have no effect on the solution at all because each #' planning unit cannot be a connected with itself. Note that pairs #' of connected planning units are treated as being potentially dispersible @@ -94,7 +94,7 @@ NULL #' or not. This data can be used to describe symmetric or #' asymmetric relationships between planning units. By default, #' input data is assumed to be symmetric unless asymmetric data is -#' also included (e.g. if data is present for planning units 2 and 3, then +#' also included (e.g., if data is present for planning units 2 and 3, then #' the same amount of connectivity is expected for planning units 3 and 2, #' unless connectivity data is also provided for planning units 3 and 2). #' Note that pairs of connected planning units are treated as being diff --git a/R/add_feature_weights.R b/R/add_feature_weights.R index 55c834a94..915507ef7 100644 --- a/R/add_feature_weights.R +++ b/R/add_feature_weights.R @@ -12,14 +12,14 @@ NULL #' representation of some features over other features when making decisions #' about how the budget should be allocated. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param weights `numeric` or `matrix` of weights. #' See the Weights format section for more information. #' #' @details #' Weights can only be applied to problems that have an objective -#' that is budget limited (e.g. [add_max_cover_objective()]). +#' that is budget limited (e.g., [add_max_cover_objective()]). #' They can be applied to problems that aim to maximize phylogenetic #' representation ([add_max_phylo_div_objective()]) to favor the #' representation of specific features over the representation of @@ -54,7 +54,7 @@ NULL #' #' } #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the weights +#' @return Object (i.e., [`ConservationProblem-class`]) with the weights #' added to it. #' #' @seealso @@ -88,7 +88,7 @@ NULL #' #' # create manually specified weights that assign higher importance to #' # certain features. These weights could be based on a pre-calculated index -#' # (e.g. an index measuring extinction risk where higher values +#' # (e.g., an index measuring extinction risk where higher values #' # denote higher extinction risk) #' w3 <- c(0, 0, 0, 100, 200) #' p3 <- p1 %>% add_feature_weights(w3) @@ -130,18 +130,18 @@ NULL #' tip.color = replace(rep("black", nlayers(sim_features)), #' which(r4$met), "red")) #' } -#' # we can see here that the third feature ("layer.3", i.e. +#' # we can see here that the third feature ("layer.3", i.e., #' # sim_features[[3]]) is not represented in the solution. Let us pretend #' # that it is absolutely critical this feature is adequately conserved #' # in the solution. For example, this feature could represent a species #' # that plays important role in the ecosystem, or a species that is -#' # important commercial activities (e.g. eco-tourism). So, to generate +#' # important commercial activities (e.g., eco-tourism). So, to generate #' # a solution that conserves the third feature whilst also aiming to #' # maximize phylogenetic diversity, we will create a set of weights that #' # assign a particularly high weighting to the third feature #' w5 <- c(0, 0, 1000, 0, 0) #' -#' # we can see that this weighting (i.e. w5[3]) has a much higher value than +#' # we can see that this weighting (i.e., w5[3]) has a much higher value than #' # the branch lengths in the phylogeny so solutions that represent this #' # feature be much closer to optimality #' print(sim_phylogeny$edge.length) diff --git a/R/add_gap_portfolio.R b/R/add_gap_portfolio.R index 1359cd596..5216ab499 100644 --- a/R/add_gap_portfolio.R +++ b/R/add_gap_portfolio.R @@ -10,7 +10,7 @@ NULL #' frequencies for moderate and large-sized problems (similar to #' *Marxan*). #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param number_solutions `integer` number of solutions required. #' @@ -18,19 +18,20 @@ NULL #' This relative gap specifies a threshold worst-case performance for #' solutions in the portfolio. For example, value of 0.1 will result in the #' portfolio returning solutions that are within 10% of an optimal solution. -#' Note that the gap specified in the solver (i.e. +#' Note that the gap specified in the solver (i.e., #' [add_gurobi_solver()] must be less than or equal to the gap #' specified to generate the portfolio. Defaults to 0.1. #' #' @details This strategy for generating a portfolio requires problems to -#' be solved using the *Gurobi* software suite (i.e. using +#' be solved using the *Gurobi* software suite (i.e., using #' [add_gurobi_solver()]. Specifically, version 9.0.0 (or greater) #' of the \pkg{gurobi} package must be installed. #' Note that the number of solutions returned may be less than the argument to #' `number_solutions`, if the total number of solutions that #' meet the optimality gap is less than the number of solutions requested. #' Also, note that this portfolio function only works with problems -#' that have binary decisions (i.e. specified using [add_binary_decisions()]). +#' that have binary decisions (i.e., specified using +#' [add_binary_decisions()]). #' #' @inherit add_cuts_portfolio return #' diff --git a/R/add_gurobi_solver.R b/R/add_gurobi_solver.R index 3b89796a3..deee3eb15 100644 --- a/R/add_gurobi_solver.R +++ b/R/add_gurobi_solver.R @@ -10,7 +10,7 @@ NULL #' It requires the \pkg{gurobi} package to be installed #' (see below for installation instructions). #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param gap `numeric` gap to optimality. This gap is relative #' and expresses the acceptable deviance from the optimal objective. @@ -18,12 +18,12 @@ NULL #' it has found a solution within 1% of optimality. #' Additionally, a value of 0 will result in the solver stopping #' when it has found an optimal solution. -#' The default value is 0.1 (i.e. 10% from optimality). +#' The default value is 0.1 (i.e., 10% from optimality). #' #' @param time_limit `numeric` time limit (seconds) for generating solutions. #' The solver will return the current best solution when this time limit is #' exceeded. The default value is the largest integer value -#' (i.e. `.Machine$integer.max`), effectively meaning that solver +#' (i.e., `.Machine$integer.max`), effectively meaning that solver #' will keep running until a solution within the optimality gap is found. #' #' @param presolve `integer` number indicating how intensively the @@ -57,7 +57,7 @@ NULL #' will begin storing this information on disk #' (using the *Gurobi( `NodeFileStart` parameter). #' This functionality is useful if the system has insufficient memory to -#' solve a given problem (e.g. solving the problem with default settings +#' solve a given problem (e.g., solving the problem with default settings #' yields the `OUT OF MEMORY` error message) and a system with more memory is #' not readily available. #' For example, a value of 4 indicates that the solver will start using @@ -69,7 +69,7 @@ NULL #' @param start_solution `NULL` or object containing the starting solution #' for the solver. Defaults to `NULL` such that no starting solution is used. #' To specify a starting solution, the argument to `start_solution` should -#' be in the same format as the planning units (i.e. a `NULL`, `numeric`, +#' be in the same format as the planning units (i.e., a `NULL`, `numeric`, #' `matrix`, `data.frame`, [`Raster-class`], [`Spatial-class`], #' or [sf::sf()] object). #' See the Start solution format section for more information. @@ -106,7 +106,7 @@ NULL #' for each of the different planning unit data formats: #' `r solution_format_documentation("start_solution")` #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the solver +#' @return Object (i.e., [`ConservationProblem-class`]) with the solver #' added to it. #' #' @seealso diff --git a/R/add_linear_constraints.R b/R/add_linear_constraints.R index c2c36c187..96ef7c8d5 100644 --- a/R/add_linear_constraints.R +++ b/R/add_linear_constraints.R @@ -32,12 +32,12 @@ NULL #' (see Examples section below for details). #' For example, these constraints can be used to add multiple budgets. #' They can also be used to ensure that the total number of planning units -#' allocated to a certain administrative area (e.g. country) does not exceed -#' a certain threshold (e.g. 30% of its total area). Furthermore, +#' allocated to a certain administrative area (e.g., country) does not exceed +#' a certain threshold (e.g., 30% of its total area). Furthermore, #' they can also be used to ensure that features have a minimal level -#' of representation (e.g. 30%) when using an objective +#' of representation (e.g., 30%) when using an objective #' function that aims to enhance feature representation given a budget -#' (e.g. [add_min_shortfall_objective()]). +#' (e.g., [add_min_shortfall_objective()]). #' #' @section Mathematical formulation: #' The linear constraints are implemented using the following @@ -45,13 +45,13 @@ NULL #' Let \eqn{I} denote the set of planning units #' (indexed by \eqn{i}), \eqn{Z} the set of management zones (indexed by #' \eqn{z}), and \eqn{X_{iz}}{Xiz} the decision variable for allocating -#' planning unit \eqn{i} to zone \eqn{z} (e.g. with binary +#' planning unit \eqn{i} to zone \eqn{z} (e.g., with binary #' values indicating if each planning unit is allocated or not). Also, let #' \eqn{D_{iz}}{Diz} denote the constraint data associated with #' planning units \eqn{i \in I}{i in I} for zones \eqn{z \in Z}{z in Z} #' (argument to `data`, if supplied as a `matrix` object), #' \eqn{\theta} denote the constraint sense -#' (argument to `sense`, e.g. \eqn{<=}), and \eqn{t} denote the constraint +#' (argument to `sense`, e.g., \eqn{<=}), and \eqn{t} denote the constraint #' threshold (argument to `threshold`). #' #' \deqn{ @@ -149,7 +149,7 @@ NULL #' # third, let's create a modified version of p0 that contains #' # additional constraints to ensure that the solution equitably #' # distributes conservation effort across different administrative areas -#' # (e.g. countries) within the study region +#' # (e.g., countries) within the study region #' #' # to begin with, we will simulate a dataset describing the spatial extent of #' # four different administrative areas across the study region diff --git a/R/add_linear_penalties.R b/R/add_linear_penalties.R index f35bdb0ac..3d872fed9 100644 --- a/R/add_linear_penalties.R +++ b/R/add_linear_penalties.R @@ -5,12 +5,12 @@ NULL #' #' Add penalties to a conservation planning [problem()] to penalize #' solutions that select planning units with higher values from a specific -#' data source (e.g. anthropogenic impact). These penalties assume +#' data source (e.g., anthropogenic impact). These penalties assume #' a linear trade-off between the penalty values and the primary -#' objective of the conservation planning [problem()] (e.g. +#' objective of the conservation planning [problem()] (e.g., #' solution cost for minimum set problems; [add_min_set_objective()]. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param penalty `numeric` penalty value that is used to scale the #' importance not selecting planning units with high `data` values. @@ -88,7 +88,7 @@ NULL #' Let \eqn{I} denote the set of planning units #' (indexed by \eqn{i}), \eqn{Z} the set of management zones (indexed by #' \eqn{z}), and \eqn{X_{iz}}{Xiz} the decision variable for allocating -#' planning unit \eqn{i} to zone \eqn{z} (e.g. with binary +#' planning unit \eqn{i} to zone \eqn{z} (e.g., with binary #' values indicating if each planning unit is allocated or not). Also, let #' \eqn{P_z} represent the penalty scaling value for zones #' \eqn{z \in Z}{z in Z} (argument to `penalty`), and @@ -121,7 +121,7 @@ NULL #' data(sim_pu_polygons, sim_pu_zones_stack, sim_features, sim_features_zones) #' #' # add a column to contain the penalty data for each planning unit -#' # e.g. these values could indicate the level of habitat +#' # e.g., these values could indicate the level of habitat #' sim_pu_polygons$penalty_data <- runif(nrow(sim_pu_polygons)) #' #' # plot the penalty data to visualise its spatial distribution @@ -152,7 +152,7 @@ NULL #' s2 <- solve(p2) #' #' # plot the solutions and compare them, -#' # since we supplied a very high penalty value (i.e. 100), relative +#' # since we supplied a very high penalty value (i.e., 100), relative #' # to the range of values in the penalty data and the objective function, #' # the solution in s2 is very sensitive to values in the penalty data #' spplot(s1, zcol = "solution_1", main = "solution without penalties", @@ -161,7 +161,7 @@ NULL #' axes = FALSE, box = FALSE) #' #' # for real conservation planning exercises, -#' # it would be worth exploring a range of penalty values (e.g. ranging +#' # it would be worth exploring a range of penalty values (e.g., ranging #' # from 1 to 100 increments of 5) to explore the trade-offs #' } #' diff --git a/R/add_locked_in_constraints.R b/R/add_locked_in_constraints.R index 80dcf0e65..32a6e3441 100644 --- a/R/add_locked_in_constraints.R +++ b/R/add_locked_in_constraints.R @@ -10,13 +10,13 @@ NULL #' solution fills in the gaps in the existing reserve network. If specific #' planning units should be locked out of a solution, use #' [add_locked_out_constraints()]. For problems with non-binary -#' planning unit allocations (e.g. proportions), the +#' planning unit allocations (e.g., proportions), the #' [add_manual_locked_constraints()] function can be used to lock #' planning unit allocations to a specific value. #' #' @usage add_locked_in_constraints(x, locked_in) #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param locked_in Object that determines which planning units that should be #' locked in. See the Data format section for more information. @@ -58,7 +58,7 @@ NULL #' compatible if the planning units in the argument to `x` are a #' [`Spatial-class`], [sf::sf()], or #' `data.frame` object. The fields -#' (columns) must have `logical` (i.e. `TRUE` or `FALSE`) +#' (columns) must have `logical` (i.e., `TRUE` or `FALSE`) #' values indicating if the planning unit is to be locked for the solution. #' For problems that contain a single zone, the argument to `data` must #' contain a single field name. Otherwise, for problems that diff --git a/R/add_locked_out_constraints.R b/R/add_locked_out_constraints.R index da4c765da..a52de85ee 100644 --- a/R/add_locked_out_constraints.R +++ b/R/add_locked_out_constraints.R @@ -9,13 +9,13 @@ NULL #' useful to lock out planning units that have been degraded and are not #' suitable for conserving species. If specific planning units should be locked #' in to the solution, use [add_locked_out_constraints()]. For -#' problems with non-binary planning unit allocations (e.g. proportions), the +#' problems with non-binary planning unit allocations (e.g., proportions), the #' [add_manual_locked_constraints()] function can be used to lock #' planning unit allocations to a specific value. #' #' @usage add_locked_out_constraints(x, locked_out) #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param locked_out Object that determines which planning units that should be #' locked out. See the Data format section for more information. diff --git a/R/add_loglinear_targets.R b/R/add_loglinear_targets.R index f735d0ee9..f50abb389 100644 --- a/R/add_loglinear_targets.R +++ b/R/add_loglinear_targets.R @@ -10,7 +10,7 @@ NULL #' distributions from being over-represented #' in solutions (Butchart *et al.* 2015). #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param lower_bound_amount `numeric` threshold. #' @@ -56,7 +56,7 @@ NULL #' #' The target calculations do not account for the #' size of each planning unit. Therefore, the feature data should account for -#' the size of each planning unit if this is important (e.g. pixel values in +#' the size of each planning unit if this is important (e.g., pixel values in #' the argument to `features` in the function [problem()] could #' correspond to amount of land occupied by the feature in \eqn{km^2} units). #' Additionally, the function can only be applied to diff --git a/R/add_mandatory_allocation_constraints.R b/R/add_mandatory_allocation_constraints.R index e4ed7676f..5a42249cf 100644 --- a/R/add_mandatory_allocation_constraints.R +++ b/R/add_mandatory_allocation_constraints.R @@ -7,7 +7,7 @@ NULL #' management zone in the solution. Note that this function can only be used #' with problems that contain multiple zones. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @details For a conservation planning [problem()] with multiple #' management zones, it may sometimes be desirable to obtain a solution that @@ -16,11 +16,11 @@ NULL #' every single parcel of land has been allocated a specific land-use type. #' In other words are no "left over" areas. Although it might seem tempting #' to simply solve the problem and manually assign "left over" planning units -#' to a default zone afterwards (e.g. an "other", "urban", or "grazing" +#' to a default zone afterwards (e.g., an "other", "urban", or "grazing" #' land-use), this could result in highly sub-optimal solutions if there #' penalties for siting the default land-use adjacent to other zones. #' Instead, this function can be used to specify that all planning units in a -#' problem with multiple zones must be allocated to a management zone (i.e. +#' problem with multiple zones must be allocated to a management zone (i.e., #' zone allocation is mandatory). #' #' @inherit add_contiguity_constraints return diff --git a/R/add_manual_bounded_constraints.R b/R/add_manual_bounded_constraints.R index f827ada11..a00358102 100644 --- a/R/add_manual_bounded_constraints.R +++ b/R/add_manual_bounded_constraints.R @@ -4,7 +4,7 @@ NULL #' Add manually specified bound constraints #' #' Add constraints to a conservation planning [problem()] to ensure -#' that the planning unit values (e.g. proportion, binary) in a solution +#' that the planning unit values (e.g., proportion, binary) in a solution #' range between specific lower and upper bounds. This function offers more #' fine-grained control than the [add_manual_locked_constraints()] #' function and is is most useful for problems involving proportion-type @@ -12,7 +12,7 @@ NULL #' #' @usage add_manual_bounded_constraints(x, data) #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param data `data.frame` or [tibble::tibble()] object. #' See the Data format section for more information. @@ -213,7 +213,7 @@ methods::setMethod("add_manual_bounded_constraints", data$zone <- x$zone_names()[1] data$zone <- match(as.character(data$zone), x$zone_names()) # remove rows for raster cells that aren't really planning units - # i.e. contain NA values in all zones + # i.e., contain NA values in all zones pu <- x$get_data("cost") if (inherits(pu, "Raster")) { if (raster::nlayers(pu) == 1) { diff --git a/R/add_manual_locked_constraints.R b/R/add_manual_locked_constraints.R index 4f73c78aa..cd0932582 100644 --- a/R/add_manual_locked_constraints.R +++ b/R/add_manual_locked_constraints.R @@ -11,7 +11,7 @@ NULL #' #' @usage add_manual_locked_constraints(x, data) #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param data `data.frame` or [tibble::tibble()] object. #' See the Data format section for more information. @@ -30,10 +30,10 @@ NULL #' #' \item{status}{`numeric` values indicating how much #' of each planning unit should be allocated to each zone in the solution. -#' For example, the `numeric` values could be binary values (i.e. zero +#' For example, the `numeric` values could be binary values (i.e., zero #' or one) for problems containing binary-type decision variables #' (using the [add_binary_decisions()] function). Alternatively, -#' the `numeric` values could be proportions (e.g. 0.5) for problems +#' the `numeric` values could be proportions (e.g., 0.5) for problems #' containing proportion-type decision variables (using the #' [add_proportion_decisions()]).} #' @@ -224,7 +224,7 @@ methods::setMethod("add_manual_locked_constraints", data$zone <- x$zone_names()[1] data$zone <- match(as.character(data$zone), x$zone_names()) # remove rows for raster cells that aren't really planning units - # i.e. contain NA values in all zones + # i.e., contain NA values in all zones pu <- x$get_data("cost") if (inherits(pu, "Raster")) { if (raster::nlayers(pu) == 1) { diff --git a/R/add_manual_targets.R b/R/add_manual_targets.R index 44dbafa15..1204a8fac 100644 --- a/R/add_manual_targets.R +++ b/R/add_manual_targets.R @@ -11,10 +11,10 @@ NULL #' functions. However, this function can be used to (i) mix absolute and #' relative targets for different features and zones, (ii) set targets that #' pertain to the allocations of planning units in multiple zones, and (iii) -#' set targets that require different senses (e.g. targets which specify the +#' set targets that require different senses (e.g., targets which specify the #' solution should not exceed a certain quantity using `"<="` values). #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param targets `data.frame` or [tibble::tibble()] object. #' See the Target data format section for more information. @@ -49,7 +49,7 @@ NULL #' #' } #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the targets added +#' @return Object (i.e., [`ConservationProblem-class`]) with the targets added #' to it. #' #' @seealso diff --git a/R/add_max_cover_objective.R b/R/add_max_cover_objective.R index b17a562dd..ce9981b5c 100644 --- a/R/add_max_cover_objective.R +++ b/R/add_max_cover_objective.R @@ -9,7 +9,7 @@ NULL #' weights should be used instead to increase the representation of certain #' features by a solution. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param budget `numeric` value specifying the maximum expenditure of #' the prioritization. For problems with multiple zones, the argument @@ -22,10 +22,10 @@ NULL #' maximizes the number of represented features, while keeping cost within a #' fixed budget. Here, features are treated as being represented if #' the reserve system contains at least a single instance of a feature -#' (i.e. an amount greater than 1). This formulation has often been +#' (i.e., an amount greater than 1). This formulation has often been #' used in conservation planning problems dealing with binary biodiversity #' data that indicate the presence/absence of suitable habitat -#' (e.g. Church & Velle 1974). Additionally, weights can be used to favor the +#' (e.g., Church & Velle 1974). Additionally, weights can be used to favor the #' representation of certain features over other features (see #' [add_feature_weights()]). Check out the #' [add_max_features_objective()] for a more @@ -48,7 +48,7 @@ NULL #' sum_i^I (xi * rij) >= (yj * 1) for all j in J & #' sum_i^I (xi * ci) <= B} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning #' unit \eqn{i}{i}, \eqn{y_j}{yj} indicates if the solution has meet diff --git a/R/add_max_features_objective.R b/R/add_max_features_objective.R index f551c8f17..081bd7e0e 100644 --- a/R/add_max_features_objective.R +++ b/R/add_max_features_objective.R @@ -37,7 +37,7 @@ NULL #' sum_i^I (xi * rij) >= (yj tj) for all j in J & #' sum_i^I (xi * ci) <= B} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning #' unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/R/add_max_phylo_div_objective.R b/R/add_max_phylo_div_objective.R index e91d63b53..b896c1b47 100644 --- a/R/add_max_phylo_div_objective.R +++ b/R/add_max_phylo_div_objective.R @@ -59,7 +59,7 @@ NULL #' (xi * rij) >= (yj * tj) for all j in J & mb <= yj for all j in T(b) & #' sum_i^I (xi * ci) <= B} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning #' unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/R/add_max_phylo_end_objective.R b/R/add_max_phylo_end_objective.R index 3580dffa0..09cc42f18 100644 --- a/R/add_max_phylo_end_objective.R +++ b/R/add_max_phylo_end_objective.R @@ -56,7 +56,7 @@ NULL #' subject to sum_i^I (xi * rij) >= (yj * tj) for all j in J & #' mb <= yj for all j in T(b) & sum_i^I (xi * ci) <= B} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning #' unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/R/add_max_utility_objective.R b/R/add_max_utility_objective.R index 0c59e9a2b..bc3407000 100644 --- a/R/add_max_utility_objective.R +++ b/R/add_max_utility_objective.R @@ -12,7 +12,7 @@ NULL #' possible, and so often results in solutions that are heavily biased towards #' just a few features. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param budget `numeric` value specifying the maximum expenditure of #' the prioritization. For problems with multiple zones, the argument @@ -40,7 +40,7 @@ NULL #' Maximize sum_i^I (-s * ci * xi) + sum_j^J (aj * wj) subject to #' aj = sum_i^I (xi * rij) for all j in J & sum_i^I (xi * ci) <= B} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning #' unit \eqn{i}{i}, \eqn{A_j}{Aj} is the amount of feature \eqn{j}{j} diff --git a/R/add_min_largest_shortfall_objective.R b/R/add_min_largest_shortfall_objective.R index 5e5ea6508..83b6c43f9 100644 --- a/R/add_min_largest_shortfall_objective.R +++ b/R/add_min_largest_shortfall_objective.R @@ -7,7 +7,7 @@ NULL #' minimize the largest target shortfall while ensuring that #' the cost of the solution does not exceed a budget. Note that if the #' target shortfall for a single feature cannot be decreased beyond a certain -#' point (e.g. because all remaining planning units occupied by that feature +#' point (e.g., because all remaining planning units occupied by that feature #' are too costly or are locked out), then solutions may only use a small #' proportion of the specified budget. #' @@ -41,7 +41,7 @@ NULL #' l >= (yj / tj) for all j in J & #' sum_i^I (xi * ci) <= B} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning #' unit \eqn{i}{i}, and \eqn{t_j}{tj} is the representation target for feature diff --git a/R/add_min_set_objective.R b/R/add_min_set_objective.R index 2fc40f8f1..f5c4e5246 100644 --- a/R/add_min_set_objective.R +++ b/R/add_min_set_objective.R @@ -8,7 +8,7 @@ NULL #' This objective is similar to that used in #' *Marxan* and is detailed in Rodrigues *et al.* (2000). #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @details #' The minimum set objective -- in the the context of systematic reserve @@ -31,7 +31,7 @@ NULL #' Minimize sum_i^I (xi * ci) subject to sum_i^I (xi * rij) >= Tj for all #' j in J} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{c_i}{ci} is the cost of planning unit \eqn{i}{i}, #' \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit @@ -51,7 +51,7 @@ NULL #' #' @family objectives #' -#' @return Object (i.e. [`ConservationProblem-class`]) with the objective +#' @return Object (i.e., [`ConservationProblem-class`]) with the objective #' added to it. #' #' @examples diff --git a/R/add_min_shortfall_objective.R b/R/add_min_shortfall_objective.R index c7817c857..ed9e162da 100644 --- a/R/add_min_shortfall_objective.R +++ b/R/add_min_shortfall_objective.R @@ -33,7 +33,7 @@ NULL #' sum_i^I (xi * rij) + yj >= tj for all j in J & #' sum_i^I (xi * ci) <= B} #' -#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g. +#' Here, \eqn{x_i}{xi} is the [decisions] variable (e.g., #' specifying whether planning unit \eqn{i}{i} has been selected (1) or not #' (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning #' unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/R/add_neighbor_constraints.R b/R/add_neighbor_constraints.R index bedd37549..67981ae22 100644 --- a/R/add_neighbor_constraints.R +++ b/R/add_neighbor_constraints.R @@ -7,7 +7,7 @@ NULL #' that all selected planning units in the solution have at least a certain #' number of neighbors that are also selected in the solution. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param k `integer` minimum number of neighbors for selected #' planning units in the solution. For problems with multiple zones, @@ -16,13 +16,13 @@ NULL #' @param zones `matrix` or `Matrix` object describing the #' neighborhood scheme for different zones. Each row and column corresponds #' to a different zone in the argument to `x`, and cell values must -#' contain binary `numeric` values (i.e. one or zero) that indicate +#' contain binary `numeric` values (i.e., one or zero) that indicate #' if neighboring planning units (as specified in the argument to #' `data`) should be considered neighbors if they are allocated to #' different zones. The cell values along the diagonal #' of the matrix indicate if planning units that are allocated to the same #' zone should be considered neighbors or not. The default argument to -#' `zones` is an identity matrix (i.e. a matrix with ones along the +#' `zones` is an identity matrix (i.e., a matrix with ones along the #' matrix diagonal and zeros elsewhere), so that planning units are #' only considered neighbors if they are both allocated to the same zone. #' @@ -48,13 +48,13 @@ NULL #' using the [adjacency_matrix()] function. This is the default #' argument. Note that the neighborhood data must be manually defined #' using one of the other formats below when the planning unit data -#' in the argument to `x` is not spatially referenced (e.g. +#' in the argument to `x` is not spatially referenced (e.g., #' in `data.frame` or `numeric` format).} #' #' \item{`data` as a `matrix`/`Matrix` object}{where rows and columns represent #' different planning units and the value of each cell indicates if the #' two planning units are neighbors or not. Cell values should be binary -#' `numeric` values (i.e. one or zero). Cells that occur along the +#' `numeric` values (i.e., one or zero). Cells that occur along the #' matrix diagonal have no effect on the solution at all because each #' planning unit cannot be a neighbor with itself.} #' @@ -67,7 +67,7 @@ NULL #' or not. This data can be used to describe symmetric or #' asymmetric relationships between planning units. By default, #' input data is assumed to be symmetric unless asymmetric data is -#' also included (e.g. if data is present for planning units 2 and 3, then +#' also included (e.g., if data is present for planning units 2 and 3, then #' the same amount of connectivity is expected for planning units 3 and 2, #' unless connectivity data is also provided for planning units 3 and 2). #' If the argument to `x` contains multiple zones, then the columns @@ -81,7 +81,7 @@ NULL #' `numeric` values indicate if planning unit should be treated #' as being neighbors with every other planning unit when they #' are allocated to every combination of management zone. The first two -#' dimensions (i.e. rows and columns) correspond to the planning units, +#' dimensions (i.e., rows and columns) correspond to the planning units, #' and second two dimensions correspond to the management zones. For #' example, if the argument to `data` had a value of 1 at the index #' `data[1, 2, 3, 4]` this would indicate that planning unit 1 and diff --git a/R/add_proportion_decisions.R b/R/add_proportion_decisions.R index 11cd2ca16..5cf51ddb5 100644 --- a/R/add_proportion_decisions.R +++ b/R/add_proportion_decisions.R @@ -11,7 +11,7 @@ NULL #' decisions will solve much faster than problems that use binary-type #' decisions #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @inherit add_binary_decisions details return #' diff --git a/R/add_relative_targets.R b/R/add_relative_targets.R index e8eeb4da9..7c09c7803 100644 --- a/R/add_relative_targets.R +++ b/R/add_relative_targets.R @@ -9,7 +9,7 @@ NULL #' (including any locked out planning units, or planning units with `NA` #' cost data) using the [feature_abundances()] function. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param targets Object that specifies the targets for each feature. #' See the Targets format section for more information. diff --git a/R/add_semicontinuous_decisions.R b/R/add_semicontinuous_decisions.R index 27b60f15b..ca5a7c857 100644 --- a/R/add_semicontinuous_decisions.R +++ b/R/add_semicontinuous_decisions.R @@ -11,14 +11,14 @@ NULL #' function except that it has an upper bound parameter. By default, the #' decision can range from prioritizing none (0%) to all (100%) of a #' planning unit. However, an upper bound can be specified to ensure that at -#' most only a fraction (e.g. 80%) of a planning unit can be preserved. This +#' most only a fraction (e.g., 80%) of a planning unit can be preserved. This #' type of decision may be useful when it is not practical to conserve entire #' planning units. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param upper_limit `numeric` value specifying the maximum proportion -#' of a planning unit that can be reserved (e.g. set to 0.8 for 80%). +#' of a planning unit that can be reserved (e.g., set to 0.8 for 80%). #' #' @inherit add_binary_decisions details return #' diff --git a/R/add_shuffle_portfolio.R b/R/add_shuffle_portfolio.R index 90d012f88..4b4b89b25 100644 --- a/R/add_shuffle_portfolio.R +++ b/R/add_shuffle_portfolio.R @@ -9,7 +9,7 @@ NULL #' [add_top_portfolio()] when the *Gurobi* software is not #' available. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param number_solutions `integer` number of attempts to generate #' different solutions. Defaults to 10. diff --git a/R/add_top_portfolio.R b/R/add_top_portfolio.R index 9d03e6cec..f0d66d58c 100644 --- a/R/add_top_portfolio.R +++ b/R/add_top_portfolio.R @@ -7,12 +7,12 @@ NULL #' [problem()] by finding a pre-specified number of solutions that #' are closest to optimality (i.e the top solutions). #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param number_solutions `integer` number of solutions required. #' #' @details This strategy for generating a portfolio requires problems to -#' be solved using the *Gurobi* software suite (i.e. using +#' be solved using the *Gurobi* software suite (i.e., using #' [add_gurobi_solver()]. Specifically, version 9.0.0 (or greater) #' of the \pkg{gurobi} package must be installed. #' Note that the number of solutions returned may be less than the argument to diff --git a/R/boundary_matrix.R b/R/boundary_matrix.R index 3823d9ab1..59aad27b0 100644 --- a/R/boundary_matrix.R +++ b/R/boundary_matrix.R @@ -20,7 +20,7 @@ NULL #' will be used to pre-compute which planning units are adjacent to #' each other and potentially reduce the processing time required to #' generate the boundary matrices. This argument is only used when -#' the planning unit data are vector-based polygons (i.e. +#' the planning unit data are vector-based polygons (i.e., #' [sp::SpatialPolygonsDataFrame()] objects). **Note that #' using `TRUE` may crash Mac OSX systems.** The default argument #' is `FALSE`. @@ -29,11 +29,11 @@ NULL #' symmetric sparse matrix. Cells on the off-diagonal indicate the length of #' the shared boundary between two different planning units. Cells on the #' diagonal indicate length of a given planning unit's edges that have no -#' neighbors (e.g. for edges of planning units found along the +#' neighbors (e.g., for edges of planning units found along the #' coastline). **This function assumes the data are in a coordinate #' system where Euclidean distances accurately describe the proximity #' between two points on the earth**. Thus spatial data in a longitude/latitude -#' coordinate system (i.e. +#' coordinate system (i.e., #' [WGS84](https://spatialreference.org/ref/epsg/wgs-84/)) #' should be reprojected to another coordinate system before using this #' function. Note that for [`Raster-class`] objects diff --git a/R/compile.R b/R/compile.R index 440a53064..1261bbef6 100644 --- a/R/compile.R +++ b/R/compile.R @@ -6,7 +6,7 @@ NULL #' Compile a conservation planning [problem()] into an #' (potentially mixed) integer linear programming problem. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param compressed_formulation `logical` should the conservation problem #' compiled into a compressed version of a planning problem? diff --git a/R/connectivity_matrix.R b/R/connectivity_matrix.R index 193ed7250..39e8531f2 100644 --- a/R/connectivity_matrix.R +++ b/R/connectivity_matrix.R @@ -26,7 +26,7 @@ NULL #' Also, note that if the argument to `x` is a #' [`Raster-class`] object then #' argument to `y` must have the same spatial properties as it -#' (i.e. coordinate system, extent, resolution). +#' (i.e., coordinate system, extent, resolution). #' #' @param ... additional arguments passed to [fast_extract()] for #' extracting and calculating the conductance values for each planning unit. @@ -94,7 +94,7 @@ NULL #' # create connectivity matrix using habitat suitability data for each feature, #' # this could be useful if prioritisations should spatially clump #' # together adjacent planning units that have suitable habitat -#' # for the same species (e.g. to maintain functional connectivity) +#' # for the same species (e.g., to maintain functional connectivity) #' #' ## let's use the raster data for this example, and we can generate the #' ## connectivity matrix that we would use in the prioritization by @@ -113,7 +113,7 @@ NULL #' #' ## we could take this example one step further, and use weights to indicate #' ## relative importance of maintaining functional connectivity -#' ## for each feature (i.e. use the weighted sum instead of the sum) +#' ## for each feature (i.e., use the weighted sum instead of the sum) #' #' ## let's pretend that the first feature is 20 times more important #' ## than all the other species @@ -133,7 +133,7 @@ NULL #' } #' #' ## since the statistical distribution of the connectivity values -#' ## for each feature (e.g. the mean and standard deviation of the +#' ## for each feature (e.g., the mean and standard deviation of the #' ## connectivity values) are different, it might make sense -- depending #' ## on the goal of the conservation planning exercise and the underlying #' ## data -- to first normalize the conductance values before applying the diff --git a/R/constraints.R b/R/constraints.R index 40986112e..aaa03e196 100644 --- a/R/constraints.R +++ b/R/constraints.R @@ -56,7 +56,7 @@ NULL #' criteria. For example, they can be used to add #' multiple budgets, or limit the number of #' planning units selected in different administrative areas within a study -#' region (e.g. different countries).} +#' region (e.g., different countries).} #' #' \item{[add_mandatory_allocation_constraints()]}{ #' Add constraints to ensure that every planning unit is allocated to a diff --git a/R/decisions.R b/R/decisions.R index 3c5013419..858e3d5fb 100644 --- a/R/decisions.R +++ b/R/decisions.R @@ -39,7 +39,7 @@ NULL #' bound parameter. By default, the decision can range from prioritizing #' none (0%) to all (100%) of a planning unit. However, a upper #' bound can be specified to ensure that at most only a fraction -#' (e.g. 80%) of a planning unit can be preserved. This type of +#' (e.g., 80%) of a planning unit can be preserved. This type of #' decision may be useful when it is not practical to conserve the #' entire area encompassed by any single planning unit.} #' diff --git a/R/deprecated.R b/R/deprecated.R index 942a83d14..9821bd471 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -13,7 +13,7 @@ NULL #' (where applicable). #' If a function is described as being renamed, then this means #' that only the name of the function has changed -#' (i.e. the inputs, outputs, and underlying code remain the same). +#' (i.e., the inputs, outputs, and underlying code remain the same). #' #' @param ... not used. #' diff --git a/R/eval_boundary_summary.R b/R/eval_boundary_summary.R index 786b8eec0..af940e2eb 100644 --- a/R/eval_boundary_summary.R +++ b/R/eval_boundary_summary.R @@ -18,7 +18,7 @@ NULL #' reported by the [*Marxan* software](https://marxansolutions.org) #' (Ball *et al.* 2009). #' It is calculated using the same equations used to penalize solutions -#' according to their total exposed boundary (i.e. [add_boundary_penalties()]). +#' according to their total exposed boundary (i.e., [add_boundary_penalties()]). #' See the Examples section for examples on how differences `zone` arguments #' can be used to calculate boundaries for different combinations of zones. #' diff --git a/R/eval_connectivity_summary.R b/R/eval_connectivity_summary.R index 251ef946f..a7519ad86 100644 --- a/R/eval_connectivity_summary.R +++ b/R/eval_connectivity_summary.R @@ -16,7 +16,7 @@ NULL #' reported by the #' [*Marxan* software](https://marxansolutions.org) (Ball *et al.* 2009). #' It is calculated using the same equations used to penalize solutions -#' with connectivity data (i.e. [add_connectivity_penalties()]). +#' with connectivity data (i.e., [add_connectivity_penalties()]). #' Specifically, it is calculated as the sum of the pair-wise connectivity #' values in the argument to `data`, weighted by the value of the planning #' units in the solution. diff --git a/R/eval_cost_summary.R b/R/eval_cost_summary.R index 9f05e8f14..375e5e460 100644 --- a/R/eval_cost_summary.R +++ b/R/eval_cost_summary.R @@ -9,7 +9,7 @@ NULL #' (USD), then the total cost would be net cost (USD) needed to acquire #' all planning units selected within the solution. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param solution `numeric`, `matrix`, `data.frame`, #' [`Raster-class`], [`Spatial-class`], @@ -23,7 +23,7 @@ NULL #' [*Marxan* software](https://marxansolutions.org) (Ball *et al.* 2009). #' Specifically, the cost of a solution is defined as the sum of the cost #' values, supplied when creating a [problem()] object -#' (e.g. using the `cost_column` argument), +#' (e.g., using the `cost_column` argument), #' weighted by the status of each planning unit in the solution. #' #' @section Solution format: diff --git a/R/eval_feature_representation_summary.R b/R/eval_feature_representation_summary.R index a3be24cbe..46585d4a7 100644 --- a/R/eval_feature_representation_summary.R +++ b/R/eval_feature_representation_summary.R @@ -14,7 +14,7 @@ NULL #' #' @return [tibble::tibble()] object describing feature representation. #' Here, each row describes a specific summary statistic -#' (e.g. different management zone) for a specific feature. +#' (e.g., different management zone) for a specific feature. #' It contains the following columns: #' #' \describe{ @@ -34,13 +34,14 @@ NULL #' (not just planning units selected within the solution). #' It is calculated as the sum of the feature data, #' supplied when creating a [problem()] object -#' (e.g. presence/absence values).} +#' (e.g., presence/absence values).} #' #' \item{absolute_held}{`numeric` total amount of each feature secured within #' the solution. It is calculated as the sum of the feature data, #' supplied when creating a [problem()] object -#' (e.g. presence/absence values), weighted by the status of each -#' planning unit in the solution (e.g. selected or not for prioritization).} +#' (e.g., presence/absence values), weighted by the status of each +#' planning unit in the solution (e.g., selected or not for +#' prioritization).} #' #' \item{relative_held}{`numeric` proportion of #' each feature secured within the solution. It is calculated diff --git a/R/eval_n_summary.R b/R/eval_n_summary.R index 0496c37ec..8309b911f 100644 --- a/R/eval_n_summary.R +++ b/R/eval_n_summary.R @@ -13,8 +13,8 @@ NULL #' @details #' This summary statistic is calculated as the sum of the values in #' the solution. As a consequence, this metric can produce a -#' non-integer value (e.g. 4.3) for solutions containing proportion values -#' (e.g. generated by solving a [problem()] built using the +#' non-integer value (e.g., 4.3) for solutions containing proportion values +#' (e.g., generated by solving a [problem()] built using the #' [`add_proportion_decisions()`] function). #' #' @return [tibble::tibble()] object containing the number of planning diff --git a/R/eval_rare_richness_importance.R b/R/eval_rare_richness_importance.R index 981c9186f..aad0ad082 100644 --- a/R/eval_rare_richness_importance.R +++ b/R/eval_rare_richness_importance.R @@ -6,7 +6,7 @@ NULL #' Calculate importance scores for planning units selected in a solution #' using rarity weighted richness scores (based on Williams *et al.* 1996). #' This method is only recommended for large-scaled conservation -#' planning exercises (i.e. more than 100,000 planning units) where +#' planning exercises (i.e., more than 100,000 planning units) where #' importance scores cannot be calculated using other methods in a feasible #' period of time. This is because rarity weighted richness scores cannot (i) #' account for the cost of different planning units, (ii) account for multiple diff --git a/R/eval_replacement_importance.R b/R/eval_replacement_importance.R index 869bdddc4..07346049d 100644 --- a/R/eval_replacement_importance.R +++ b/R/eval_replacement_importance.R @@ -47,19 +47,19 @@ NULL #' replacement cost scores correspond to the reduction in the utility when #' each planning unit is locked out. Infinite values mean that no feasible #' solution exists when planning units are locked out---they are -#' absolutely essential for obtaining a solution (e.g. they contain rare +#' absolutely essential for obtaining a solution (e.g., they contain rare #' species that are not found in any other planning units or were locked in). #' Zeros values mean that planning units can swapped with other planning units #' and this will have no effect on the performance of the solution at all -#' (e.g. because they were only selected due to spatial fragmentation +#' (e.g., because they were only selected due to spatial fragmentation #' penalties). #' #' These calculations can take a long time to complete for large #' or complex conservation planning problems. As such, we using this #' method for small or moderate-sized conservation planning problems -#' (e.g. < 30,000 planning units). To reduce run time, we -#' recommend calculating these scores without additional penalties (e.g. -#' [add_boundary_penalties()]) or spatial constraints (e.g. +#' (e.g., < 30,000 planning units). To reduce run time, we +#' recommend calculating these scores without additional penalties (e.g., +#' [add_boundary_penalties()]) or spatial constraints (e.g., #' [add_contiguity_constraints()]). To further reduce run time, #' we recommend using proportion-type decisions when calculating the scores #' (see below for an example). diff --git a/R/eval_target_coverage_summary.R b/R/eval_target_coverage_summary.R index da50d27bc..a2fd140ad 100644 --- a/R/eval_target_coverage_summary.R +++ b/R/eval_target_coverage_summary.R @@ -40,16 +40,16 @@ NULL #' #' \item{sense}{`character` sense associated with each target. #' Sense values specify the nature of the target. -#' Typically (e.g. when using the [add_absolute_targets()] or +#' Typically (e.g., when using the [add_absolute_targets()] or #' [add_relative_targets()] functions), targets are specified using sense #' values indicating that the total amount of a feature held within a #' solution (ideally) be greater than or equal to a threshold amount -#' (i.e. a sense value of `">="`). -#' Additionally, targets (i.e. using the [add_manual_targets()] function) +#' (i.e., a sense value of `">="`). +#' Additionally, targets (i.e., using the [add_manual_targets()] function) #' can also be specified using sense values indicating that the total #' amount of a feature held within a solution must be equal to a -#' threshold amount (i.e. a sense value of `"="`) or smaller than or equal -#' to a threshold amount (i.e. a sense value of `"<="`). +#' threshold amount (i.e., a sense value of `"="`) or smaller than or equal +#' to a threshold amount (i.e., a sense value of `"<="`). #' This column is only included if the argument to `include_sense` is #' `TRUE`.} #' @@ -73,44 +73,45 @@ NULL #' `"feature"` and `"zone"` columns, respectively). #' This column is calculated as the sum of the feature data, #' supplied when creating a [problem()] object -#' (e.g. presence/absence values), weighted by the status of each -#' planning unit in the solution (e.g. selected or not for prioritization).} +#' (e.g., presence/absence values), weighted by the status of each +#' planning unit in the solution (e.g., selected or not for +#' prioritization).} #' #' \item{absolute_shortfall}{ `numeric` total amount by which the solution #' fails to meet each target. #' This column is calculated as the difference between the total amount #' held within the solution for the feature and (if relevant) zones -#' associated with the target (i.e. `"absolute_held"` column) and the -#' target total threshold amount (i.e. `"absolute_target"` column), with +#' associated with the target (i.e., `"absolute_held"` column) and the +#' target total threshold amount (i.e., `"absolute_target"` column), with #' values set to zero depending on the sense specified for the target -#' (e.g. if the target sense is `>=` then the difference is +#' (e.g., if the target sense is `>=` then the difference is #' set to zero if the value in the `"absolute_held"` is smaller than #' that in the `"absolute_target"` column).} #' #' \item{relative_target}{`numeric` proportion threshold amount associated #' with each target. #' This column is calculated by dividing the total threshold amount -#' associated with each target (i.e. `"absolute_target"` column) by +#' associated with each target (i.e., `"absolute_target"` column) by #' the total amount associated with each target -#' (i.e. `"total_amount"` column).} +#' (i.e., `"total_amount"` column).} #' #' \item{relative_held}{`numeric` proportion held within the solution for the #' feature and (if relevant) zones associated with each target (per the #' `"feature"` and `"zone"` columns, respectively). #' This column is calculated by dividing the total amount held -#' for each target (i.e. `"absolute_held"` column) by the +#' for each target (i.e., `"absolute_held"` column) by the #' total amount for with each target -#' (i.e. `"total_amount"` column).} +#' (i.e., `"total_amount"` column).} #' #' \item{relative_shortfall}{`numeric` proportion by which the solution fails #' to meet each target. #' This column is calculated by dividing the total shortfall for -#' each target (i.e. `"absolute_shortfall"` column) by the -#' total amount for each target (i.e. `"total_amount"` column).} +#' each target (i.e., `"absolute_shortfall"` column) by the +#' total amount for each target (i.e., `"total_amount"` column).} #' #' \item{met}{`logical` indicating if each target is met by the solution. This #' column is calculated by checking if the total shortfall associated -#' with each target (i.e. `"absolute_shortfall`" column) is equal to +#' with each target (i.e., `"absolute_shortfall`" column) is equal to #' zero.} #' #' } diff --git a/R/feature_abundances.R b/R/feature_abundances.R index e65398119..28c20cf5f 100644 --- a/R/feature_abundances.R +++ b/R/feature_abundances.R @@ -6,17 +6,17 @@ NULL #' Calculate the total abundance of each feature found in the planning units #' of a conservation planning problem. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param na.rm `logical` should planning units with `NA` cost #' data be excluded from the abundance calculations? The default argument #' is `FALSE`. #' #' @details Planning units can have cost data with finite values -#' (e.g. 0.1, 3, 100) and `NA` values. This functionality is provided so +#' (e.g., 0.1, 3, 100) and `NA` values. This functionality is provided so #' that locations which are not available for protected area acquisition can #' be included when calculating targets for conservation features -#' (e.g. when targets are specified using [add_relative_targets()]). +#' (e.g., when targets are specified using [add_relative_targets()]). #' If the total amount of each feature in all the planning units is #' required---including the planning units with `NA` cost data---then the #' the `na.rm` argument should be set to `FALSE`. However, if @@ -129,8 +129,8 @@ NULL #' s5 <- solve(p5) #' #' # plot the solution -#' # this solution contains all the planning units with finite cost data (i.e. -#' # cost data that do not have NA values) +#' # this solution contains all the planning units with finite cost data +#' # (i.e., cost data that do not have NA values) #' plot(s5) #' } #' @export diff --git a/R/feature_names.R b/R/feature_names.R index bbb985f49..afc93e37f 100644 --- a/R/feature_names.R +++ b/R/feature_names.R @@ -5,7 +5,7 @@ NULL #' #' Extract the names of the features in an object. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) or [Zones()] +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) or [Zones()] #' object. #' #' @return `character` feature names. diff --git a/R/importance.R b/R/importance.R index 26f2d10a2..de7d94551 100644 --- a/R/importance.R +++ b/R/importance.R @@ -16,7 +16,7 @@ NULL #' Calculate importance scores using replacement costs (based #' on Cabeza and Moilanen 2006). #' These scores quantify the change in the objective -#' function (e.g. additional costs required to meet feature targets) of the +#' function (e.g., additional costs required to meet feature targets) of the #' optimal solution if a given planning unit in a solution cannot be acquired. #' They can (i) account for the cost of different planning units, (ii) account #' for multiple management zones, (iii) apply to any objective function, and @@ -27,7 +27,7 @@ NULL #' Calculate importance scores following Ferrier *et al.* (2000). #' These scores measure importance based on how critical #' planning units are for meeting targets. They can only be applied to -#' conservation problems with a minimum set objective and a single zone (i.e. +#' conservation problems with a minimum set objective and a single zone (i.e., #' the classic *Marxan*-type problem). Furthermore---unlike the #' replacement cost scores---these scores provide a #' score for each feature within each planning unit, providing insight into @@ -51,8 +51,8 @@ NULL #' zones considered in the problem -- and measure planning unit importance based #' on degradation of the prioritization. #' Although the replacement cost scores can be calculated for small and -#' moderate sized problems (e.g. less than 30,000 planning units), they may not -#' be feasible for particularly large problems (e.g. more than 100,000 planning +#' moderate sized problems (e.g., less than 30,000 planning units), they may not +#' be feasible for particularly large problems (e.g., more than 100,000 planning #' units). In such cases, we recommend calculating importance scores using the #' Ferrier method. This is because the Ferrier method can be #' calculated relatively quickly for large-sized problems and it diff --git a/R/marxan_boundary_data_to_matrix.R b/R/marxan_boundary_data_to_matrix.R index 6e767e1ac..6e931bd7f 100644 --- a/R/marxan_boundary_data_to_matrix.R +++ b/R/marxan_boundary_data_to_matrix.R @@ -11,7 +11,7 @@ NULL #' a single zone, then a matrix object is returned. Otherwise if the boundary #' data contains data for multiple zones, then an array is returned. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object that +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object that #' contains planning unit and zone data to ensure that the argument to #' `data` is converted correctly. This argument can be set to #' `NULL` if checks are not required (not recommended). diff --git a/R/marxan_problem.R b/R/marxan_problem.R index bdc8a960e..4cfba0cb5 100644 --- a/R/marxan_problem.R +++ b/R/marxan_problem.R @@ -6,7 +6,7 @@ NULL #' Create a conservation planning [problem()] following the #' mathematical formulations used in *Marxan* (detailed in Beyer #' *et al.* 2016). Note that these problems are solved using -#' exact algorithms and not simulated annealing (i.e. the *Marxan* software). +#' exact algorithms and not simulated annealing (i.e., the *Marxan* software). #' #' @param x `character` file path for a *Marxan* input file (typically #' called `"input.dat"`), or `data.frame` containing planning unit @@ -108,7 +108,7 @@ NULL #' [official *Marxan* website](https://marxansolutions.org) and Ball #' *et al.* (2009). #' -#' @return [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @return [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @references #' Ball IR, Possingham HP, and Watts M (2009) *Marxan and relatives: diff --git a/R/number_of_features.R b/R/number_of_features.R index 381ab84c6..ebbe8d7c3 100644 --- a/R/number_of_features.R +++ b/R/number_of_features.R @@ -5,7 +5,7 @@ NULL #' #' Extract the number of features in an object. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]), +#' @param x [problem()] (i.e., [`ConservationProblem-class`]), #' [`OptimizationProblem-class`], or [Zones()] object. #' #' @return `integer` number of features. diff --git a/R/number_of_planning_units.R b/R/number_of_planning_units.R index 36c2b9de9..2030e1839 100644 --- a/R/number_of_planning_units.R +++ b/R/number_of_planning_units.R @@ -5,7 +5,7 @@ NULL #' #' Extract the number of planning units in an object. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]), +#' @param x [problem()] (i.e., [`ConservationProblem-class`]), #' [`OptimizationProblem-class`], or [Zones()] object. #' #' @return `integer` number of planning units. diff --git a/R/number_of_total_units.R b/R/number_of_total_units.R index bb31dfc8e..fecf7f48c 100644 --- a/R/number_of_total_units.R +++ b/R/number_of_total_units.R @@ -5,7 +5,7 @@ NULL #' #' Extract the number of total units in an object. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]), +#' @param x [problem()] (i.e., [`ConservationProblem-class`]), #' [`OptimizationProblem-class`], or [Zones()] object. #' #' @return `integer` number of total units. diff --git a/R/number_of_zones.R b/R/number_of_zones.R index 5b80ca1b7..dccab7350 100644 --- a/R/number_of_zones.R +++ b/R/number_of_zones.R @@ -5,7 +5,7 @@ NULL #' #' Extract the number of zones in an object. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]), +#' @param x [problem()] (i.e., [`ConservationProblem-class`]), #' [`OptimizationProblem-class`], or [Zones()] object. #' #' @return `integer` number of zones. diff --git a/R/package.R b/R/package.R index 8870121a6..28d0e592f 100644 --- a/R/package.R +++ b/R/package.R @@ -38,7 +38,7 @@ NULL #' @details This package contains several vignettes that are designed to #' showcase its functionality. To view them, please use the code #' `vignette("name", package = "prioritizr")` where `"name"` is the -#' name of the desired vignette (e.g. `"gurobi_installation"`). +#' name of the desired vignette (e.g., `"gurobi_installation"`). #' #' \describe{ #' diff --git a/R/penalties.R b/R/penalties.R index db277e4bd..f69cc74db 100644 --- a/R/penalties.R +++ b/R/penalties.R @@ -6,7 +6,7 @@ NULL #' A penalty can be applied to a conservation planning [problem()] to #' penalize solutions according to a specific metric. Penalties---unlike #' [constraints]---act as an explicit trade-off with the objective -#' being minimized or maximized (e.g. solution cost when used with +#' being minimized or maximized (e.g., solution cost when used with #' [add_min_set_objective()]). #' #' @details Both penalties and constraints can be used to modify a problem and @@ -31,7 +31,7 @@ NULL #' \item{[add_linear_penalties()]}{Add penalties to a #' conservation problem to favor solutions that avoid selecting #' planning units based on a certain variable -#' (e.g. anthropogenic pressure).} +#' (e.g., anthropogenic pressure).} #' #' } #' diff --git a/R/planning_unit_solution_status.R b/R/planning_unit_solution_status.R index 1112beeb4..7a5b69c4c 100644 --- a/R/planning_unit_solution_status.R +++ b/R/planning_unit_solution_status.R @@ -5,7 +5,7 @@ NULL #' #' Extract planning unit solution status values. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @param solution object with solution data. #' diff --git a/R/portfolios.R b/R/portfolios.R index d60ff242d..96fea9b60 100644 --- a/R/portfolios.R +++ b/R/portfolios.R @@ -6,10 +6,10 @@ NULL #' Conservation planning exercises rarely have access to all the #' data needed to identify the *truly* perfect solution. This is because #' available data may lack important details -#' (e.g. land acquisition costs may be unavailable), contain errors -#' (e.g. species presence/absence data may have false positives), +#' (e.g., land acquisition costs may be unavailable), contain errors +#' (e.g., species presence/absence data may have false positives), #' or key objectives may not be formally incorporated into the -#' prioritization process (e.g. future land use requirements). +#' prioritization process (e.g., future land use requirements). #' As such, conservation planners can help decision makers by providing #' them with a portfolio of solutions to inform their decision. #' diff --git a/R/presolve_check.R b/R/presolve_check.R index 6ab37e02d..ebcd7676f 100644 --- a/R/presolve_check.R +++ b/R/presolve_check.R @@ -12,7 +12,7 @@ NULL #' false positives. Please note that these checks will not be able to #' verify if a problem has a feasible solution or not. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) or +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) or #' [`OptimizationProblem-class`] object. #' #' @details This function checks for issues that are likely to result in @@ -21,7 +21,7 @@ NULL #' planning units have negative cost values (after applying penalties if any #' were specified). Although such conservation planning problems #' are mathematically valid, they are generally the result of a coding mistake -#' when building the problem (e.g. using an absurdly high +#' when building the problem (e.g., using an absurdly high #' penalty value or using the wrong dataset to lock in planning units). #' Thus such issues, if they are indeed issues and not false positives, can #' be fixed by carefully checking the code, data, and parameters used to build @@ -30,15 +30,15 @@ NULL #' This function then checks for values that may lead to numerical instability #' issues when solving the problem. Specifically, it checks if the range of #' values in certain components of the optimization problem are over a -#' certain threshold (i.e. \eqn{1 \times 10 ^9}{1e+9}) or if the values +#' certain threshold (i.e., \eqn{1 \times 10 ^9}{1e+9}) or if the values #' themselves exceed a certain threshold -#' (i.e. \eqn{1 \times 10^{10}}{1e+10}). +#' (i.e., \eqn{1 \times 10^{10}}{1e+10}). #' In most cases, such issues will simply cause an exact #' algorithm solver to take a very long time to generate a solution. In rare #' cases, such issues can cause incorrect calculations which can lead #' to exact algorithm solvers returning infeasible solutions -#' (e.g. a solution to the minimum set problem where not all targets are met) -#' or solutions that exceed the specified optimality gap (e.g. a suboptimal +#' (e.g., a solution to the minimum set problem where not all targets are met) +#' or solutions that exceed the specified optimality gap (e.g., a suboptimal #' solution when a zero optimality gap is specified). #' #' What can you do if a conservation planning problem fails to pass these @@ -56,7 +56,7 @@ NULL #' costs of the planning units in terms of USD then you might have #' some planning units that cost over one billion dollars in large-scale #' planning exercises. This can be fixed by rescaling the values so that they -#' are smaller (e.g. multiplying the values by a number smaller than one, or +#' are smaller (e.g., multiplying the values by a number smaller than one, or #' expressing them as a fraction of the maximum cost). Let's consider another #' common issue, let's pretend that you used habitat suitability models to #' predict the amount of suitable habitat @@ -83,7 +83,7 @@ NULL #' solving the problem, you will need to manually recalculate the cost #' of the solutions but at least now you can be confident that you have the #' optimal solution. Now let's pretend that you are using the maximum features -#' objective (i.e. [add_max_features_objective()]) and assigned some +#' objective (i.e., [add_max_features_objective()]) and assigned some #' really high weights to the targets for some features to ensure that their #' targets were met in the optimal solution. If you set the weights for #' these features to one billion then you will probably run into numerical @@ -91,7 +91,7 @@ NULL #' guarantee that these features will be represented in the optimal solution #' and use this value instead of one billion. This minimum weight value #' can be calculated as the sum of the weight values for the other features -#' and adding a small number to it (e.g. 1). Finally, if you're running out +#' and adding a small number to it (e.g., 1). Finally, if you're running out #' of ideas for addressing numerical stability issues you have one remaining #' option: you can use the `numeric_focus` argument in the #' [add_gurobi_solver()] function to tell the solver to pay extra diff --git a/R/problem.R b/R/problem.R index 8dd0ddc80..ca7e2e129 100644 --- a/R/problem.R +++ b/R/problem.R @@ -6,7 +6,7 @@ NULL #' Create a systematic conservation planning problem. This function is used to #' specify the basic data used in a spatial prioritization problem: the #' spatial distribution of the planning units and their costs, as well as -#' the features (e.g. species, ecosystems) that need to be conserved. After +#' the features (e.g., species, ecosystems) that need to be conserved. After #' constructing this `ConservationProblem-class` object, it can be #' customized to meet specific goals using [objectives], #' [targets], [constraints], and @@ -29,7 +29,7 @@ NULL #' cells to `NA`, or use the `add_locked_out_constraint` function. #' #' @param features The feature data can be specified in a variety of ways. -#' The specific formats that can be used depend on the cost data format (i.e. +#' The specific formats that can be used depend on the cost data format (i.e., #' argument to `x`) and whether the problem should have a single zone or #' multiple zones. If the problem should have a single zone, then the feature #' data can be specified following: @@ -38,7 +38,7 @@ NULL #' [`x = sf::st_sf()`][sf::st_sf()]: #' [`y = Raster-class`][raster::Raster-class] #' object showing the distribution of conservation features. Missing -#' values (i.e. `NA` values) can be used to indicate the absence of +#' values (i.e., `NA` values) can be used to indicate the absence of #' a feature in a particular cell instead of explicitly setting these #' cells to zero. Note that this argument type for `features` can #' only be used to specify data for problems involving a single zone. @@ -77,7 +77,7 @@ NULL #' [`x = sf::st_sf()`][sf::st_sf()]: #' [`y = ZonesRaster`][zones()]: #' object showing the distribution of conservation features in multiple -#' zones. As above, missing values (i.e. `NA` values) can be used to +#' zones. As above, missing values (i.e., `NA` values) can be used to #' indicate the absence of a feature in a particular cell instead of #' explicitly setting these cells to zero. #' * [`x = Spatial-class`][sp::Spatial-class], or @@ -159,34 +159,34 @@ NULL #' on the management action(s), you can compile the following data. #' #' First, you will need to create a set of planning units -#' (i.e. discrete spatial areas) to inform decision making. +#' (i.e., discrete spatial areas) to inform decision making. #' Planning units are often created by subdividing a study region #' into a set square or hexagonal cells. They can also be created using -#' administrative boundaries (e.g. provinces), land management boundaries -#' (e.g. property boundaries derived from cadastral data), or -#' ecological boundaries (e.g. based on ecosystem classification data). -#' The size (i.e. spatial grain) of the planning units is often determined +#' administrative boundaries (e.g., provinces), land management boundaries +#' (e.g., property boundaries derived from cadastral data), or +#' ecological boundaries (e.g., based on ecosystem classification data). +#' The size (i.e., spatial grain) of the planning units is often determined #' based on a compromise between the scale needed to inform decision making, the #' spatial accuracy (resolution) of available datasets, and #' the computational resources available for generating prioritizations -#' (e.g. RAM and number of CPUs on your computer). +#' (e.g., RAM and number of CPUs on your computer). #' #' Second, you will need data to quantify the cost of implementing #' implementing each management action within each planning unit. #' Critically, the cost data should reflect the management action(s) #' considered in the exercise. #' For example, costs are often specified using data that reflect economic -#' expenditure (e.g. land acquisition cost), -#' socioeconomic conditions (e.g. human population density), +#' expenditure (e.g., land acquisition cost), +#' socioeconomic conditions (e.g., human population density), #' opportunity costs of foregone commercial activities -#' (e.g. logging or agriculture), or +#' (e.g., logging or agriculture), or #' opportunity costs of foregone recreational activities -#' (e.g. recreational fishing) activities, +#' (e.g., recreational fishing) activities, #' In some cases -- depending on the management action(s) considered -- #' it can make sense to use a constant cost value -#' (e.g. all planning units are assigned a cost value equal to one) +#' (e.g., all planning units are assigned a cost value equal to one) #' or use a cost value based on spatial extent -#' (e.g. each planning unit is assigned a cost value based on its total area). +#' (e.g., each planning unit is assigned a cost value based on its total area). #' Also, in most cases, you want to avoid negative cost values. #' This because a negative value means that a place is *desirable* #' for implementing a management action, and such places will almost @@ -197,13 +197,13 @@ NULL #' To achieve this, you will need to select a set of conservation features #' that relate to the over-arching goals of the exercise. #' For example, conservation features often include -#' species (e.g. Clouded Leopard), habitats (e.g. mangroves or +#' species (e.g., Clouded Leopard), habitats (e.g., mangroves or #' cloud forest), or ecosystems. #' The benefit that each feature derives from a planning unit -#' can take a variety of forms, but is typically occupancy (i.e. +#' can take a variety of forms, but is typically occupancy (i.e., #' presence or absence), area of occurrence within each planning unit -#' (e.g. based on species' geographic range data), or -#' a measure of habitat suitability (e.g. estimated using a statistical model). +#' (e.g., based on species' geographic range data), or +#' a measure of habitat suitability (e.g., estimated using a statistical model). #' After compiling these data, you have the minimal data need to generate #' a prioritization. #' @@ -256,7 +256,7 @@ NULL #' formulated as mixed integer linear programming problem, you can use #' the [write_problem()] function to save the optimization problem #' to a plain-text file on your computer and then view it using a standard -#' text editor (e.g. Notepad). +#' text editor (e.g., Notepad). #' #' Please note that this function internally computes the amount of each #' feature in each planning unit when this data is not supplied (using the @@ -322,13 +322,13 @@ NULL #' add_default_solver(verbose = FALSE) #' #' # since geo-processing can be slow for large spatial vector datasets -#' # (e.g. polygons, lines, points), it can be worthwhile to pre-process the +#' # (e.g., polygons, lines, points), it can be worthwhile to pre-process the #' # planning unit data so that it contains columns indicating the amount of #' # each feature inside each planning unit -#' # (i.e. each column corresponds to a different feature) +#' # (i.e., each column corresponds to a different feature) #' #' # calculate the amount of each species within each planning unit -#' # (i.e. SpatialPolygonsDataFrame object) +#' # (i.e., SpatialPolygonsDataFrame object) #' pre_proc_data <- rij_matrix(sim_pu_polygons, sim_features) #' #' # add extra columns to the polygon (Spatial) planning unit data @@ -361,7 +361,7 @@ NULL #' #' # in addition to spatially explicit data, pre-processed aspatial data #' # can also be used to create a problem -#' # (e.g. data created using external spreadsheet software) +#' # (e.g., data created using external spreadsheet software) #' costs <- sim_pu_polygons$cost #' features <- data.frame(id = seq_len(nlayers(sim_features)), #' name = names(sim_features)) @@ -1062,7 +1062,7 @@ methods::setMethod( assertthat::assert_that( all(!geometry_classes(x) %in% c("GEOMETRYCOLLECTION", "MULTIPOINT")), msg = paste("argument to x contains invalid geometry types", - "(i.e. GEOMETRYCOLLECTION or MULTIPOINT)")) + "(i.e., GEOMETRYCOLLECTION or MULTIPOINT)")) x2 <- sf::st_drop_geometry(x) assertthat::assert_that( all(unlist(as.list(features), recursive = TRUE, use.names = FALSE) %in% diff --git a/R/rij_matrix.R b/R/rij_matrix.R index e30d6e7ec..87e57dfc1 100644 --- a/R/rij_matrix.R +++ b/R/rij_matrix.R @@ -21,7 +21,7 @@ NULL #' @param ... not used. #' #' @details -#' Generally, processing vector (i.e. [`Spatial-class`] or +#' Generally, processing vector (i.e., [`Spatial-class`] or #' [sf::sf()]) data takes much #' longer to process then [`Raster-class`] data, #' so it is recommended to use [`Raster-class`] data diff --git a/R/run_calculations.R b/R/run_calculations.R index 89748dac2..99061f87e 100644 --- a/R/run_calculations.R +++ b/R/run_calculations.R @@ -6,10 +6,10 @@ NULL #' Execute preliminary calculations in a conservation problem and store the #' results for later use. This function is useful when creating slightly #' different versions of the same conservation planning problem that involve -#' the same pre-processing steps (e.g. calculating boundary data), because +#' the same pre-processing steps (e.g., calculating boundary data), because #' means that the same calculations will not be run multiple times. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) object. +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) object. #' #' @details This function is used for the effect of modifying the input #' [`ConservationProblem-class`] object. As such, it does not return diff --git a/R/solve.R b/R/solve.R index 793ed079c..f3311eb62 100644 --- a/R/solve.R +++ b/R/solve.R @@ -5,7 +5,7 @@ NULL #' #' Solve a conservation planning [problem()]. #' -#' @param a [problem()] (i.e. [`ConservationProblem-class`]) or +#' @param a [problem()] (i.e., [`ConservationProblem-class`]) or #' [`OptimizationProblem-class`] object. #' #' @param b [`Solver-class`] object. Not used if `a` is an @@ -29,15 +29,15 @@ NULL #' then the best available exact algorithm solver will be used by default #' (see [add_default_solver()]. Although these exact algorithm #' solvers will often display a lot of information that isn't really that -#' helpful (e.g. nodes, cutting planes), they do display information -#' about the progress they are making on solving the problem (e.g. the +#' helpful (e.g., nodes, cutting planes), they do display information +#' about the progress they are making on solving the problem (e.g., the #' performance of the best solution found at a given point in time). If #' potential issues were detected during the #' presolve checks (see [presolve_check()]) -#' and the problem is being forcibly solved (i.e. with `force = TRUE`), +#' and the problem is being forcibly solved (i.e., with `force = TRUE`), #' then it is also worth checking for any warnings displayed by the solver #' to see if these potential issues are actually causing issues -#' (e.g. *Gurobi* can display warnings that include +#' (e.g., *Gurobi* can display warnings that include #' `"Warning: Model contains large matrix coefficient range"` and #' `"Warning: Model contains large rhs"`). #' @@ -85,11 +85,11 @@ NULL #' will contain fields (columns) that solution the values. #' Specifically, the field name(s) containing the solution values #' be will named as `"solution_XXX"` where `"XXX"` corresponds to a solution -#' identifier (e.g. `"solution_1"`). +#' identifier (e.g., `"solution_1"`). #' If the argument to `a` contains multiple zones, then the fields #' containing solutions will be named as `"solution_XXX_YYY"` where #' `"XXX"` corresponds to the solution identifier and `"YYY"` is the name -#' of the management zone (e.g. `"solution_1_zone1"`).} +#' of the management zone (e.g., `"solution_1_zone1"`).} #' #' } #' @@ -100,8 +100,8 @@ NULL #' attributes: `"objective"` containing the solution's objective, #' `"runtime"` denoting the number of seconds that elapsed while solving #' the problem, and `"status"` describing the status of the solution -#' (e.g. `"OPTIMAL"` indicates that the optimal solution was found). -#' In most cases, the first solution (e.g. `"solution_1"`) +#' (e.g., `"OPTIMAL"` indicates that the optimal solution was found). +#' In most cases, the first solution (e.g., `"solution_1"`) #' will contain the best solution found by the solver (note that this #' may not be an optimal solution depending on the gap used to solve #' the problem and noting that the default gap is 0.1). @@ -289,7 +289,7 @@ methods::setMethod( # check that solution is valid #nocov start if (is.null(sol) || is.null(sol[[1]]$x)) { - stop("no solution found (e.g. due to problem infeasibility or time ", + stop("no solution found (e.g., due to problem infeasibility or time ", "limits)") } #nocov end diff --git a/R/zone_names.R b/R/zone_names.R index 06451edca..905a7af6e 100644 --- a/R/zone_names.R +++ b/R/zone_names.R @@ -5,7 +5,7 @@ NULL #' #' Extract the names of zones in an object. #' -#' @param x [problem()] (i.e. [`ConservationProblem-class`]) or [Zones()] +#' @param x [problem()] (i.e., [`ConservationProblem-class`]) or [Zones()] # object. #' #' @return `character` zone names. diff --git a/R/zones.R b/R/zones.R index 0ae672db1..de9c8bde8 100644 --- a/R/zones.R +++ b/R/zones.R @@ -17,7 +17,7 @@ NULL #' Specifically, the data should describe the expected amount of each #' feature within each planning unit given each management zone. #' For example, the data could describe the occupancy -#' (e.g. presence/absence), probability of occurrence, or +#' (e.g., presence/absence), probability of occurrence, or #' abundance expected for each feature when each planning unit #' is allocated to a different zone. #' diff --git a/README.Rmd b/README.Rmd index 44b96120d..90aded7f8 100644 --- a/README.Rmd +++ b/README.Rmd @@ -6,7 +6,7 @@ output: -# prioritizr: +# prioritizr: # Systematic Conservation Prioritization in R @@ -242,8 +242,23 @@ spplot(rc, "rc", main = "Irreplaceability", xlim = c(-0.1, 1.1), "#FF0000")) ``` -This short example demonstrates how the _prioritizr R_ package can be used to build a minimal conservation problem, how constraints and penalties can be iteratively added to the problem to obtain a solution, and how importance scores can be calculated for the solution to identify critical places. Although we explored just a few different functions for modifying the a conservation problem, the _prioritizr R_ package provides many functions for specifying objectives, constraints, penalties, and decision variables, so that you can build and custom-tailor a conservation planning problem to suit your exact planning scenario. +This short example demonstrates how the _prioritizr R_ package can be used to build and customize conservation problems, and then solve them to generate solutions. Although we explored just a few different functions for modifying a conservation problem, the package provides many functions for specifying objectives, constraints, penalties, and decision variables, so that you can build and custom-tailor conservation planning problems to suit your planning scenario. + +## Learning resources + +The [package website](https://prioritizr.net/index.html) contains information on the _prioritizr R_ package. Here you can find [documentation for every function and built-in dataset](https://prioritizr.net/reference/index.html), and [news describing the updates in each package version](https://prioritizr.net/news/index.html). It also contains the following articles and tutorials. + +* [**Getting started**](https://prioritizr.net/articles/prioritizr.html): Short tutorial on using the package. +* [**Package overview**](https://prioritizr.net/articles/package_overview.html): Introduction to systematic conservation planning and a comprehensive overview of the package. +* [**Connectivity tutorial**](https://prioritizr.net/articles/connectivity_tutorial.html): Tutorial on incorporating connectivity into prioritizations. +* [**Calibrating trade-offs tutorial**](https://prioritizr.net/articles/calibrating_trade-offs_tutorial.html): Tutorial on running calibration analyses to satisfy multiple criteria. +* [**Management zones tutorial**](https://prioritizr.net/articles/management_zones_tutorial.html): Tutorial on incorporating multiple management zones and actions into prioritizations. +* [**Gurobi installation guide**](https://prioritizr.net/articles/gurobi_installation_guide.html): Instructions for installing the _Gurobi_ optimization suite for generating prioritizations. +* [**Solver benchmarks**](https://prioritizr.net/articles/solver_benchmarks.html): Performance comparison of optimization solvers for generating prioritizations. +* [**Publication record**](https://prioritizr.net/articles/publication_record.html): List of publications that have cited the package. + +Additional resources can also be found in [online repositories under the _prioritizr_ organization](https://github.com/prioritizr). These resources include [slides for talks and seminars about the package](https://github.com/prioritizr/teaching). Additionally, workshop materials are available too (e.g., the [Massey University 2021 workshop](https://prioritizr.github.io/massey-workshop/) and the [PacMara 2019 workshop](https://prioritizr.github.io/PacMara_workshop/)). ## Getting help -Please refer to the [package website](https://prioritizr.net/index.html) for more information on the _prioritizr R_ package. This website contains [a comprehensive tutorial on the package](https://prioritizr.net/articles/prioritizr.html), [instructions for installing the _Gurobi_ software suite to solve large-scale and complex conservation planning problems](https://prioritizr.net/articles/gurobi_installation.html), and [a tutorial on solving problems with multiple management zones](https://prioritizr.net/articles/zones.html). It also provides two worked examples that involve real-world data from [Tasmania, Australia](https://prioritizr.net/articles/tasmania.html) and [Salt Spring Island, Canada](https://prioritizr.net/articles/saltspring.html). Additionally, slides for previous seminars about the package can be found in [teaching repository](https://github.com/prioritizr/teaching). Furthermore, materials accompanying previous workshops are also available online too (i.e. the [CIBIO 2019 workshop](https://prioritizr.github.io/cibio-workshop/) and the [PacMara 2019 workshop](https://prioritizr.github.io/PacMara_workshop/)). If you have any questions about using the _prioritizr R_ package or suggestions for improving it, please [file an issue at the package's online code repository](https://github.com/prioritizr/prioritizr/issues/new). +If you have any questions about the _prioritizr R_ package or suggestions for improving it, please [post an issue on the code repository](https://github.com/prioritizr/prioritizr/issues/new). diff --git a/README.md b/README.md index d14480584..b40b819cc 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# prioritizr: +# prioritizr: # Systematic Conservation Prioritization in R @@ -69,7 +69,7 @@ Alternatively, to cite the latest development version, please use: > Hanson JO, Schuster R, Morrell N, Strimas-Mackey M, Edwards BPM, Watts > ME, Arcese P, Bennett J, Possingham HP (2021). prioritizr: Systematic -> Conservation Prioritization in R. R package version 7.1.1.4. Available +> Conservation Prioritization in R. R package version 7.1.1.5. Available > at . Additionally, we keep a [record of @@ -249,7 +249,7 @@ s1 <- solve(p1) ## StrongCG: 2 ## Flow cover: 1 ## - ## Explored 145469 nodes (243327 simplex iterations) in 3.74 seconds (2.59 work units) + ## Explored 145469 nodes (243327 simplex iterations) in 3.83 seconds (2.59 work units) ## Thread count was 1 (of 8 available processors) ## ## Solution count 6: 2627.64 2747.38 2761.33 ... 3139.89 @@ -271,7 +271,7 @@ print(attr(s1, "runtime")) ``` ## solution_1 - ## 3.762 + ## 3.836 ``` r # extract state message from the solver @@ -419,7 +419,7 @@ s2 <- solve(p2) ## StrongCG: 1 ## Flow cover: 1 ## - ## Explored 10262 nodes (23307 simplex iterations) in 0.53 seconds (0.54 work units) + ## Explored 10262 nodes (23307 simplex iterations) in 0.62 seconds (0.54 work units) ## Thread count was 1 (of 8 available processors) ## ## Solution count 5: 2838.26 2839.12 3021.28 ... 3027.7 @@ -616,36 +616,64 @@ spplot(rc, "rc", main = "Irreplaceability", xlim = c(-0.1, 1.1), This short example demonstrates how the *prioritizr R* package can be -used to build a minimal conservation problem, how constraints and -penalties can be iteratively added to the problem to obtain a solution, -and how importance scores can be calculated for the solution to identify -critical places. Although we explored just a few different functions for -modifying the a conservation problem, the *prioritizr R* package -provides many functions for specifying objectives, constraints, -penalties, and decision variables, so that you can build and -custom-tailor a conservation planning problem to suit your exact -planning scenario. +used to build and customize conservation problems, and then solve them +to generate solutions. Although we explored just a few different +functions for modifying a conservation problem, the package provides +many functions for specifying objectives, constraints, penalties, and +decision variables, so that you can build and custom-tailor conservation +planning problems to suit your planning scenario. + +## Learning resources + +The [package website](https://prioritizr.net/index.html) contains +information on the *prioritizr R* package. Here you can find +[documentation for every function and built-in +dataset](https://prioritizr.net/reference/index.html), and [news +describing the updates in each package +version](https://prioritizr.net/news/index.html). It also contains the +following articles and tutorials. + +- [**Getting + started**](https://prioritizr.net/articles/prioritizr.html): Short + tutorial on using the package. +- [**Package + overview**](https://prioritizr.net/articles/package_overview.html): + Introduction to systematic conservation planning and a comprehensive + overview of the package. +- [**Connectivity + tutorial**](https://prioritizr.net/articles/connectivity_tutorial.html): + Tutorial on incorporating connectivity into prioritizations. +- [**Calibrating trade-offs + tutorial**](https://prioritizr.net/articles/calibrating_trade-offs_tutorial.html): + Tutorial on running calibration analyses to satisfy multiple + criteria. +- [**Management zones + tutorial**](https://prioritizr.net/articles/management_zones_tutorial.html): + Tutorial on incorporating multiple management zones and actions into + prioritizations. +- [**Gurobi installation + guide**](https://prioritizr.net/articles/gurobi_installation_guide.html): + Instructions for installing the *Gurobi* optimization suite for + generating prioritizations. +- [**Solver + benchmarks**](https://prioritizr.net/articles/solver_benchmarks.html): + Performance comparison of optimization solvers for generating + prioritizations. +- [**Publication + record**](https://prioritizr.net/articles/publication_record.html): + List of publications that have cited the package. + +Additional resources can also be found in [online repositories under the +*prioritizr* organization](https://github.com/prioritizr). These +resources include [slides for talks and seminars about the +package](https://github.com/prioritizr/teaching). Additionally, workshop +materials are available too (e.g., the [Massey University 2021 +workshop](https://prioritizr.github.io/massey-workshop/) and the +[PacMara 2019 +workshop](https://prioritizr.github.io/PacMara_workshop/)). ## Getting help -Please refer to the [package website](https://prioritizr.net/index.html) -for more information on the *prioritizr R* package. This website -contains [a comprehensive tutorial on the -package](https://prioritizr.net/articles/prioritizr.html), [instructions -for installing the *Gurobi* software suite to solve large-scale and -complex conservation planning -problems](https://prioritizr.net/articles/gurobi_installation.html), and -[a tutorial on solving problems with multiple management -zones](https://prioritizr.net/articles/zones.html). It also provides two -worked examples that involve real-world data from [Tasmania, -Australia](https://prioritizr.net/articles/tasmania.html) and [Salt -Spring Island, Canada](https://prioritizr.net/articles/saltspring.html). -Additionally, slides for previous seminars about the package can be -found in [teaching repository](https://github.com/prioritizr/teaching). -Furthermore, materials accompanying previous workshops are also -available online too (i.e. the [CIBIO 2019 -workshop](https://prioritizr.github.io/cibio-workshop/) and the [PacMara -2019 workshop](https://prioritizr.github.io/PacMara_workshop/)). If you -have any questions about using the *prioritizr R* package or suggestions -for improving it, please [file an issue at the package’s online code +If you have any questions about the *prioritizr R* package or +suggestions for improving it, please [post an issue on the code repository](https://github.com/prioritizr/prioritizr/issues/new). diff --git a/_pkgdown.yml b/_pkgdown.yml index 8d59b719d..869805be1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,10 +1,5 @@ url: https://prioritizr.net -home: - links: - - text: Learn about contributing - href: http://prioritizr.net/CONTRIBUTING.html - authors: Jeffrey O Hanson: href: https://jeffrey-hanson.com @@ -26,6 +21,40 @@ template: docsearch: api_key: 486efa122ea6783724263412c5f28ab3 index_name: prioritizr + +home: + sidebar: + structure: [links, citation, license, authors, dev] + links: + - text: Learn about contributing + href: http://prioritizr.net/CONTRIBUTING.html + +navbar: + structure: + left: [home, intro, reference, articles, tutorial, news] + right: [search, github] + components: + articles: + text: Articles + menu: + - text: Package overview + href: articles/package_overview.html + - text: Gurobi installation guide + href: articles/gurobi_installation_guide.html + - text: Solver benchmarks + href: articles/solver_benchmarks.html + - text: Publication record + href: articles/publication_record.html + tutorial: + text: Tutorials + menu: + - text: Connectivity + href: articles/connectivity_tutorial.html + - text: Calibrating trade-offs + href: articles/calibrating_trade-offs_tutorial.html + - text: Management zones + href: articles/management_zones_tutorial.html + reference: - title: Overview desc: Overview of the package diff --git a/docs/404.html b/docs/404.html index de82f7dd1..293d1b862 100644 --- a/docs/404.html +++ b/docs/404.html @@ -25,7 +25,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Contributing to development of the prioritizr R package • prioritizr - - - - - - - - - - - - - - +
-
-
-
- - - - - - - - - - + diff --git a/docs/reference/add_cbc_solver.html b/docs/reference/add_cbc_solver.html index 00b97e4a8..6abea23eb 100644 --- a/docs/reference/add_cbc_solver.html +++ b/docs/reference/add_cbc_solver.html @@ -1,108 +1,25 @@ - - - - - - - -Add a CBC solver — add_cbc_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a CBC solver — add_cbc_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
-
-
-

Specify that the CBC +

Specify that the CBC (COIN-OR branch and cut) software (Forrest & Lougee-Heimer 2005) should be used to solve a conservation planning -problem(). +problem(). This function can also be used to customize the behavior of the solver. It requires the rcbc package to be installed -(only available on GitHub, +(only available on GitHub, see below for installation instructions).

-
add_cbc_solver(
-  x,
-  gap = 0.1,
-  time_limit = .Machine$integer.max,
-  presolve = TRUE,
-  threads = 1,
-  first_feasible = FALSE,
-  start_solution = NULL,
-  verbose = TRUE
-)
- -

Arguments

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
x

problem() (i.e. ConservationProblem) object.

gap

numeric gap to optimality. This gap is relative +

Usage,
add_cbc_solver(
+  x,
+  gap = 0.1,
+  time_limit = .Machine$integer.max,
+  presolve = TRUE,
+  threads = 1,
+  first_feasible = FALSE,
+  start_solution = NULL,
+  verbose = TRUE
+)
+ +
+

Arguments

+
x
+

problem() (i.e., ConservationProblem) object.

+
gap
+

numeric gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10% from optimality).

time_limit

numeric time limit (seconds) for generating solutions. +The default value is 0.1 (i.e., 10% from optimality).

+
time_limit
+

numeric time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. .Machine$integer.max), effectively meaning that solver -will keep running until a solution within the optimality gap is found.

presolve

logical attempt to simplify the -problem before solving it? Defaults to TRUE.

threads

integer number of threads to use for the -optimization algorithm. The default value is 1.

first_feasible

logical should the first feasible solution be +(i.e., .Machine$integer.max), effectively meaning that solver +will keep running until a solution within the optimality gap is found.

+
presolve
+

logical attempt to simplify the +problem before solving it? Defaults to TRUE.

+
threads
+

integer number of threads to use for the +optimization algorithm. The default value is 1.

+
first_feasible
+

logical should the first feasible solution be be returned? If first_feasible is set to TRUE, the solver will return the first solution it encounters that meets all the constraints, regardless of solution quality. Note that the first feasible solution is not an arbitrary solution, rather it is derived from the relaxed solution, and is therefore often reasonably close to optimality. -Defaults to FALSE.

start_solution

NULL or object containing the starting solution +Defaults to FALSE.

+
start_solution
+

NULL or object containing the starting solution for the solver. Defaults to NULL such that no starting solution is used. To specify a starting solution, the argument to start_solution should -be in the same format as the planning units (i.e. a NULL, numeric, -matrix, data.frame, Raster, Spatial, -or sf::sf() object). -See the Start solution format section for more information.

verbose

logical should information be printed while solving -optimization problems? Defaults to TRUE.

- -

Value

- -

Object (i.e. ConservationProblem) with the solver +be in the same format as the planning units (i.e., a NULL, numeric, +matrix, data.frame, Raster, Spatial, +or sf::sf() object). +See the Start solution format section for more information.

+
verbose
+

logical should information be printed while solving +optimization problems? Defaults to TRUE.

+
+
+

Value

+

Object (i.e., ConservationProblem) with the solver added to it.

-

Details

- -

CBC is an +

+
+

Details

+

CBC is an open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks examining the performance of this solver for conservation planning problems have yet to be completed, preliminary analyses suggest that it performs much faster than the other open-source -solvers (i.e. add_rsymphony_solver(), add_rsymphony_solver()), and +solvers (i.e., add_rsymphony_solver(), add_rsymphony_solver()), and so we recommend using this solver if the Gurobi and IBM CPLEX solvers are unavailable.

-

Installation

- +
+
+

Installation

The rcbc package is required to use this solver. Since the rcbc package is not available on the the Comprehensive R Archive Network (CRAN), it must be installed from -its GitHub repository. To -install the rcbc package, please use the following code:

if (!require(remotes)) install.packages("remotes")
-remotes::install_github("dirkschumacher/rcbc")
-
+its GitHub repository. To +install the rcbc package, please use the following code:

if (!require(remotes)) install.packages("remotes")
+remotes::install_github("dirkschumacher/rcbc")

Note that you may also need to install several dependencies -- such as the -Rtools software +Rtools software or system libraries -- prior to installing the rcbc package. For further details on installing this package, please consult -official installation instructions for the package.

-

Start solution format

- +official installation instructions for the package.

+
+
+

Start solution format

Broadly speaking, the argument to start_solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

-
- -
x has numeric planning units

The argument to start_solution must be a +

x has numeric planning units
+

The argument to start_solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to start_solution.

-
x has matrix planning units

The argument to start_solution must be a + +

x has matrix planning units
+

The argument to start_solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -329,8 +231,10 @@

Raster planning units +

The argument to start_solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -338,35 +242,41 @@

sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to start_solution.

-
x has Spatial planning units

The argument to start_solution -must be a Spatial object with each column corresponding to a + +

x has Spatial planning units
+

The argument to start_solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to start_solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to start_solution.

-
x has sf::sf() planning units

The argument to start_solution must be -a sf::sf() object with each column corresponding to a different + +

x has sf::sf() planning units
+

The argument to start_solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to start_solution must also have the same @@ -375,85 +285,84 @@

References

+

+
+

References

Forrest J and Lougee-Heimer R (2005) CBC User Guide. In Emerging theory, Methods, and Applications (pp. 257--277). INFORMS, Catonsville, MD. -doi: 10.1287/educ.1053.0020 +doi: 10.1287/educ.1053.0020 .

-

See also

- -

See solvers for an overview of all functions for adding a solver.

+
+
+

See also

+

See solvers for an overview of all functions for adding a solver.

Other solvers: -add_cplex_solver(), -add_default_solver(), -add_gurobi_solver(), -add_lsymphony_solver, -add_rsymphony_solver()

+add_cplex_solver(), +add_default_solver(), +add_gurobi_solver(), +add_lsymphony_solver, +add_rsymphony_solver()

+
-

Examples

-
# \dontrun{
-# load data
-data(sim_pu_raster, sim_features)
-
-# create problem
-p <- problem(sim_pu_raster, sim_features) %>%
-     add_min_set_objective() %>%
-     add_relative_targets(0.1) %>%
-     add_binary_decisions() %>%
-     add_cbc_solver(gap = 0, verbose = FALSE)
-
-# generate solution %>%
-s <- solve(p)
-
-# plot solution
-plot(s, main = "solution", axes = FALSE, box = FALSE)
-
-
-# create a similar problem with boundary length penalties and
-# specify the solution from the previous run as a starting solution
-p2 <- problem(sim_pu_raster, sim_features) %>%
-     add_min_set_objective() %>%
-     add_relative_targets(0.1) %>%
-     add_boundary_penalties(10) %>%
-     add_binary_decisions() %>%
-     add_cbc_solver(gap = 0, start_solution = s, verbose = FALSE)
-
-# generate solution
-s2 <- solve(p2)
-
-# plot solution
-plot(s2, main = "solution with boundary penalties", axes = FALSE,
-     box = FALSE)
-
-# }
+    
+

Examples

+
# \dontrun{
+# load data
+data(sim_pu_raster, sim_features)
+
+# create problem
+p <- problem(sim_pu_raster, sim_features) %>%
+     add_min_set_objective() %>%
+     add_relative_targets(0.1) %>%
+     add_binary_decisions() %>%
+     add_cbc_solver(gap = 0, verbose = FALSE)
+
+# generate solution %>%
+s <- solve(p)
+
+# plot solution
+plot(s, main = "solution", axes = FALSE, box = FALSE)
+
+
+# create a similar problem with boundary length penalties and
+# specify the solution from the previous run as a starting solution
+p2 <- problem(sim_pu_raster, sim_features) %>%
+     add_min_set_objective() %>%
+     add_relative_targets(0.1) %>%
+     add_boundary_penalties(10) %>%
+     add_binary_decisions() %>%
+     add_cbc_solver(gap = 0, start_solution = s, verbose = FALSE)
+
+# generate solution
+s2 <- solve(p2)
+
+# plot solution
+plot(s2, main = "solution with boundary penalties", axes = FALSE,
+     box = FALSE)
+
+# }
 
+
+
-
- - - - - - - - - - + diff --git a/docs/reference/add_connectivity_penalties-1.png b/docs/reference/add_connectivity_penalties-1.png index ad439174a236105362f1d5ae95dd3d9ce29da854..fa793eaca67dd5edeffa4c559bee979325dfec6e 100644 GIT binary patch literal 48210 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 z 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 be+-8897R0$Q{pedgSSKUP4y~to#Orn*;HI_ diff --git a/docs/reference/add_connectivity_penalties-2.png b/docs/reference/add_connectivity_penalties-2.png index 32c1120e6dcd01f3042ac4de01ad094bcb8798fd..b8f617d9041ba6fc57a23cfd9dbb5268b0e149db 100644 GIT binary patch literal 70437 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%0DYiID*S+r4t_(Fd{| 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 P4HLjw$1~NZ{1X2Q1mhJV literal 69188 zcmeFZc|4Wx*EYO2A#=vcTu3sODYHVzSSl%*hh)q=4=Iw&LYa%C%(JlVLS~A}JkNz~ zo`-i`>igcm`+o1=dq2-V&tK2}D7Io>*LkjUtz#X>v39VQh7vgm0|^R+B3DsXxQ;>* zxS>$^#zzU^JN>a~i|~oiSXD^@KEq>6zS0*y5!);4I-*d_LC7C;_~eoq3dM#}QMh!& zEpcwp-7WEl`{F^~di|X$V%%rOFNS0BCro}GixuX0d^y$s%&B9_MPDOg(%bqmDn7^J zCLX*hBeooliY<#dg4e4|LVH0i@Q`N!}U4}MXxbB?Nt4K3T z`p~^Sy>7?ZZ{8g*D>_!lqQoT}DU_o)RQ~VB|6PIqvn!C%JMHI7uv~cW>6Pbsr~|Hk z#rq0dcSmqV_a0w~vBMv3bfG%DAYxH`qg2uUC->`m)F-6RAAWV8YdQIBlglXvDw5zT zRtnR~V=7UBJhJ@+5Xu>HJ}G%oIN~)rPl1bF20_afk2$!fw_4Qx-kw7Ye%0SEW8N^0 zy!aFQ=}eQTyuZg_@+^@X=bsb7tt5`+`Fl9To6-7zzihrnv3T{*FS#D(;HUGy{;S`8 zwm#0L(kZxia)tkdDqjNJ-n*whd#U;SwM+skShudLZGjC5+dHC?* z?oMnh{jcA@vvPB%=88Hfq+A9+I$QS^5}Yn__ep7z2+BITsiLpX!jXvlrxR(EvkhvJU5DI>My9bQy6@E4 z<;k^3tXp6&&YwXM$lWU?{r2?y$r%C{^U4S+Q_;nF>*J?XBrYK973M)o&zt)G{ki?U zFQJTCd3n?2<6+}p812ZU)^jTto{=wN)NH*?iyqIQa3?Jisg$c0Ka&<07Yo|-3G-L4 zcFN@z6r2~u?g%fMzI~?d{{(9Tm%Fzx>bpPf`-RZW;8fW{8iz`6=?U0A?&W$9+{)FPT@`vN)tDUO&1qb`M1M`k#X|MI7l*z&t ze$t8DiYbOWckgQJI6YuSojrSYq7jhfO5H+Cr{9G-MSqXSZ?8X%_hOUd-uGi zgi?%tX6r7wd3s77EK(js?7t6Mpo=<4+fMPz8?>(_*Ynw$^sg)F)>n0Obo^))4Y!;A z{2@f6Q)_ce3!`ASnC#baB|rN)(|69{i~DPIGQ0ADK77ckaO;#S1pIf)Qvu(eefPDm zuX%a01;4=FZA5bkH(XO=kg&J2fe-s3pk-jd+E`LjQm>Im_=;L;l7GaP(7^fElUk!& zdHT#x=le=X{Q?66efH)T76=WumM0-#WLR*&emzUNg-TIdcCf<}pse||+EZ*vv_P1A)z0kiX)=yIrW@|e!PLuig7;)(_2o6%{WpRe`SRST zuh`PRuCK&uc7EP$tS&evF0Nsmh=R#*XMLWQmR8Vis7fvYx!IxwrFb0`A3hnvnB<3F zPWo@u9jypL?!&1LNiocHZ`BjNbl2>!$-x0MTU}kX-=59XzIl@_+-{&eayrG=2Rof0 zVs)#=zYWe-kp0EhILs>N`wt(+m%8;mZ8*p%T%x_WpZxZ(v}le0M5m(%hhuNO-^s~I z(0AYKjAk-Zcv*3AzW0vH)vH&_f4%>JXA{eJSG1GJ4Hv=^{2iahu4?JZmoR2V%3R$d zbB+OdB#}uN+ZLYrACg;hiwio6gS?A+B(}Nv>Qs9YqjI)h1+`yP6!r2l#eVbe+1a$r zObaX*A1`luzF_$cZwm`9)XkeWC#G3<87*~mjx#eeqnNQ>D#^#N;G_s6l|T@0;$t$caj8Uvf8|C*I00JPgM? zhj)gsXROG{$&oCUN_;yxeZB@SJhM_xC93fL`N-h_p4ZdJqQ|(X`q@)3iKYL>Bm2CD z|NTYo`0Df3xyBztt1{mFq-$5BVpJe>+Ev$y^i(>J-f-ItQjUtD@uAp#tvQ#8Ef?^f zRejDV#?PWx@_;T}di@045AU7gvtN;LE-on{-@H7X>}hMbKK(Q2Zg2TR_O49L={~DG za;f#vAXf9WUq9q$G_Fb$=)L0DkRN$WDo_>C6wTET;JL9d`Avy6<>${^ES_`cpM`|H6t%gN`sqU+#-j&4#I5~S>&_j9 z158E5Nxxsee)-@hx5V;Ej)kRvDxHaJG(dByC5VRh7Fjd|z}li9uvaUu*wyTbqTmRO z*3oE^O(gqB^peoAUnw-Z(C3#Pu#c>=@b!xSKE?jYA>F;bDxRL6Bc6U*XKA0}udc7N ztZ()8_v45E{`CtlwX(8uc)KV_%9lXv#RT^TRqt00tB;kH<9mJq-XuY_d%4#%H7QYW z)>2DKL?TF^TrTLkO1&YbmR`et{q;tjKR%IhX2$ao&$E95c=9!;0^v_v^$1VNSB$d_ zp9s8#a82l6R~rh>3k^ybPYYG~ys$uW`gL0f6e|HUfDD}@;sLOV!dC1{*56L23JVAb zE{6LU~7U9|79x^d%%g3tb^7pb2|7t@4{lwKcevY?N{Q)J&{K`?_vAOa~Q zUhyeY8yv}wm-ZBu-5sUG4^FVdyi`vREwCTGJk^$Pym8R6L;7)Tlk5}Ohhqvdt7()3 z#s_N#2Z~l!JYqv`?5w*ZMEDgvx#eT|0bfqq>qfbe=T}x$CE9t7Gy2!B^s(;^V>QI_$mit*finLUu>@tws_j=W=(vI z&{q19Xvk}uE89y3N=}^DKbZS{>~!AszT)9Bu^*7OF`LHwokyipi|@?x&hHHC^ zq%07eqDYQSQgL7lcr8Sty=`R*uKT3Sl&Z~x9)S#A-a z+HkQ{TNidyMkQW!PPc z&2$0+f{AIOgWSHsL2@~FclR1M;chg?-GPk*{C&ZZkhdLe*!dSPUOZ=(6piTl=*)w9 z^5n_HB=fGt6+1hDG_9vqRaIDz+GDeJtK0MFpPcS(R(?5IzvM_t@XZ{j+sOxp0|N;e z7#L8m{MJ>A4u*=WdQma>1E%Ux=|@~(6LRlWQcoG<7b%^lC7~jD zLOliPELwyIzg;DYF^(hoJfH%TVT?DiqqMX%@i^4A+}vDKP4U=?JwD0K?F*K*f#gls z$CZaNmsG|MGw6K;7SpcgVSPE9qzP|${k}U4Xyi1D*RQNhH~4cx%$5l`zp0;C_ZNvI zBqRvEKFMxH%ciBKvhD8$k~6wZC%MLp*|DVTuV&(hSK}}{w<}%dBUibYS=hE($2|Q` z#@%x{2{3tSq|~PW_D3h~$;rt?fEfOSJh!q_v$BGWt`TEd!qc>kWTJ7l$wN}Ag8h`HJ; z;ovOo%r5~2y)CfOkoB#b0>8ac67t?&mCMCBoM8V-sxmgtwj?dJTFs8cRqf;%tHbbu zGXJ02H)?V?8)=Yruve^*)&&V-fM!^eXd6+NcVE6-Sze*sZxbne)*044(Xx+h8|X?n)u zGOSQcZ0unwg@b;MhliV+JGh~CR1jc;QfHh~FS)hmF#+@gC|+6b-W}yw#|>-3A%M+T zBJVaf?tTZcCDIUQrQv+Vv-xVE0hRX)zGWzrf#nf*B5t++(TcJU>{v$SK7(mE7ukPrwv{XgSZ2Xg4z zuA6Qw4DL>e)ezUcN=QK8F0*C6TWT%-dfMv#{gEcF6tX%ANlEnPk364x8civx@Sxh! z9?MTu)yjcT;}Vh&;$dptWQCj;rL zy)QCNg^CTKj&j<>+-u{gxx$Lp$jZr?+#C<5s=db8xFq(qb;YJ=Ak z{nn#v)@UT@$dcEH9V)kKxmnH_(s1^jl5=}6kjrltN79tAmtFKO`e)LmM(AB#(G_m~8!Ap`ymb1q4Os>CCU3`bxgqA7KYwd~s>%<@B2VsNx!` z+p(Bs4wVMK|56~x?ps@@13zKJFQMhG;Nj^Ro#C(^US?xs0}TUbE4h8>)y`X5^!0=X zG$&}Jsk79m;~+4cXb~WvzPc^C*rDRUXG~#=!UHIg& z{P(Wv=yc2FbxT({6pbCADY=^VDB_p1G`*jlH=zi#LSPAU7*V+NnyTl-Xws+&egfOO z#rLTWk8H5yLV1ALu$R!_VGqHOhTsV9>Y-4U;ML0$MUgOxt@_<_`$K~ zW=uv(n6JD|`o(EEyaoLV!X-Ip(j)BU_iTfwk55KSI)=qF~kYHr`lp z6T3P)JiLUxpGw@5Fu^^1ny8z{*2-#p&^3U^XRg0Yz{NvqFOT4P#6L{~XW`?s8Cj)oLeRG2e*2WfBa)_SM)G489{6)9zWJJ%u8dh||FMKC8 zlJ2jcU0z;39H_^~$A>_{2M^Bj@r5=(uhd&$(!f?3d5bZ)Rc`(RZM?3QmYkWH8C!wf zu*~JOV{fj68bhr5>zSC08=7AVSKz~b*&LIb8}h_u zl$FIn2@eBsRX;B2GB+~3KRU|tOX6?P^H`~l)QmBp$A z|H@8nhdy3{{R!Zb5cX%u5SX}{ok1K*!jF~Ha_97-avx6&$K{)!?yliTN7ch0No+!dNWztv!~FE6rgS*!Z3;2VYbcy-m|^(K%?N89PqfA$sP~b z3M{+M+_`gy&|1oKeGY5g-QKPN9EH-uhv$Jka2_f6-JNFvElAdT&vy)dk2e+}HK$fr zCjmtuX!QB9+`HX@W!tkA559=2q=#L~*D zJ2W~GJ!N*oMnrBF?TJkjP0{1S2zg~YC~;L)wE`ip!o4>~gOom_?AR3u-FAj*q2UdH zeFvVawaR@3uLADn1#o20ji|u+kixjHOzE+V>?uZVUK~k))dEtOJzQ$+ut+$?*hh;T zR$-`Q|Ax1}p7Pfv*iwO5vYmj>5UL2M;nE1clD8C zv_tQ8f?_OoPu)CK6d;+uo1L8F|5Vo*mwQh;l|zBJ-Rt9Q!wq9B{pD>4_r?$ zQFrdWmd*^-lMsZ*_1%n8KLJQnbaWJA-NX{zF1I&g+klc39~Kt2dPCNW2n89i0(Krh zxN_lR2r%K1eOBK9=>0N|P=&yk8U6a6 zrRTl&!(UWWYUxN`&A!Kj4;|0VqYU2s_Ommv|3u*ROFM;x0|Upj5W>Q#8(}!P6%Tu6 zzE}Qy-o{C&A3uKhqlbE~Qy?uztn*WEry5{*zD|7|I7*k2>oM@Kfq=R|hMta&bKdR( zppv~lTKX&dTUz@1L%0%1LSgxHr+&Xa*bB2no#o;A?Mi8KC6qx?S66p;r^nFl+O=yn zqMV%Kl93_`%T~oig+7#snv{Wkx-Wb4Zy;WG_*Vfbm{6tjS{^joQ2I7E3dl17j=BS* zfFC|TH+RGkz}TM5d!G@kf6weog*f!g8v-|Wk(-q27n#iWw$~8KB>nr(pFe?epPfCq zxOF*At40)Qfq*7`(!lB~6=C(@!Lua((@Ql%68<`bUf*afZEdFpU5C}<`-?5LNA`bq zcjHH5a`Y-LEcV={h^I_^F)^usx{%|t2C#yfH5;mP`*=9?GPUq;JbjIS2zrTIyr&BH z=W>W0?35f|N+e&EKOp>I>i6+VXTkY<$48FIHeRl27W$fZWBYr#eSnT0(ub zs00c%NGit56HWegz?!LCWP1DY<165h-KsJ(gZ>aRkQVkt4Xv!LIhZIMcb6K7a)*I3 z?5Pqr8Gm#ah;#sMM$!fzrq z39xh~>V+=mkklt^0wu8X+uVDOB}MGBSFadk4z@aos9gsh60NPRB|3Gg07Msvs05N2 zep(Nhpyv!C=U9e8@sQnW(!8#>O(iL}Jc*x6w4e{@3^17^Tl>Xs%s*a$r6x_EDDR#1 z0=K0x?1&xca+ETgBYpxGO}>z6$nLGm&0%&H-oU-)c}?=Pz7*nO$;->bx#o1u7`#?G zy@svR^xf_PNc$x~plT5hm}cBbAQBLzD)mzUrHnHCLY(ixj`)0W7hVOYYAm3wq`qH5 zI3omF1-6~Gs;HAolhP@vub*r%~Wc2le~EQ|uAG4w0{o#?8mJ&xh;=LTVM zk}sH%fMj@-gT;4uY4fcE(#X1odx;>piTS&pdF#sN1stAvF>vj!J2ElfvO}{9R-)DJw$+Fu>iFSaI_+d9*eXr7`UITZ!vkdbS zbs+F$&EhkxfJKrs{b4@5d*_bZ{?;_0jTBDHN?JFLdAh4noo46rnQl`_BLF_V_%7aK z|GVL-ZLjYqsIiEVRfp8N?4g`P)5mdL&S;8jDWfvdG5#_ceIaDkHN%K*mHu5$#m1=eS3EwXQ2@;#eQp|{8-&zh0PSr*zsPgCEkOwu?@$pIweA5>$(gb{ z!G$00y)_x9Tk_yXzC(-e{cLEC|g?NTePa$|_FIx{n4_~rRYCJ1i-0aXFT z{ez8K)_p<9oJ*VSNs>annKyROg6A)JN!eSy_S^&|Gv8sd`A@`TQ6gPj!|Qw4nlnvp zcp6DE-X1ld`iA!wTmkq_wZ5dejREy2$fm#a+P!;&dFt?&@`;gqtkTpKWbqDPPFfs^ zwd*Ohx$EV(kshrrgshL(?hY&say!s}ue7^z7M!W^+1o}ygL>4a+Q`r4Fptv;L+4%-Ys$N3ztsO-lJ)5H^mOJnCWvsRYtnK-`+XZn zU$^`)4$_`j#y**~#4fc8bpSj(!ViRxT!0d*cMK$_hfSPu z`G%h!$t@u8KTYdw$q&{F9+h33KZBt>1Q?Kv7HmtJ2dkREDDWVdunOmmBb{Jz8$o+` zctl}t!?wlgdswoquXbq`S#|TvtQUkYErmn9+WB3xZv{s=JY80PO-hhoWx>E-(`$mA zbre7``C)O#TZI3a`1x}{+(Tfjv+tIL!S=5H?y~dUxeGB7@wrpToTn-8n*siC`R;Pd zA*>9}^WFPB*Hgg$rvq-_MZ%Nh0g%~`QO(WGP|HJzLPFSS&aZ&_!%eKw+1An$B0Kd} zG-#?YTSTP_bz0;WLKGD2(}PC#8#fAK<(gG=3hT=r-RsvoaV5qTWKa1hBb6CBFhS=5 zhXTrgP2B9p*id-ug$478MJf|1|9FyUTGkWD~%HnPjpaG3Rh=zBTd$Z6K@&FVb|^uKI=G z1_%d4v8SI_b3vcwKK0T9FFfbNhmfRX(mB;dC!>)iqvMGLQ0MlB1cIt?9Ry*TU#(dq zMI!!KW}L|GTsD=P`%8eD{o2eUTjhTF3jK>gMaf?Hy!1YMt@l-o>R{s{K0HqZ(h z&rA$PraptUz&5Jmv^S+-#@n}nh}JR&&CDq#3TxhsBpO06!i5o?$lfp_f|w-EHw&6e*bRrjQOKU}IWk>^NFe zBU$R@A2uD7d&>t8#^8mg5LsrzXS*u}&%RBhTkE#lqR#S1=ii%K9lpt*3xm>MHg-}4 z50DM`z&)ClkfWq=2uu zpd04l_!Z3dk31F?-@x${=mO^DdNXC#xRGx96PSM<0iPE;9wr$^%3sd*b;O^DB{e4} z6kvV$M%A?R5_C5LWdjbXzW`<%D1z#16yT1j7b8Efg|wm1bqn83P+wcR)#}WWoQ_OPb0e&tO*3lr{EGeM^kA*EU$}; zV~{?Lp=!O)YU$<2CSJYr2&jRZ8*rSaf9Mzxykosid3{>I@6nMZN|YE8gXesB78Xc;?vSj&fT&?2eZ;M)|CCj=pV#mC0N%l;jA4c z-Gsv$9Wpz-A8zF|A96w0+Ygz~(GNsEu^g^4`Kdrd5s=a!p@steQpkcZR;Z`aRRDxw zi`AKLdwr1LZ8^vmbHiDJy-vov^;u>T#M+&Zbm66xUOfiTruCHw&sRe@q#!#%>yzoP2A)GBagq<)jp@U>v@!vJ+*B*Rl#M4r1K`*G71Qe43V*UzuHk{l&x<{HlW_ z4h(8j1IicT$zdx1{^F2`PQT>vKamI!^GHL~zmn6-&k^A#f;1} z>UC4!x%3G0HnpZHtdwkhURh7WW5F}NZbSNr*IAQl)ldSaeFjd?lpsIuUhcb5VGp3* z?|T1By>E_yi)_wp31RULeX2Q~UY;k;2fB{J!1xWQ`0$g6Wd97YBZiUR#Af=3qcicP z70k_dxfaBDpYNe$7Tp`ETDhx7*>Lpo-g-PJs5)7r2W_~}0!fSvd6yH%h%gOZ(c zsO{|ZcKBSe!}-}2hy`g|Gf1l~`&#UglrO*kkUJKmtr1KF9*~Op(tdzTp1A#4eFB45$bLOUuihADz1ncbiLgk zxp7-}Y>5J)1L^C690fuzfiRXld<)dPO7ggYpMX-w23`(U>2(7;mQI22`HLPSA}mz; zrViPQ+(Kaf7~PVyp%6OyN~LN4_OVCGz7#o<^da8rKZnuJzh`|e$e^X3eX`&E8njoR zp$!QpkI-%X8Ya=NA|$ofx((pCbMx&UFXCVx9KzqF?IG7wHeH{0GUOGEZX8+g^pgbb zAf|D|mPSyZ&-R=ae$}zI@b4vdhecE37sAIVm5C^r_qC&v2|rRcRpaZ?OCOMF^<~PaiPc|3g{jhY6oY^@uwnUVhuIx^Efuq zvyAcw`x-=NsN7|{*P4bS(O*PD*Y`eHeP2fE>z+OF_ceEf1rC@JL=P&R#HOu zRPPP4dw+9)s_zk z|AKN2mdFzRT5O$6Ma%0Y$sJLNI;A(^TPkmajo!;Q2O;@@++4 zk&+xMk)x@x{YJO$oWbz;0enw>&6~ntCq~Amolzk{z%gh3si;0O9fhnQZ>6Dp1$FgjP_as&C zq97SBF8af%=~4Ff-piMWG`G6A zn~5huO#s4tJmQ=z1-Rg{ptf_w83}0~AkNZF`VFqs?E5udCl7i>v-k1E1jB0si}ci3 zywmu4O6ORS)LpaJUqjA;@F3xjo;-)uNFw z$%T=n23AeQdr?l=jV3eW1ky-l<$iH^cinm;335D(Nk_JN?j#diG$}HF475ZaV=(6H zMju=n>&=Gr()_%6rd#od*t9hD2K8215(l^Z;m>y6W-E34#wqu@V}{a9bY9C@DSY=- zaT`}r33$DjbBt4FNV*QX?gfav?rs2o>Nl-@r8`kE{pb8&VBJOtN47Y^t)BbvsYV5j zJnv82*F<4E{I4nOFS?~S*wnh+gB@U|-Fb6xHE#?5uq(Ca-gZ=?TE;k<^`2@gbV62Yyet)3)XxfHACMnP(=(oejCw~?# zaJ3m3E!JLTW{#v&f^dYix#_y_2b!HKiSQh~J0O;*V z@XbOqiAN5VcV=NBu#wdvmeYNxhI4t?CHErbCiIJDHR_W?ZsX+OS)gnho<{k$V^uqq zI|~fYM~2GiD0Co+GL-GjQ?f6C{&r+zOUIR1cgE2yE2A?eJ|c6!0&P`_1JjszinQ!L z-Z;PdTkz{QY@-wT>?}^MVfsR65qr0?$F`?AqUlYauiC%0?t4{5Vv6_7ve43B=T|op z=<+8cbv?N74;VLx*!rp%_O{QD2t-Rwjm>nM3b6>y#(f*tIv`a0xb%e)7&Seq+u(}; z8a1h@??M_8o2+b#rq>vae;wq7=zhP&(%90y^cSPM4 z(przOA$pgcZ3TQgg@K!d4#8x+d|%?_6zg4GByLq0q6WU(*KpYj7kf=@s>=tCp0AZEb%0V53@ zpBV{Q-Cjz2x=8tyqdTb#S><3dp++W0zj`}=O*2VCBtJ%Rb$sWBj!u!=!$q1ba3v|L ze6svb7U}8snV9PkV9QOgY*b%Cyb6ZUyhGawK+^h7i29o=+XrM%w~k_g0^J!Le)I_M z5Ua&>A>>V%Ehp9yn}r<~-25~cpYX;Y4zGwJSO@<=-E?tNCe_Ey>~Mf1JU>kf-&^1Evs3wH6!zK-ocdE?2F zZl6#b^E?4LzV1(p9)qX)l&pE+72Af3uvpCahgp89`l}~A41zp%&$3Ia&Ox)@{N-Bm z&;rH%S(J0ecu(R>+l=o?z+Z-&rs=CR$3nZlUi}~Kdgr(7q&gw;75_p|QNVwW7YwA- z=G+&i80%YR#9HwwDVmTubbPI2EiPR`gYf0AXMM&3;((2XI(dmzurZr z_Q|%!kIMVQO5zbZ*RNA=+Og4+Ogi|&-8zvS&O-=$+C^=iHmslPE!rBT^gW?G;M{#r zYIm{LQh=BIqcX+|yM`SErg`KUPwvq}da3i4S8 zk#+9{Fj^OUzvVkIbR-^%n(X#9`_oH0HQP5uZ3h}ZKOqI#R-etaLwchwE<@QAJaKwn zOZEOef7y4$gt_%KNQSoYp8MpBz1X_OM=eox4d_tFgI>_uk#YEJwHOq_Db|o$QlJ;0 z;IH;R*xTp=F`Ha$Kn_eLQW&U}J_pqk5qZ#)ucPT?0c{@=SUTCaKiQNG#{Ox^jhbY4 z<^NzN4i)yue>3{T0J9S&w?O9$m)&<`TZfUHI*ksFt+#5j_!6Bux*lM3D+E?oj<+=U z`|rG(c2LyEkglAqkhmY8QtH@!ZQ%7Kr5QK9z@B@wM|$@?(a+A#P8^G%CMi)&~)rqG+GY08VUT>=nv*iX7=`05OeTsaeQM(SsVTuj-2q`KBkp0!@_@$;iZ}+@Hix-l>0*O3JMB_p|wAw7B2^$DYo^WckR&j-xQBu zLliMwUr7iu9HPan@`fLJqar`|;Du2T?bSdR1aukm;zj*9P~X=5rJ-YE##0?BtjIpO z&a^J*t!SrmT&kkq{0gdqE~4uec&t4D2JoeSC$eg_NN?`M+jn_6TyK~n(E%!$L$6sJ z0d-59KxRe;XIn-_Jt;`gC$Wl%R08Bp7!e}_17fU$tw?nKsq0UuDZ3{OWP$~F!t@-P zIQ{j!y}jQOx|K!kvh1Ta`+6->+8TiIl^X*l- zUYPg-XG&yLRDj82czsr4{Hh)BmSMO8x>~An5Gk~2>NBnY{aC*=JI&G95IWe#FZSp zUsN!s5=^xX<09q)wbV*xKhXxLHxQU;KQeuukZt}!x4L~`nAO!h`@>7gs4LpS>pNR+ z13|g%)5$M8DmKD+S(7|9T*U&7A|fK>u3wMh{Q2$M=-?i$;fT)Qoefi^XSEGD=i@6Y|A8PLe6k-+@`;d52Ozso3!-cGGcrkBG~FfS2lU;;3p7% zO;#>Pm-F!OwBVTLhih1C1(ns*>Sm=R=&|!IP@)2ANAp|7$fl%69V$@2{4}kHRubY@ z3>re{u#O;^gV{|NFU5DiVnbg5O+oa|3f01%oc2_ClMX-O{K5RWu!sDoPhXI`W2&y8 zz=2G!7>1lpdZ0Z-ljd}K;cJ?F>3KUYa|8akmj0~cjl2oV(g9bkk3PC5uQ~lY^@x>S z#FNQRKOq}qchSjfT>}GZU@F7j*3=y7?-hJL#JuGl`WaI_D>_+w$I-C}M~RPaUA6W; z8QG$&0FSz>%vGmPpT2c~FJ(pJ$BH( zc)evq9z3uVn9s=khfyuzl`y8JvHB1`Is^~{^BI5{h4$Ry}!?-*;! zY;h=sF%jFXe_LW%e`9Y)6l(zEgsn-E-8%PA5X$b&YQBj~c#1s2CEOGh6tZPDto@zv zfoBUzOk^@Jinx}&G$Nm{EG3x98878t$QhB8qz**x$&)9+ivN>(MME1gqLGl0cum3! z8>v+oe9ARqjF1Km8}4tGXP~Ho>2_jb;#~LJiHQjxNwu z*^roX&(kWkQ#@5;-^{*R8u)RKmt_9^vrCnPm21Oqm~?SegX;^=HHtp}I{&ClMY{Sl zb>6e{zF{`O&Jfub>={1sAfJMc4nRx&Tun`lLsGJz8s-3&Lft}zY+KvE%3VQasi>%= zzkPcLZ-^=d9OtoAIksg9U@T*zAbR=oWvm2n?cb!7f&>r7`*a8+m}eM7g@p}I5W-Kz zKKbtFbKF2xS$UC9ZtBOkZ~jInOifKAqN2ul#K_)kC{N#h&$(ElNqB|eV<6DB<%71= zG&FnkH&>G1tgbq>yt9pKnPi-03S!7V@RA$xR6~v>$La5Xu9~YiCWX`*nV_oKtQX+* zM||T|+rK_gY4YOz%1@CQIWw}6ndm@*pe;WCl)X6%Xhk0a({|LUOCY|ur_Mi3rxTsj_LHuts2K2CX^X{zgIf&H zP%Ts}Fj@L@uEYHAm`i2F)GMpTj*g8NIyY}F$sVl88fi#Bz)bfdH>_Z+5t1Ez7}CgJ zb==O2&;ISRHe_w6&jeAjPR(Yfl*YV>iE(qSK|L?d+9tvi7!u9CdF$5ldcWP>W)F{6 zbKv@Z6qerxE4U&5=k>4hl)As574vizIP%JR2%3l$Jf$1z>Y>9TQVtVG4fR`qOS)8} zKP(iz}0^>ReCkEXEWEX^QY<$_h4%vgM z$>f<5>>aB0d2gB?iPh{kU(f>s9p^c*q}LiD#T-63__M$075LmFbG+CcE&uNBgH>&o zsDu-DJmq`M70T6HOuoL=DkWlf#v?Sm8c}t+WRUtr?33{5{r=)J=DopBSMyG+Sp0~+ z%?06&pGk$}Z+RWU-8l6*QfwkJ(*!nB)e$fb&PqQJO7`;e_RJ$Fgk)$6cjom3PA6DX zq7oBZiMbLDv+{ipd|+F-VYKz#yGH7MjCZA7oz-LJI*hsj{+V{jrJ%Xo#T)vDfk;70 z-pWly#V2+Xc}br?UoI>v@*ZXk`~>Kb1j`wxXDnU{VkVGql-Zn-T~GJKKceFAX=#nP zrqqCdb0Y9lv1OOCii-F)Owa*hh)zy!e?&ma>x}4+0IF_BhnE_8kR!`9aUz{UpNc9n zZHH_}sZ|Nj5j;Uo?O>c!9RXX9B-rjE+xe)6R;~RL(Y$wH<_APE%x9p=VG0!5Gm;Q` zzQP+|rN}HPd12Z0_92X&-!=x(?c1-BJ*FM2czivi!9nIeZsQG63_-n!ig#P>K9||m zYe$6po~6x~l*S(y0sxOTv#Y5Lv&Pp_zz7B-ilhK%NRI5-j!c+#1|q_WxC+KQ%Julc z>LG7yo15nac3|Y*l9Jj8j_Vm_A@gYs&O~{&ql=Y+(BLyrbZa3|5O(RX-X8`89C(ea z0G+>I|9_u}8z=caecE^|htQU2VR4R9Yy+(m&LZ7cm`aZ*ch!}w=mnIs7@nAtaOGk@ z2`_`l1KOshCd_AGy@wNviHV70No>m)YX$8kl6(sE9cgjsS1=|=ytro1X*cfcZv|kY zQj}oaN#fzf?3!m4diEDCs~wL3N#vV zseE{mDRwIaF*{UE>#EQ&te9nf=294YAe5Ir5i7WZ_gpLJvcAbVR zA#e44v9nf>El4@~-|8W&To-#-@K0Nb*q5nW3q|b0`xL~cr@!&O$?CItYGcsVK-eMV z@e4T*zh!Rh$GJJ{Te*6c#K@Q?GUVmG?PT5>Zxp--CgO&9mzA|V-y|ybu=QYO8L#*f ztL{MA9jp(BD2gnN84uoJhpI$)UI%^SpL|?ysGukny2nwL% zxpli)W_M8@=4-JqhExuI623!<7|dJbZ>1v_eX*pX;A4FR{oA(c0_ zw@+mkwHMxS12?5Oq%hSuezQ-H2x5DXGz?_t=T}}QPGa#AdK9q&^e|*|Q|F!;@c@WU zz`rR-h#~b>{K{eUaTHP*9$YL%3%W0xwyGTB>*^G~cfalwySw4fISaItoc z0VxB@iGM-_q6B$7`~zD5Jb`E0!br)u?e1P-Mll34LO5G`u*TmZ_)6Q``nhA$;hMI# z3HM67Qvdhw3ea6+Jz$9O4F)r`;hncP;Yas6yQ*s7LR+9%4IGymQRvIbX4X8c9$XM` zwz1&@!+cR4Xd2a(mH}##@4>MHF^|E8Gl}ELxw@7acuK-t)w`^$r38cW?nl-_N2|{2 zGOl;MuG51lW+FoUMBh2v?L51F&gj*E#!k6u=@^eI9 zg&s`}Z7k-AndhRiwR$JR`EhzQbVk_fYi|0%fWR|dm(!=;8n>n>-8D{v8zxJm zc7D8Yxi-8r0AIH0e=olWV?*~hpFz1S^=wq5nx-ZfJ9{nlV%U0HYiluY?xaHnKp*tX zGljx{d{Sj{Eg6Or0B3I9xB+lxc6K&4vfuDzD5DlMQNxe)0Iv*P`&7I3rHAw)UW^4E z0i+V2kZg#IQI|>;b@dWneIlifZ;g@cd>aZnIyyDe0$*O`ACS4#Z&T7E`$HXc2xO=w zx%y)aNTkG}dG%SQ`JLJd(pNN0Y>SNrD`L@enF;00x9`C$1iU+l2{DXikw7~E{eY5& zMpzy)o(obnOKW@W?N@f%`(`7v$ZQ>0tJ|o{0Sm$Om)X)-J?QsMItvaJZZl~?k1)M| zXNvgD5C8-d1*n4)PS6NwicYThF+&vS`R=X7VyicXRKZf=IMr$dltye1Fg<=oC-fZK zL`*GMlhTYJneW2!CU^X0w3cXbh_s1$_zKVbjXk)*%)=Y}`~1H?6DG=oye$ZL`ScPI27Ho00`Z1lPDiG7d zBGwNc;2FaB4IJ3b=ABaM2!Lt&UQ3Fqy7KY?uCAXWqCz9VB&zSdc2iXs?B|9O|vL+u;HIQ+jj`~43;boMjn(?(9*<_VX3B}^i4 zT7w!h^ERQEF!)Pmm+iFQB{(Z$M(tQ4#VM6u_dXTW?I`FzLlvx9A1~`#S|Ey*w zr13cEb5&4M8oz=m-WkRugkY%OAgyI`te}DAu#J>hpRXop)GIV4hB|`z3}aSc`Xtn1 zT$;PA{meTdmw*HYwy#{f7MX`=dEn}1LPoUjo`bojaL9P`;~&UGdQW71*=M>(*a~m| z@M@9?jYtJ3;7EdCM%%z0Ith3Oly!`)-W`z0V3upcd-%tXdU$sellVvkPuAfd0!uPk zPHcyb@rs`=yi3G581B~SfN*VaB*G;?cCtfp(H}n^9p~yK8VH^_x0#WW%0Mpk6PQ_H zfd9Wj=i=jC>v#(7Ud!?fnFh7C6iW^G1CV zK3e(RjMUIl2xF|rnj3zwFi=n!^E>QugXLjB6&Xh+5%=R3{jqG_98?VT%kX@Qq8kr`pW=AwMOilDKTM7KhRtCB3i)RN+N4o0j6o40GQ5{Kg9U2AK*i&Fb>}4WoQ@tn_ z)og9~rxP8+4!0Ou?2F)BzBn19&07GqH4YTpr<{^05 z4<4lCE%Gdym@vJSecOn;9}H$-h2=HCBtmWfOud85=mM(SvAkTj$F*pYa2stNWI-_U z>n7%en<+Sj62Y(-OM;}ewzjrJkdYBagSv8Op_1r=ghVHXJe}xl2ce9zy80x%D76wTJQA*pz4q1@gdi=L_sS)H&oqE4X$leD!dyA%ne|v;r&*Y7x>RC z-XLznW{#?hj9qv{aF0I@u9W|d4rE`jJ@OWOlo)P3N{V*%i<>**5*YhH!OX8h@b2)s zN1msy?nz?epKEJ+{KLYS`uOgeJa%j@YVz8+L&dI4x0xBHSM`hGLB)Oy)`t3T<~%8S z3!gCm+|S^;+ay!&N1Y`Fwz(;rUXbNfyY#=kFM3+-L2zqDe!npWFCmmMSj{sf+?;*8 zkvytfuC(iQO5xMG3aI6*|EZQUF_Ia6Q0JwLPkixWvP`S%Qvi`A=w?o~kzdhMj|{=C zUvLDN6#EK2gkgZg7X0JqPrRwiPSr{dE-o%`mS|=Z{~ylYJD%$Q{~taaGb<#UGSaZi zo|RBSr9@;F%FNC@-jxxO6v`}>lXa&j6@$cFx&7NBK1#k$sGP@!}l7dT-J*GU%(s!61+qUjfMt z{VA0}DegE}ir}Xg34r*cUI!TfJP;IO(eG^m;>+kWs{qe=M_4^#VO>s|mQcM2ck+OB za(a4$%#-WrK|b=`MV;iNUCno5m)r5aAln>!8|RQiwmqVFXrygp6i*#RE$S%B5JJ%7sUF6Qd(Qt2T-d!Y61}F< zl39dw6VKntUdq1<4!TKToq@}_0s8&&g{i6A8D+~oS%-M6k^;IfdV}25fD{FArKP0} zfx3Pef`0O<=PGvmFrXWDj6xWVn25;D#urkIIz^_9UH%!F*zJwHHk}a=d?yMzeG@5w zN{DIl@6Ku2ZB+i=_5WRk?gFX*{#iZaG#Pv}fi_Z33Qj@RrkmI4dar^qWpfQaW`ID^ z`8!YRB(B=0KiCm$E$c}WumV6bgxO>WW`QCXj$E*?EE?VTSYFP9+uZc2Ape9cp^q>O z-KFD}_>!U9s%*Y{8sPOCz9=p2h|L#3XC);ig>OQ@5wzOiau%j};s&e2(2%-?dh^Se zpee~V&pH0jgXagA($j@2iegk&2eBYm(A3pkF}l&{tqZ0DilC8+%oc8!Qb=2h&fCaP{y`-*4L(xP z{xD>0XpbE~{Pg1?sBx=Lp+bV-blp5XdLoIYWR?|5TyblPug5MCMlaqnH0vb)`k`W0 zm3Dz8a`i&knu~>2I+Rs1Dv5Hu=T=*In|fO;UpX$tW>=h7ZiRE{|@C= z0gz#8?(3@r9yI=g2LtqUb`TF(ifz~oemEi-_f5jJe7N~4*+`Nfoc?OTR0MYUU zwlb*y$;J876_|*@W1zT81)Ln7AHZri(iSTzc`saH zu`#o|T=u}%+mZB;|D=|w1u*{QpP_)`@%`Ou^K-Yhy!Z$HupCH*i;4vwf|@#rt0@0m zQ&Th4U+K+O`0bEBS%cWoqbL$58V^B8614Xw+ft3*I*QZY%gK>M?bH;PysJ&2$2<>( z0Tf+8{?4;N#$h(NP@$g#q0-woD5s!;yA*t4hs4C1FQvYIeIGzcbT7%}fBa}K>pCB8 z>95;BVWsM&27SEmN%N|dMF9Kb^z#*Q57X!ey^z`f*JJ+)$ z?Y2X^d19EIzTclj<@~vGISpxh#*DK~=xbX|@6lFK8T1QvQ0H}(j;kd%9@>9P3_HG3 z76=qdXa52FJxA}m{GjX7p7Z9&vQ>5D^7E%CXAbawg|(rZ_oH~snyh%OpF~GnSGPcz zt8+Th2}=im8`Y*FEcH64E3|eL`CiLs)CA~lBBkIicv)7~C;Ahk-d!!p7p3j#>fr$# zwmoOT^iSIFYtM(f1K%9zbi#8NV?VO~TzZmKVajAzpK^$FMeV)y;s?pDUcQ$e<8Hh< zGnsl`H*(P35~+D{w{7?}`yn>L**Bc@98_V2fHH{$Apa?3_^I(1T=+;jmlyI3lj2x7 zM6nC(f$Z4|#KBHI0+MIA%70Eu{;nkrP<&Cq|7tSAs_@J#@4 zv&m4V)#COB(FMdPSrE;o1&FEJNy5{}$V#9og>M3AH4H2iV)u>RyKhE!Mfh)8LNW_w z8_;6x8?9rMxZfz*>*>>!trc*q$|IpU)ZXJ^$D6V)dqM$a0|^Dgp<;+4FL$mIFI?0q zF%o-Ut~ofcNE5(-7|iP)eK)BUH)e7{<dYm(5Y{Xj-n;4O% zr3Fw3(;pM*(tXo{eVmx64|{D0+H)a(E`$O3PtRyUZD%SxC77VY0VRQgTfVnO1aG}^ij*AoVe z>#NW@h}62fx?-}x5ma&JgID7fZ=St2&vH4;G;j;=;7VIA353uG4Css2KXr~eFKu}F zPM?sHB7w{3Gc+?I$p25A>sCF9i?O5Vr&)h{5m`K+Jb9#Zb4E!61p9uovnYWMB=B(k zHwB7^y|tkNLEATCB6vR|JUk@fDqj)LjQO{@=t{4Nw{^Xe&Ma5+(>$^BLcy0--Mgyq z*{%V}qQsnFMSkL_rb*Q;0(%;>xN4)?+v`;q8Bg1`oo&@MRC|@qwzya>*a7f;9sU*> zYFWs>)7fQ(Xp`x10USh5-a+ylBAZ^7DYd9}n2*qz-M}qS&2$4v;vORAYMONAH$%x)El;*Sf|b> zf?BJc{##eI1|c{#1=QytUqE=;C}1n4kgj@izzJ?LVS_CEB}AWCuu%Z}&JSXs@&$kl z7VhzGnu*VbJPoz4zlv#Q*}osOE5OUjRIJfKmjn$#48ZmqKwBVjJ8nqfpOwAKE7c$I zV$HH6wA-Vm+h3Rpfyt3`8dQDlf?$ovy|;ExGxYA^zhvJa`2FwezyE7Ac=t*^oySqg zU09VX7U{t}zX7*sh1{(HzYTj7G-QIgjs2lS+Y7BHrK4Py#PQ=%%sR4_Ola)`%5+dl zs$H^KR2Aen7=Ddwo%YjknSfvR6AIQJ9w-?hZ1&%xfwNgXc9(4gh+sjKH z5UBEn#Kc5D))p-t9R#gj8dK5$wQ+uY1rT<~9NQBhWM{HKZMKDfvCsV&ZXB*c=*Qm+ z4wealVn_3Qp6IP1d$D<3<8(RYrd&f&U=@5Yp${L%0;C{_P?+zK!!}mNW%H)Yc`!fN z`Y>PX1zNkB&6PCSnxfquQ+LNgYV05N4;Be^cYYpfaN((6zDv8n9Xxu!|Lui>Z?C@R z;2o%*bUO7|tkvRJPA+iN=O20z&UW93x=fq*%K=lykT8pe=B2+JPE6O%K~y;I&%|6M zR69@}LNqmEn6GxVwBWG}U34~F`_Dm%!+^WHINodxuJC9)*bm6@kYgZ{a(x9dumHq@ z!`~7W1M(33^b%^du(Y6`S7rzsT4udBW3D4}t?vUsK1YCH7nG8Ewp-_zL;N4a50m*; zf!Gm_)47duP&&g#fV7ZCuqWJ-TmD$kZvo$S@0PB>O!YQbA8CVesiy?uTTT@a8bPsy z#e*E+cG?AKvw$>tM&AC(bsjB-O$bDiz$OE`Li|hn0<(mAJU+tK+~)a?*2` z3c?TYC$AB?mRBd~TLq9&1N?!Og~h7@{!Q>rOcvbpfK-#8V`J;(x|*8kJcP;@$RMt7 z*?&ux1F$d1@eL5wD&a9_Z2Gd`Ugwb0>UEFQHo0B}PnFMp0&|V{```yb=`5y0pl)hM z^dp}n?Vs!a&mQ^jE%Kk+2Yp3INPFxDrA&a?Nb&l3Cs{7_Q>XrMbdgV>7;|&R_DK`C zCF+Y*c0yCNbyC}M%=3@@Xgo8x*uwZFpL~I*GSmD-f!WHp`@xA126^~@d-EB@iH3P; zQH)vQfzf&Cli?V0gppddtw^j1NiJ121B<)7&|Arg(?s0TT33!~pe zSdb9R$cWQ)qo)(>Y>zT~XWetXmZs*dRXkIfoBMpm{?!7ka_Eqp1INnRV$zNsLV_QH z&^G#(_3 zb>N`p{#6a1p_;sT%Tm81NSqd8E2bl;!GW2nVU@)(ZLMNN6O2Wl}xrEm6$Uj<=Qj<-akL3;`3$rfcQQjp=pBc9-bZVG7jwM4?$(d`o*xd zwO*!a_(?YgPyB<}(pN8846K>s$QvMu1`;*vn7SZn6y$)BP43MA#o0qw2Xyw=((OXPnkOzJ5M>rW zg*@rujy_dPt8*R|3S9Ei_tpZn*J1)vY4+?X9DaCAIA8!`5C9LA1PJAj_KTm#Jc^7J zf47{LP0*>3M=@CFmvS7t=RpM(2j~1ETo})u9lLss6b$J{fIt^EDUAgH)^rVJMUI3I z-m_2DNvH=oz3@#2#d%Dixf8h1ByRsWY`O-44WHo}kcXP;A5J_v9;+bt1F0hM*?$>A$lGnqTH%{cBMO=YqI5)}qlIvK})TGiXPkD~rj2{MoY@TOmX z1!tMSp=&W=^NA_&QN=f_h^?1lpJmL!%EeCxA}B4Lg~c zm=+M|<|P8ghwQ8T%EyPMuHq}8Mw5hUMhivV^e>mw=25;gBHa`~@X3Myfx2N$3k?5- z6*cP|YD)uO+7W<|QT2J=LfAL8{NP=la2Da8pDeSpn)eADVaGL1e)YV)u=TKou;C8S z38S9G*tpZb$jQtObkdBG!Cs^;KP?V;V42Go6NutS7-a6|CiREqrfJt7ycE~Pjqim8 zC-E<114bgsK?sG+zYMu;Zvd);YBJY#@{*Pv6d9nN2@+duNyl!RGjAM!42#|mfH-84 zyjAj9AQg(^HytRKFC&P5K<2~=)EnWpUYS zC{Sg7@9zF;Xr1r{l_{gJ*FaV*YVlRQi(^J1c$+k=93sN&zJC41!lUTzO zDY^9i6zbXfQ{ALR%w`Z04xXIVJt{k!i-%uJ2|UBMNoOC!6~U5098CG=#6%ttCrTf-|QB+PS$4!MGhZE+ECz( zZ!vIye`S@e%XEcy&xotpA=YUjRcyupkF{l4ri^HWliqcT1o$1#i?z z-WdEpaXjDm(V^O624I=i_V&?@@{$sT!0!7&-$rxtsua#(&2AmzvUh}E4;YQ;Si0>W&kNtO7zZy6N9Ow1A6M+wuLbkmEV(b$?Zbwn(+NM}LkKDtMT->_cs zC5FJ45i^)IbJIHLes@*FFNR(M^dD>DSDc{_j0`^frL^3gu-x@#e~u6pDr=L}avAT< zABnt`+2<}G-^h`7464~VOeSk{J=K=6j|#=wLoVHi5-PUtp#l$q^?bc6O_GdvBdyeb zF-6P7nNghWsCU$i{rR3T8&eRY$a0~xDpq5s%EYp2jzd1+Mwakn3NG%-US025+Rmm6 ztjnEB6RZ0X}1kRiCG@f5_Re0XfInO-D zhbLx!#T((v@HDgoP5b1e=8wfX&@YcFb=Tggn?K{(z=Oj7q*_rOd*cE6yQ7= z;gzr2fn`kuU(@aaEGaHVK61`5p0mB@na><&+T$NAK?wGxM`5dx8^;^$+g0TXat% zCDKWazqE01u!4IcTGBBNB!~+`R$0FVV z%?+iF?|?UJT$Ru8E62DRtX{x5vxr75urlrI7v?9z#qGCJ4Vl%YC8-t_g~O*+W3>O4 z8v=830UjjS*oh+q;+mf;ZbWPux)+G2`|8p#@Hu*P@Z<8&IdE{?%62zFA|6?01gxsiLI)4|=O^v7fee)cLKqUv6xlrC;KIQr~iZ z#+1TexXAU#j<}G?`Mnso&0>7Kp=jF7b?r9a%dhqI7GIwTM(4n#bWva5O3%{ZIBvtv zVh!)wz_^sO6yi$~i;YoV-db_{%Z@m#jN$sYITq24}wUUB`y9c^tohf%*RH+D^ntsxb!Kqup7vc)r<)d#@R?x69kv1dPYdLD zLR1E73Ivxy&njr5;OPO#buYHT0F(f%*Kd?H5K9;7f!K&_(*Efgg?4)F#7xl!a<|iW#LokmP=`U%tDjx@dsk%!D*`$x4GTIJr9|o^&Xz6r-SgS%Ph7Bj zX+YQeX!|(A2qZ7}3l-#BiWb7bap%B`lw3Hoi@%Gr9-jIfuPf6+)hU^bVPm-LU0t$Hu*pluR)o*Fk#(6b7R#9Vm5+QWz*SHv;ODqV z5ZUh}D?FNLppex)Cg@LMM=Lk7t&PvEGX@q?5VF_8&DjJ51XjNQuO&{4O}?L^Lhx1~ zGiD6A&p*L4=nZHw%D{-mL-5kiK3|#%>I)t;PPS^f%yRDgEVmm757Eav9EcQ8TqR8; z3rZVXAC>hc*VYy11)Iw~++KE{oMO3K?zitJTV(JgP{VyiZJj5Y_iN^8-&L$9otUKl zkx3qC-qP=&@XTFw?6*k34Z>4~tIxN%%{(6A%tD{MG53G8{oPUj^vK5bJ_2JOh_d&% z`&u)tMmXLn+H(-aiGhg7eW=a8{$8V?7(5E56?E$`bsZgKY0zmJSyBn?zvvr-87Pn& zBW>=ai`3PqwvZX)l^|W?nQw>O8C;ug!SYHksG9m)%W9uAQ0&`y@gJz`2NMnzJ7Z1ri%Q%v7tMM`&-wA{;LCibsh;D$=umodIDI*$CDZ<_nW)VnNWl+aK(@q&vxUvz82T$IVv~_pI=@( z*1%Y*QWNjuaboP@&z+#|);{i%A8Iq8_HJ!yF+1W?23*dyPF_JPv$m(!Z2L~C7^^s!(4;o0%fU|vI{WlNX?F7=1u?!1 zPSBZwiUDUkJ-sLonF#ziRkGKCvkdpm+5B8IyfgKzty4|C>8eUUO;1mY=s7@x>g5rY z-CV-b?8^FU3@+^GFB4wcXCwdd_2!iG8}DJ=1<>R&MlIc!`@_4_Y`g53?tYMHUV0A# z_o^Cy^PT52j)Z;pe1fJxi{^Nk(2o?64g$sF2fM}+wyoYdTRz@MFt1m7!9iVCHV}NW zoUh7IV$9m|Zx_@?Wx^QQ@|$PK8$0Iz*%}*z?W`3SW0cU~UA}&zX^Dvra{htlzj*{X=_!_1 zoarCG046mi$s^AZ&dC8OSa{V#P6qq=u`ZWAh^?7-#>4|OhTdX|LPA3Pk^(>{R$Q`# zL5f%|PPDotPZZ&q8d^(DzicyJU<^rA0KL-{d>8og<8~#&D(e&cD0oX`c8_tz&9ic&ZrV!QT#d?L`VAw`tWXrTH08!_KohwUdm zFmCu?&A@JZgFHFiAp0{DiU317UjQbELI!R5LRAn$uHTjyZ1L6B2tOdq;cCw^uL^V; zHqmLY69>7ocj(3(sa9P@%(tNL0-@OzZoM}7a=!1GSSafe0x`v{{YWm z|K1+IDYy43%B+JiCc-x%lMTbA=-E4Ip&nre6>L1~|WwB41fS2y~Yfs0$1GocF0PFweGcrf?p zeF@OJnQx~y;p25Z4h;t1Qn#>{8gskWJ`lhc8=B63(4n*Dz`#qkGZXf7ZX0JVU)$gH z{N%#JFZ*wGeo5927)#)7Dz9tl`QX1Y>UlZr?vCSsn6H=&h&%C_zwNZ~@(pS1{p8n3 ze=tW5&5Eh&;}%X0en64$Ffc9}4=!lr?pAjg>Ije{m3W+sTeGZvbaPkG0i`hD(&rNu zyCv(3K;{qd8>n|ag9}~64L+TqD!HSz=H(xY#OLD zRR9>^fQ(G%N|`yE4=7X*zUFzt07Z2HE*>Gw>FFs2-r#QoSQYj#lKE>rfFc#umiMWd z4<9|6Qw=xsStBDO&)kSdzTg9)31FvT6@8tYjAxqfXGS;1uT!W)3%E&2gUcitpl}Yb zYuGvA|IMW#E|%tlJ?6dTW61YN?9(KU;;+`itq0-rmegouNY%?l$$cMz)^@^*`$L9? zv1{v@Xgm-qy5KuUIuy`nTs66Vy%89Qvmn^{fXoC8K`wce8_QOh0q|t8vl=@E2oBCR z7(s;E*|QqfV-U8l0qPEjiMIsKsGR8SHm`0;MkK&(XEFE#I1K}N+iwsGdF04JIl1TH zPBuIWEW~uyQ zncqx6oF(`4>y^zoYcrjJ3lF-p=Pd`-eru-w*;#*!A=?oGJyZPInv^;)eKxcWUk3J90I8CN3$-f-u5;Pf;ic>bvIAf9o3 z-3wn)20wJyj1&E33>yyJtVJAQqVEMI})%{{-Az3m-6TS{OU z-paf6Li=ux_F9(5>byBUuQJ#433N~d?y@0US|nB$kLEY_I9+>eS%8fo<-|RyOFhJA zp2KZ$gL&h~{OC8)k}$NuNKvk$CilH6JTu-~I$HzB3?sOhAn<7Vn6Q}GIDCRaz|QYF z49yqE8iCBo0n+bb=tpchn)3&`+Rt%YgMHMSnc6cV2bRDB23r%{G@FafxiAL+AeE|D zXF7~VAkXhj3jZo@7A5aS6Cr!Q2^0fdr=|`TQPfdgP-gUm%RFiq|3(>oP|Kr%fKcrk zSirmhaPhc+`F|Jfz6%O^2%A(}g+0E5j~4?4vYNmg>e2+V^l&b?Ot0nHnJ+#Ki)RK; zCyOkkg=mtUAR!W}Z7OnZSB79348Cvw^jw zi|u?(bUUZ3OL;&CwbSojq+(9fmP7*yRf%L#Hj=g+*RV^j4dJ?~{ zgvu4q-fd_~=5IZJ4*fa2Udy0{o~)B19h+F=6W{Gn{l^Ur>fj%L-mjxY#wn9P{g(ZV z$uPqa@EEP<=3U#Kb!0Uwi%+hoF18#G4TG^$-}tTr>Vqqyx(Y?%r~&bnEl9w3MY_{c zS7)g`-=3b=B&i{}uKGrs=!q!0%A8+#{koev^%Y*JnQ9 zO?v0B?_IoW+1ol@%NN0=2@AX6`w)#R>hhuc!(>*|6nhuSiSeG}4u1w)w|x9|7~_eGQ4RveN(1 zy42y{U~?rjj*Ma*k~zAtJ`eXBFw5z4le(|CgfYGo7ZiMOF-6_59%KpL5x2P?(UYc- zJ{2M*k4-X^KR^`<3Kl3Y>lzzB{kUasA3i3x&IL<2 z@zGjtIh|~;kTkVsJ^Wc-r#LN$f&VFoT;3-)C1k`2gn@U1;**uzCQT&jW{>jiCz#{* z$b!3Eq%#Y`Z9!lduOkHF-snC3(r;2zH$D|Wc~L-6%8RbP0L%vnh4P+)`lPkHn>__# z6+b;w;gpZu4yW{^F>Y^Yf@P@*^9{JFm6D7!?u6cB0JN^kJm>5z@oPW5D`kyO^kBF(ZAnNE3~_#qW36HcZ*RrQJ%AVCDjC zRz99k8vQfxuUt5)z(-W0w4`vQ-Zp5L(Y^Y1-=I-{XC>V~8iDRF${j*@fpQc>pD z@fRD3su{*E*E{HkFAmV{O10ssiW+l38B9HQD4ot7V`bi0H(M8Ekn5=xu#f1nDR{Iy zL^IQAxSASCK!3~<#&?WbMGQD)^2aHn&&cFX$oi~`LtIz>#c{&A)i^Ia^YXTc`W>pm=zQdI~cDhbVDAh|Ix^vg6va z|HJbgksd$mB5^Bjce4G7#Sice2O3`ln5xMf-|Z_IZ%s?#?@1jm_^|zrusj-UCEy-y zUe>=-tH2m3tsLQI8mM<|WwSvs&pr6C?hQX*S%cpoNnDo*{&j@! zjAa;`#Ank<_maD?9V{MSyPRHIUa_<12Ft#EkDuRlkaReEmKu!DU=4ynl%6S-@D6Qk zY|H=(tFa~PIB%BP#@_m~YM!(*X7&z7g&d`PhvT8Oll7MVEjtTN&M#+J6F3XpO8&vlw=p-#bLFTE*14E9r;IgUM20Rvp zg)ek#G&#%=7YPA(;1ag%$}-=s=W#_&?4OEnN#GC|^hr8NXWjACFo@w2v8MCKk}|abjHre`ucjJr0nWW_YYl?xrX}s;i(Uq(xi~L z_oK7(xa-6P7?NXG4S9Yfu`v7U}MSiT@*aV-B+;|7K--DkBfd~$HMZpN=*mo??(SES~X>E z;ovzJj`nBrQYsT3xSL`!e(|Vqx9-BZ=-YeF278e5@hLsUdh*VTyjndzD3SbqZ1W`E zBY=39m$;C>q$Z(}EnIbNae-u}Q;}uztUhT0NRpla96$z%+0=u4PD%I0FcnjScZn3+ z>@T_aZ;6RZ2nm6w&86?QgTq@eZ3I=F1caeA8U7V?aHmd@(g(@$R>HkVAAxN=Sw0}0 zPM0}c8Q``x(Uuy)2+nuMIPVJp8wd!*ZxD-WM}B>xcGvw)ric3so+e9?>ku<@8!#X6 zaDo=BJ-GdcfmXW$*CX6^ai2rhx_8%@w=yO@_`-JNZ;turKvl7Cxs~+A%e(DK-apZn z(P)pq$E`>_WoApZxNlOEB#V8z{Mf{xF(c#TL9b!|eP`6XNM$-2`mm}L^URKN_m`6T zM-2Q0IO&b4X>Hr`nF{yQ%{|nlf_$%8r?!vMIRO&f@lma1VXr>GxC5Iq0HqjKKXqE+ zj6>l8Tv&xMiCXfwUyY`zG;yL zSK_hM@s?j3@*-00I2mlOm#s1my}8vI3Wj1yqYQEA8+a{js1ky8JJf51e!B;w+O`r zD%b^#=a4?S$z%5*pOtlD?+EM7B$Z*3jjE?EB~uJo()wTpAMatID*IRvaf5a{yf`n9 zN2QWz6WqlUw;pA-RN%j}eNq>^Ndq8eRcyr9Ii=Mv5 z@c8o9>^p84g&-B}Uf0k-mj32wkJz2aCrmSrWJ>2YR4%z*v$C?nE49+BBT<}EFa@B& z@?#|1Ln73w(FHiE5AW&erv65GXmZ)WR(fr#Xu$dAM~%Xt0~-EG2UVz=?xDTT7tb(8 zW`LKP+8`#kQQdw7XQrT7gyU{)6qX0^GE~jmUsu52ZD|-N9cOeCdz_GNxFSEV$WE|!M#jOthjF-D- zLfzU?65HbmiN!~n)~wh&8m2Zz5`s1N^#!;k1?hQ>yp5>f+uRN`%1P->J#%Lw>N@o& zQDljRetYe;+p>A9x{Uhe@_-<^q;mj*xg(+On}i3aIARr|5_ zspVGKk*KIB5H3h?fRu*YS99!HN*>Z} zLFk);vp6;kgB>wR$lHMIycId1Jta6+DD5vgQ|0fib=8=>v@O$AB?Bg4@n_v`KMIQg z4Y`PwYuj|)0g+yC+vJRXPC{OdQ86FuhM*t@;$^JCWT4&$#s#ps4;c-t?+01N@6MQ* znA{s*pNPKQ%aTNw(#VYO1QS^UpgLiHgtQ>XMwNIcD~L{g))wY-!^&C91Y?)*&jwa& zWC}r$z4Z*XI?7bc-gPH2HTt`WcT*5ae3wM-$6IFBq<*zK!Hm|o0NbNab$ZnpiE zTz_eMHBz+jMP2C!N2MbMFd4P$b&G6+BY57j>>KF;Mgz@FjCk+f5RA>HScqgpZ2U{?zteUVdnP zs0un$PPdOAJNjKfrui=Wpdrc_b3!aK0dIRZ5plD=DLm)^K05R51VWW34 zW@B3!Tgsa)%s=y~)5H?0j_q3eb%8%`+hiKUFrTXv?;W{fY!@h+>k+#< zExt5hEjR7hsQ>U|$2^~?dDEZ^qwNOnuM!v!E0NNk97Q+A3Vu>!l!W&}7^VL~7;^kcoitko0&Reu9y){*rJ9P}hn2qXbGAsi1; zl=OHMn{)m=tm5eCOW**3pM}g@tz)nS20@#L#h8Dcv|l?e1OBLaN!lTjCxh* zyA2z&4%YPC_;?)q8Q84(C)&RLgVq3u^*UA2se_U)J_1{sN&>2-DxlxOktEUP_HB#0 z#|OiBRg@15g!$=07?6-_pFA1BI1k?g>;Ui}{=Q()2XyuKEJ0>45n#tj_`+i&DBLFaH_xL- zx)4Q@xwe1W#bd$8KKan!J)jhbE|q^@UPsV%@$c*RC2H;@01HiANWZFvt`zb=@6Sw7 zQu#2V_c|_`M1`DZo+mW;jmia*$5Bz1WKa*H@CzHV+x9*F6ceqXIr%Ou^N*^Akg|&k z%G1GLo)}F{@tqCZs{@C|YBuC4eo38jyx)GE6{}IHOuTUF_mRu#TLF_#RX(-$NXy`Gi?WFD(;R=IY@DyTQyNzxc#m2rOlKl z3G6NQVwdCgX5X0E;9MEthE`t3dcGnvuzP^kaZ=<|AMC%89MBVxADY`a%gh*(dvC(} zWpI2_lUbuGtrhkBB3hm>9=D20aUPEa4nn*KPT4~<8ykn`-{|@+L?LAe6%y%VdR^PJ z1q2f|#s>jHy8)wtK@SoLghRK@VnM}LxbKFAWaOv)@R=nbxwQHq1uz#4s8 z+IUTGXQpX$O*kiNS>RYWzB|7I3jbWd1wrR}quOUhO}7I!BEkn#<^cG+37plPdX#54 zeD)p(pYzK&HX@mt+N)~1_?(b-QZO?=iHox(>!EjEmeiTqFv5a$T3ug%{%bfG3u`oX zd^1m5SXijWv6ZVkV1lX`of6)Yti$7vFim5~6X}%_FA3sx+3(`}!Xd4q|Um@%i@^Y4Tpfr+*iHvmipo+Dvaz z+dD`?4I?@odd&XXkU#5S@$)kpkohsY{u#VVOJaH$a8*#KUb(`uvpKF3?xLHhIH?d% zce%@_q@XxvRu192os;mRFrZq?@m_J@A9F4`rECh>K&o*sIBb{hy?VumqC)uf4Gkw1 zO82ns`*~y<*Lbkv7A+Tq6~Ug|8Ch6x#(;4NyH4osrA0HCwtN}*NG8_j+$9EG<*{=i z(kshUgV*BT8qJHg{@s(`^@OX|$v3w>h4{XQ3V-n$xUWb5<>0{Fy3){!O#JYp3;jhB zO$xm^$4+h0U6pE`ysEpIH{w*>nflElDd)E&xxq?(G<+`~UqUK7v|fCCtclEcf^;^! zY7;=2bq$I#0RDBrKC~s>=ogsZ) z1Mu>S{*hl!6BpEKD~wM6R3a&S2W2iTr*s3=OwT787=*I3y%o;>5n>Y{#_7RkAS4W_ zC*fZ-&iwL(VQZz?A%>Qg{FL-u6X*P2jdK6fEM39I76&0^sDj21RRsYB1$`TZolb{U zpu6smFz}9%vK@CC#N)PMl4dK6`<_KkaRD840mG>@WH>5PX-8@`0ErOiW5p{?fhDd{ zTIppD?R_;Q)6aBXH< z0{3$DD0_W&g-|(zqM~EJVmUZRIRhYc9pWJ(C#=v(eW#6C#r^O7jUH%1^8dE-&!GNNn3ef7cJB7u?Jsz@{)m-h|PF&2ZI73&Ia#(Z77Pu4gbh-i)udg$g zfSSVqtvNJXaBt4z+1`(egk!?OKjHK_J_WPBNIXH&d=_1k1^M~zt3H6MY?p69G8+a3 zTR(}4!j?0L@89qqK4azf z#H$!clk*As2A#<4Gg6gD4px8PL#4>S>)$!C>;6*Wo>awsK|UoPq5(&GuSW2_Sg-JX zl(N-}M1JV_!G$TEyVB7K6aNsI{gRfe51LLshiW9`KWl#gls=kbu=jp2knuD@q!%Q2 zNRq-ppqp?6X4i>u%<-@8gk)moIN;!hv?2*oWVD5ajN(EAu+3$cVR=AO;VC-`&rVV; zI|~Mq(+=fpR;XLZRCJ$V6RX&ignPdsR-(!V*-m%T^%e9|bp#(O<6FuM)*WW=2FPo zGaFIRmcXQ&Fzv**qs zYuJLlBT#)8TU_itoWAlxNKUT%F!0Yjj_E8mIY*f76AMzWVLSocR|E;OdgX?^ z>xCJUpq=oObDS7mfbs7n6hHPB2&LKR93FmGzUSpfj3RvyfBf`xO}TE;qr1BDsgDn= zsKI4rz5e^2r=ZB4i#Ru3yATifjlSRQLHWUIYQ>NM^hNaPgh-`8*N+t&SQnsbaeDE# z=ZBC7e}X9WaiXS++0M1<1j201th>i2L(*m6sn(>&w0CsxPet{4UxT0FMfLZoS#yzm zx3^Hi%vG$7#i2n)VR-PEYQv^@6@Tq==_6nNs~DMns2315zYBEES5lIAg6lMl0-#3l zZyh}vLUBi`lJbWJW`mNJ4G-~+-u&}mX}tvAFfb0*1JxYf1q5IyXxz7~0L??D*LOy_ zR&>yCp_yI48MFqYDM_g4e52Cp#F|xosDmmCoCYb*pa_L{WeG^Eb10rX#Gh~RWe-eA zNr|+6-N3i1bccF=8}uWdGugFac=$WM0jIzgiq7cQaetp>!q5hlhxkVf@V&5d_y?VS zPr#^_!cxK5{WyTio;+#V^5~IaJ^_u(yOjja(~Z(g+GeBH9trRii{ovxq#-xPzyiv0 z{L0k9c$My;2_NbO^f00XgzcT!azvuK|6>+)+m5RPFmu}ao-Q<8d8^X zF2vQSZYV!{W*9RDT?9$z3jDLYN*7bv@!E#mB*gVmB-|=&PP*|hNKB{u&o5v1z>0m4 zWG(`h9BUS@Wf?=&5d{RGD`~85(OpsI9xc^SJ3`cqS$VZY>U!zK`sLHjCqf>%>|E^4 ziSj&*FP<|RM&Wg?iB`K+yru^W$p<@&PSWm}>+aA#uy5DHod^DH7PunKIV-Ji$>TUN zqUEa={1>iVIRn(J@A_|Zz^Bz`CR$UVD%3b~Bi$$~x+ikcHeY{*L(Wd)zyM6a<_SO>j z4hR82Lk!NPm8(4RuEA(%7&^lJW<_S@Fsyi}SnW|LlNuPofOPvAZR8+f;Sa+QWWkO7 z^pez5%L>J6h5%FK;zkpqsCq%=1SVZ^02bGmXQwk&ynY{sbx*TK2e)G*Jhbp)$Qt8- z_l*3u6i~lp4!~eJ6#QQDJ?ULB%wh~D=|=eLH)%`QN-k)KZF%KBJf+`Ef6o;37S&$Ie8>E&yMPt^U#0EQmm_-cMP7L~@wx$& zrL}zDb7#m8fx>ANZgB{?M^Sp1cD#|aeG-YuBd@^7D6%|^x@qOL&~_-_;^WVIc@;C- z+Snw+=&Zpw{7&_uc8m&{-yKMMA-BuP2d-a{hXLw!S+4Q1puGJ2VOROn%C|r^mo33* zuth#WD9HTf19!&{7<~STy?0t#cTHe^rOEAx9S;s>2Ui>1TY={epaz7OsGWOQRBHmY z9vqHgVYINv?Oh3|ZAFt*BF zi~x>Mz};?upy)SoV8E2D0G`g@53W#;GUL+#$c*?lD!Kk+S?YGh+3RtglI$DP{iKEV7Ol}_ z0`5*a5x%^C;0{eEzNE5{flfUJ5I_8}*TGTM7$zqJS%F$PN{GM_y=mdR2+{S^M+mQ?BE(e|JG z!+Y{V#_cZ3EO*MVYf`z<5mvsA`~onA$_+TDvfsXd1v{T?{d0O$pFX(EtF^;2UxqS3 z;~a{=QXx@1tlcvT1x`iT#5O*&HRyN>VPInzUd(?@3m%jq%#a7`;bNzZl+-WC@IE8b zEgEa3r$Ta4f+bIcfK^NFdKH}{(z7N`j;d32U49v`wm(ETIOh-Bz{LgY*J9&hm;%%`n7X6_L*I; z`ma7GwVPh?CUXf(9kMYaU&+-(CGY5!0e=;Wv}pM5(?Rqkd1^`}FsPt3`>E!*zawc!-dufR$VI<2Jo@KTGapyk@;Vxa!M95gEe7LjMX$yv; zu}Ry-R(zjyU8nHmOi3~^c!c%nt_ESBKjeowK~ghkfZKEj7?ja*ERkFWDeMqX(A&Bq zJf&XV`um->FYllhusG=IS2qKE3jmDJgQo+hL~+5KsYM~zX~s94k8?B zb%=xl-Q5%SPO>(NfB5_QZ^-n&N>7U2OZ~Te#d>=tq_h80$$&5bZXJqk^R^j@!A}!b z`^&i%+)=>LjwRS)6dDAwbg=uP_ULV)xE?t`{3y*S4^3i=+!fhs&9VXdZb$W?xAU}wuTW$woj+loS{ zM8GI+TMhFue1dk)%!haAU<)+ln=`z?Oc~Zl-yHud)r)$WSXCQPapqr*$E0>N3C|$H zqWB~({nZXp}3$e`0d+oS3~+mvBUd|y2+=u7rX=Q_CATfw;D z`i5ptRz}!o;2k(NZN_#`Q*8OWiq-Ju5gjsrZQy`PsX2W3u9<>F$%u!91K z!&S}A4}y8#0%XD{P$0MrxM5&MG(@^})|r=&TZ)li!YQe`#H|aPaa~~8_sKSQpTXWK zP6+I6uL&ExN6FwkKX?)}k>9vIdd=fEkhambGegHUg}{N+?W*?4%{qp&oVC~?HS{QO=&Ogs4NElLq>fzUZ*dye749HM z0_4LV83{yUzDm&Zn9WLS29GTlEw}s+s(q}iSU}2=oG{Gb+O@1Z1+mALqs?ZNi+Rs& z++-)EL(?oV036?_KrsaVDO`(|V;0OLaKRU5s z2&X6=gJiNt?kN3}RoMPNqLcsk1{$hMCggqQbM6O>{w3f+@1Po-=eDx^=*@9naPX0d zmH8bEx`UOaWQ?>rVeFPO@D|AuW|+#2>6$4=$0xn5)NqIk2qh_e%+7AzSz5j0qMOsx zb8Wy}Wri5CC%y6Q@Ih)&P&64hr!Dm!s0#C)+Y-E6_$^9*pT&2M_rfW_@<*)7o;}+iRucI#rNF7ra(CE7I^Q}by_`fp>FsaHtvi)pCzW z&5CjhprYA62g(rmF#tG#poo(WqQ>Bp46%<6xF)yL9|XUEQX*nfM1>9U+IZ^#IaU93 z?o3{+-~Ly9SMV}rrg~ZK&D-2r9KKI4sd)v4clpn3tl+Q42pfJzv())f8m6nPDw~Fs z8QqWVjf$NT#?@)loq9++V}~i#Fd<9Y0%r3hgR}HYC@V_Y^55#twZ02G*W$7JWjj*Q zB)CgKf-g>rVgc#`;V>VCp~zBGCZ0p&Feji8&JAcs${CHa%ubShpUqdoKLe-RA*cvV z?}8=`+$aN)FjoN$VrAdGpMjKMCkSKhzij)i$~p}|O7B%)87#*EQ~ zP=+QlWS&JrGL!~=jHQg3=Xpq`LdZO&5S38IOk0GIIb+y`Oc}~NKlh8i=XcIozwJ+95w@%#aC_~?zzqdN1&T90yA{Q-s1GJ9*@?( z64mF+(1?p%DBB^8aSLxXq!#n&*U=}js!hn}KpzwQ68Jf>3bG+_S`Qi*28*Z!E;-&d zvLk_o6s?0IGlvYRXKxTi<&i!Jy#1x6j}U4xqqEn^M~I;-zhNy`HgBIP<|59U^wmHz z!+laKqIv(7)hPUsNd7*8mbL}^_7RIovcKJbu)x{&W9)X@8;82t7H2aoBwvQ(6LNHD z%Go&e$)qSeSS(5dq6byAV9*mJPqq@t7xfbB4agE62uVIJELZymz0f=8j$X_{Bj{6j zVjBG6a4lr3$O-0tOZT^5b3>Rn-pqb|VM`RDDL>hLfQyBYjQ)%OfLE}&5!UZZf$n_> zP7k-66H!+@9-s&Hm(dkaEErl7#zr z49Fq@Gj&6NPEQHMlmKV@iM?1eNt;=Z)SipqvF+LU$4tVFR_nOj5g_5+-;1;A^#hhg_fn_UsJe}9+1b-Jzv}pvGAmaC5BRvc4ZK^H{jo!& zzhuq5+2&o~-KMTJn>4hW zj82|vcD0&vxEw|V#h^?Ti0Uw z$(#q=_F4F>kT$c_aF-i&J}CIDVrBm=Is8Y?a*PcR&TF58rH&)o{ARg*8;&Qt11u0F zTx;h9ucvmX0zZdz9=%C`kGO<)iWl#1r_FQH+9B;eM^mj!^b5O;W8ipVE#`qMr*#Qe zyzt@qp#~-}Ma(}Bu}TMFt^s4>dnd6-mUQT`TR`<7nN&)Ayb5T5_5+<2M;)2!`!YHH9LyVeQL`>UuBTc)`|8)eO@I z$qIv$4^5{BG6$@j@`6B?3`J6zCNF>IiEciLJ_mgu++ob%yb&EHQ30G zo}bDkW{n4U)Brri9^=7!p|8~5BC9K2?>kUWI^&zV6rzjb32kt=Gv@+NgK!U`DLQ=%&-YX%46Nc`|Nm-sf9N2R4>I zAF~`L%QUj===O@SBAvfJXInBo9uibL;FP$29%Z!v6V*Zv)7MIpFI&Wb_q>J~0VuKo z;P=hw%J^5HU_CO5+f)^Szrt+UU-#oz5J(<~(oqN3+-%PPhh#CYB@hd8-UEEM>6&2x z{4sV2plqkJBNH}$g_{>Qrbnm2naNGg+mn=*^*+@)xD^vi^U(~2Bl#_R#jU))eNaAn z{Z{+!N@`J;GiOqT(v8I^wlWKYDx zFqUAiExXWz5tcjN7PKUwf7pgvlQDh99Z&09t!+UQbH_5vse8bfZ`}CzUj>TtW3yn= z{SlNkukri@3vE^5%S1HTR+xvXc7BsD^;r19iaR)ckYRN4VjNtU><7`0xrFi=l@^$p znMao1;)-BgrYv-?DyU%4p1qeY1Bi~{UO)ITI?SvCavgjOc*Y-ZTsDceJ+>+EZ zkGjZZbE2Hp;R`@w${Mo8w7P&*8(LoMuB>umZY#VFwjDg}Z9g3+X&;+8yRmkka2Sir zjR+^p8~mL6`kPg;t>yP`!e9h~-C7d&+APUGZIDpiemaYb zmKz$csK~AYkw~;Y&~9uFcx2t>+dd#ChK3ZVTrRYa=A6pJ9UHCq%JT~DcpEHZ?ea;Z zT8kcq5+4OtK|mx9{1^c~2DgjH)KmC`idhE$J){+WEcDUN^@!yS zOtJSEx4~_&th$I)GRK)gsPPHXD_gT5(_tLxu)xg|xr-SLgAM3m2ms{oc<&fFKied4 z*a)3sHKF>!Rx}{FMxR0sf(9h8)q8v4;seyHL-%OK#Z0Lh!}EQFZKveQN@=SnO}G-b zAOd9-68~M&-s08&hz9&8hLd_%y%N!&jPdHI7BBO?F3`ZmQ|ah90BV2C{oXuZBzW@# zV&2g5pnRmNOSX2}@4=yba z0gmZ281C<%lDtgjWxnuq4)?n3>4mr=tHDd4=Ge)%7JLW(^gc2WbFfacU=*QK0=&tA zyoluP;XU?jQt#hd|HD#b-K{S)$Kp%w9qy-{6kAb_ePjgP*$w{&?(Q+?E3@3%iEkbz z4$IzoVjvdNX*(Ni+w^wj4Bl;~s1|9FC#Cif(fz@Be4dl4bt4EUyQdn?%GZb4Y(BBf za?AoU-oOHWjT_`Or_Y>WKYKP5j3k(;fp9c~sSQv|ApR-H@>@x}?1*sK4J;W@r`VET z@0uPr!hnJuC{v$kNC1Qg<pIdR~ze}LtzNXuDX-k)15!*gywzpvr=PbqJG z!Y2a<1#VJDe4$gaU7Wb$^P*jYkB1KzC@H3-<*ImL)bFpzB7*#qva;d#j}I?`b@#N} zI%dwzn&KxVJ_DLEFp`+x5f=_X@6K)NfP`#s#x$d z`#(!bvBLRQKT>Thycm4Yfn?-~A`rLB&i!2>XRzDLW(NNK32?G~1@@9CKL~Xa&DDnqQW6z&|T)xLf z0IFW;piv41^4SKEOJrupu?$Ur0>}+d z5mijIDfJG&A10km7Brzjr3n!nL8%bP73yR)xD26s-Er3|fQm+jXP*Q(;6t8h+hYNg z@4EPGf!+&=>2?)bpjuEA&#{ucua!#FO&9}>Yp9(0D`8g16uPLcuHIG$Xcq^}?(dYO zr5z{AzSshV#bd0#fh=N#)(_k_3n&6co~R%C5X_V4=z}&ItNK+(OhJyGLp+^g>FvqD z!0;mJ#$EKoN<;!_2L>Ba(UACm6nMbp4!*YGi(K6_SpDj^{O z12{7>$3H&+M&{wbCGL!Xs%DL51J`-Ka?qCGP)t9Y4gepMsu1#(gM9SEZ7=DVv#{Mi zEoLB(t_x%HXH;d#b43p%)(T}*KTc9YvdZI+^7b0%hL=b(iMIR-a>zoS$LpFP+X(IF z-+{!etf}lBHVVXSu#lj(1_Wn6Vs7WSyCRf+KgI3+-L0=Q$2x7Bc%`%^N;D#Pclb!R zLL5`&qcg6|TAX5*AN_iz??|A>fZcwtS<%|#!zt;QX~*7wH`Ru|<$4Q-&A~=pjfXz? z8K^{sNI_B4ast9I$dja8>3zTs)5z%_jNm{C4hk+oLp9p7TRjIEC4_(eHNRu%xZ%@tlE6~Nk+{JpE_JzrOhdDvk#!b@eTJiMXx zMwcl0CR%ea*9|VFjJQM;(+>O(pqx+PcC9yEfb>OBws$ueTerT|D8_Mv6IRPg<%%>1 z(v`HNNH;-x?GYJ|lB!BLC!tUd*fa*UH|&slq)iF6{v3isG*S^RH4f^3P|FDrp*VP8 zlmYxgN+j>Ce53l+8n$h^5upbOx3zW@38hf8{ejTPVRB^DdcW_v%4?{&CC!i;`0uX> z{r{wt`;Xe^oINCw-e3j8AYYs4!}jS^Y!HCu&mqn*Yr#bdgIWV1+(SdVz3#w>!)Wly z6#o8#zO4J#x*})LO8f&{*ypU*uV3Sk6Dg9Qq7OyFqY`eYt1u1IRX9148(rb5~1JqD&Ur+Il{ zg+2le4HGqhHXor0#L_69hVPD9y_wis?0b7A7b*TArx$cpC@zJW8Ui=loHE}gcUX-M z=#lxBBFhn&zr08Q3LHQ3oBv+pfn5bWtZm4lqkYDc8SkWje7R?y*RTW3J8Hm+dNUfv zWgx#x>JaiI^;g@&+u5b@z?a8ED8Y^`Fd^2g1iiVl)gF`$h*3mCi;Yk_LFoXdREFU9 zaq$5{sxfoHr^W^tH$Z+Aj;n!sqXt&o7IgK|QeelydEx}=Vm2W7g1B<02c!8mM-6L( z_Ul3>%|*f&(P1E?`N1X&jTbD9jg3`e_3gg|rx;Zg6ARMD2?28i2+U?QjOw7fC4%+; z%?;f#P)?(p3djP8->W)yBQ|l}VaqS?_sfuM^7m6*>=iI{F*ZIyK{4*d0%&(yX(?l1 z&F%Z&NV+AmSi{Mlk(!#Wc;NEAa>YZJ{wZ7$$vb5?Nwx&DhIBnoph$V)+c{Y~X6LwA ze%D#ofAfYw(?q(5}0ooqvf_FeYeKw9YmukB=7Uf9c86gQkaYeE zAfG5gD-;ivrOQ+SbnuC+zI&W_|o$yQMCr$Ss>S`ej z+{eT`j)bV_Xhozn$scsRa>tn(d+DP~B|*!$!f%k_%7k@JF|mC|W+nZb&HnKb8*QU? zSTc7_5~Cgu@Gj6PIrZ=}ygAzXeg)?5l9c`jT8*GZLjbsdGHuG@K25Qfy))@Q0#C-=jL1lS?Z@&SN(^3-9rClN}b;qNwtkr{P1-@>(ifnTx-J2c(>rN5sXJc9xl zz^34S*9;t#&Mp+bTgjMotfEWow&~7Oe2fh}b!J8e7eQi8#9Y#0f)1=!OsV2%lPLZ? zy)Jh<{SRf}zzmHGTMFC|*P^oJtb$4ydfT>|cQ5S=ZdLcRt9*vp&@?$a&*ABDovc3t z$7Vzv+J2fAFogS^oLDUkatI#(m}df7j?i}}YW`0#$`{+`NZVK6D3*co~h z`Y$bjw=SU4K+3X5+?0}<0;CD&i4v8m^uY)=28L=NHuw?4`I+Gh=bR52u3H|6Er0S0@sb6o^b9jqLLQ9|As;5H9ki8^ebz8MG_hgb}=2 zU-<>*I2*MK&7*Ct02{@4L$1@UVyomqtrM7(29thQFhz`vFn|Io&=T$)g#KI^2jvn9 z%-vN=N+zohe?J9OX{_(pl=i z?7m$Omt$qk(O(;jC+0n_F+ddxzb4QB8;J^2;Ub1xx>PSW&(W+o-4uoyk!!)@FIjp5 zQERTWf2wojN~5){|DQ(%&M+MtUMr(lMPyB*^CJ#au?`V?QqiC!gKgZA1L>8r^Gy)q zu{VMMRrh)A_l>@0u-p|LI=IsMPyxmMYVu(n?aj3 z-HU&7=hvWXobL7H)g9-hm|3Ao9?rYN`$L~x=r*Q!;K%Wp3A_I}`STX4;dZP^(iKJ) zu8X55&owNtC@QkB?58^r$VNxPawtNP;TR?8ppHrQbtZ$?pZd$P>5clFw{LayRQaxh zcw}QqYCKzK{G0u>{kp#CdQGP3+3K15!Jr@sG^SU9$$7$VcMU8w)~913nsH`BbiYZc)Q~odJT}j3EgUW&k+4VxAaskN%;0)Y+v$ zT6qQbp=~|OVe{SsGp%moIgoNQ3Y%Zs+=@A?gQWc6CZm@U49jRtqI%>0oAf`H&rk#- z7}lN9cO~Gl=o5P74oGjEZwg_{GGUzp{{>1NJ+GgTUo2G3tul2`^i~wSR`{LHe$VeF z`T~B3Jf+az{bKh&N%DgXlWQM7q`44DT9Qq?Q~4Outo<(m0w~n&L@XAUnWXuw#C;kk zCqy570VWj{9`?2#S5;ZDUv>4S<*YVy;9J&>kzK4Qnes6O9D71Wkv(FzHg+=p>`Y*< zyE&2P#`t`#RQ*lnN4L#VWR)VHDp zY&)y<3e>59`hy*wYNb(A;Bb^zxif~XboIw0Kt$EEZnr;I#Gv5n3+&0I1puDK!BARU4QmVtb`iYR3iM zJVY)MqG={Vr`5LB@LNF46F?G7ExbD)2oBVbcs~S{+K&hHS3(d4UFgLH2D9OrV_WuX z8u%Si2=WHMf>)X~fJ-WZpX`&lURYU;yDMw4yG?!m;M}z^3{0J$yW8+YQ$aYqVZ^LI4yOFrcb1Cxa8`B*NwZDsEXzKF(rM<7OM%V?Dq6LDv z57-50^!%ql#nof$!pkqHSiFU2SPSKm9r9REJ9FmhT>LT^Iyi}o{$#-`n(>zU_ezwm zIjqiq=Up*dnH|JHhZF_T2oL+gid;}1RQ9E+sVtH|YK{!PG~6)elmqL8*j2!zd<`u} zVWnqG8$6w6N?#ubhwE&)c4c@GSGUg+J;#5PCu3Jp{MAIf!#ktqnvMCr{B>~At31Au zY~kq2sMM$AY=6PQt?Ac{Kc>`Gk}KFsD&>|1lAn$rF*izmFVvdKq0)HVWcLlaQM>S~ z4Y>Ox06_q5C~YpN^>Kxfm<^614{g$GFU`v63_+fo7McUl8Hma;po)Rf8G$b3u(l;! z^M`J7u2Tg+3gD$6*ehz**tbe62{GOKR$`mR*aS{qP!OAJG)sF173VHvVQkr%u@j0v zNby54nnxYGuA~)#Vb6+zf9aCM$~&S;&^rg44+R*U&%q$$o;0;?5J2}Pt9l)XOT6Yx z&rhsPb5ypp6e$I%}mhd)gl@qkdTan6QqgF9eoeGB?P)jaTtBH(FBN z;Jd7H)j<3KI*qTMFu(+um|9PKQIuVXZ$*}Z-c?{20IK!bA>A`Qhtjjza7QVBVf9tb z%V@+VAydQ{RC2!?0f|6SA;1TVgXOgXi@xjFK^Xh^1nn-emX)^z3qE47vz|5kMoYxp zQSgN%fSz-NfgxJK$TH9;TE$alpKj754(d^GdeZ%Z724oXo|2 zmVM*y+W^^T7dh#JZ@$&e_XEI3033+IWVcr}17(4mMPn5JfJ23OB-h%~attYn*JUD! z5=5<8!RYcv)+`81U#6wq+?lu(NqUT-94(G>YYH;F5D54Z9LVBUH$Xmf?-{3y_uwO$ zR19RuF@ZDfz(_PgoPdZ0!3e))e}QHo>=;n>@uIX#a9vWcgz9oe&Cl8LO?pS7z!xr6 zKWDUO?@7W_h@K{%b8}wf;-{&3amkv z!2KON$sMfRxU25&l8`Ktw!%VRzYmW5k#Ww)(p?wl-=BK=CN@+UT5Ld~)T%{SAZC$~>QblDf%uXOINljO8b;mHt|pw!@5`%B|p(y z6#0Fh;#2a4Mp)(_s2`uUXAHg|KceQr%cb(EX7`Rt-xiWq+ma$oLIq_D+SNcNFTm>l z%-7J4OXta~Y9_2RJ-ZD)2p%&Q29&}mw&N!ZtNoa<_LekBvb~r!6QRV4!t*eEKRJ4> z)){QNQT{JjuE)`;R4f z`p8oR6Zq%geb%#_TcSxr7~{W9~8#fTuuYR(3(c*Ml!Vk9rpL#c@3li{6U=dhY%;%%TDd?Z{ zrYN?Z)LA>l{A_24^)(i3cPM{NSTwIeA-nkyvVv=eb8o%R^x3@_-CqB5A@9B82ek{F zDxYq%R#&yU>jbQLH{AN#RxFUgt+Rf7nR(%WyLIE7y-+3zNzm4yc!)Fv_}YI{r97O# znf)>eO2kM`vtzv^-{LcK4vEc{jD#7%Ficti#0rHJ6%~2Zck3uA%Vvi4rAm@VS_Zd} zg+crTnmzs92o426CO!=p)4HXy-m8HPz%=>%^j{ailf?5%KuA&mE$#RZNn`TXMA2|4 zRa~}!taF{rh;?IfK9LCD^A`S;A+=_#rU9u3VC4J|&aN2<1weamZv*&9p|LClOR#Ka z0=!L}Qi^uln-F(k>4k6A@7z3&Py&rqIAAebX#r@idY({? zUVI?@pvx&lVew#e28~SUO%Y+?B@@4uO6|#D!OxL|WQYiWV_@G`M@|=HZg>tjS(h!a zKeS6G=+SdaU!nFurh72@DA_f?*s0M8uMVi<(uwDfqd0WHB*y{MHXK?l86w2bsFM5K~4*2FWvO7b&&j4WFGxcmHfXns3RI{vex!>p;N~+6_|j zoGQvTsGIXE|G4>4Pwx9}YDkNRi9UI1fLbrSxB`uCR#p~yA&Z1V)<~%?b$R@c503cy zKl^RizS6(Z+SL7}9eIs-Z^K-@()+=Y8)m81dOWNW)~~4y`_7*7fl58yf{$b#JvS*=gmvj5IjoBZXk;WB?y;XBsx*cPMP0Ya zj5Hdky^-hj)=}fb%veCSTg+Z2A2LUl)sn^|nM;61B*Kzg-2nIHJmBYidhA$CX^)Zu zYdaRg{gzxHgE=Tq)SK43!V^+<->T19-o~xw3#2qJ%G5|FbG?+yZ+W z<(WIzjjLuP$TbgIv)_%dL%hhjt(mYegN4Rseni90kjrbX9vE6K(hQs#AglFnyXXVH zujqm_KXUG_GIV(`EmOwxfbPBQP3*Efw;c3Yv8ii~(cFCnW(=s~s~gBKsDlv*7|3!f zm#N{orxNM*<^Omag$yC%Lu+f=itubqn0ilWFVkxrWDwe$-g*Jtkb`t!VnHo73mTW<@1?V83AR1W+^zjclNIF-XzyN6B<*tZ?$1QWMo5B+B~zZS7&Tt|`BQv$ zC+cO1U^MrFROS@Xry2ksp{*>PW^+-{hQ^P_DB<3a&UG$5qU zVay;YV9QdyVnEu3(G%eSqa1R$SNB>UR&xt0tFYEnU*3z5iyTq?Hc#WnSO>i$GJ0?P z=pAsPrC;qH=3C9R6(YaUxhL?yf#8v+W_h0j&FcvB+w=ToYjD!ajc2;D02Mi635}%x zUPJup{yz!pw3sSsIQwsv@NaU3>sXaOMEiU#>@}ZET&CB#yix!_RrxA}x{#6GACMl{ z)iIjbY_E+6_FI+Bh=46rM5=O)J{Fy(tKmDV;hHsw=g3q`6obMnPT1l`)(j{;TgUtRpYO71 zy=|&mIaVo1NoSCGwf{k{`v_WL=~Cg@BFo~c3!CDR=|}DM8jxyJ1{<9i`|c&BW8w}z zi$)TMUB+PxLZY#G)^{i*P@xQ1`s>~jTcbMKB)ArVs-^wP)oRu9;%hn^nBI!b)93M} z0f?js>B5i!X_r+44I46gLZ1Dgf=(*}U*5KF-;&$~`ZwK2?`e#yG>;>QwnGPmKy0wMpH)@%MjnhaXBvCK$pcl7pi@LU}yZ2X=HpVd4JZ3*bRRyJTiz@d6^O30{QA%>Z>4{y)7& z;Tgb8>I0=n1I|aTg$5j4{kZRG@LnywL$^#g@xaBXGX$h)!{_1d*rn-Fii7h zRu9SqP^CBzznDps#>boWZ?mp;LH#VYwK%_Hvf9_`brB5gAyhq(QQFO++zUQfRE4+b zJkqOFp_8Ycx-y1REE%t#UWWiW$3I>Xcx{xS#e zw>Y)$YeGvk*Z?ZV4z5bLV&6~Z0a*gX4_(Si8@Y8< z)aoc;V?jNjuO8!4R=H|x8zsC45~%z~z~I6I)MarY39xB!0B|YyBs`BknJoKr#n4Ed zX!-%T8NpoRlhq9v!N62!>a*0dpv%Hmj6N>qXQYlVM`@VgE$C=tSI^ylbEp{dk=y8D z<<+j#s|shf6R+6t+3R@?QoXI;aFt)`Qr&0>HTIi#kbZM>m`cf%axaHmohIm*?&7cdU|%~ zX{J>8K>{Vok)GysfSs097X&Y;T$K^q?errhESo!0oL;^KMOdJ5!^IcvzVGsTMEv5D zJ~#U26AOw(IY%&4Q02l-d>3&O6oIfi)`A54j&}@)2g|vF?jMldv;e4(@0BB%HC6@+ zW7x&=!!=uQ+8lUx7hD~lMmugZ4tI3O1C9Wb^q|%d>iP}{Hf*yi1ou-aQ)B?pOy;Cf zl^j?-VsoOpXH_5Th1(>~QXM`B*8HuX^tpXr`VROGW49fTsW4scIIHyJtL#>%tSxlw zb;E3s0Res3C})<;dZPYf>HgJ5a4~m*u_6|8G;aLv?5zlHZ}zRN5{8l=bKA3XF0JlE z4CXMC;<@y6&WsPcD(tn2`DIV(&WZm2)GKv^b1KIKj70Xngab zq0{Vq`g%6;dq-z%)73>u%;w>FMn5iQufZNeE|oLJFywUrvK7LsU{q~Z?9H)Nw&&SA zCs$*>$O#@aT>CQyt;+E(#QcTT@L?pcE)MS7lnT%PboSOe&go3^&rpa|qwTM$S6=~+ zL1o(IF1F<|w>jSZsjQ9XM)9n|!pR$Bmcx}Smd}v4!;Egq=<1{ViT#^=9UbhRFcD_6*e)4ejrO0dPUW$2XX77&U4@38| zIv*%|)EuUJMYW4~z8=0y`dAGg4-C~voKYBsa5)%#Lze>!453>+NPl_fQ^`h5L!Wp7rtkM`xMMgdez?a&n`rvN8k@F8p^}A~i7qtvMLO>UI5=}zGr6i2hBQP-_ z;Pguy=1iH*Rh?;3L#{e|KiH;_gpGduO&OXdN2lX1&)KHib`1OPXh-|y1EQ& z2sob}eFk6E(aox1;OGRvE6imJtVcEktKAI%Bfj@+6v!Ze=4!cN`276|7nA0=YCuKg zJxP`QRyNG0Ot7h9Q&}%u=G=#w&0!DW;KTqMHQ!xQ0^X{k5Yrg!q&r<%EqtzL4Jyky zs&x*QIa!yhfA`jpT#UK1`=(A+ubb!6y@}s=%zQ=l8BRzO1?c2cd#R+ z>!ke7EfxOPCB=d+N;r3WrU*`z4xO8VdHJ!PY^}GCMy6ZwLz((u_@)oD?@QmNiElG* zBGtbJ=ZFqt*x03T&Kkc8Ll)_spQ@|Vt<)Of%iQ{$_uEKkJO7RWShcl+xXV~t6(Ouc zU%0fN-=@yqd8tofqwy<=kF#(~=gl^opzcmGMGxOo#rz|O4)FusaWxzJJVKdM!u8Ln zX7<~+Vf35ZuQ*oEc)YT^ltx6x%5}&cTD2^?S{A4-X0G0`EECih-+3+YY~g47vjM%vq*Tz#w8AzHk_EMW?L=XXymU)42;VskzGQ==3L< zSjXr#mM$-+c$c11NR5;si828!^4CzZMD9=%9P?+`;g~nGNj&5}81p@;u6J61czV`p zlcX)5difLH)_$}DCc24PGbY%h3<*nNMvquG>kT8$> zm@fIY`xFRjwE}xUrYBSqRKFmzW0a|^S`7`^9-ipzvhn&kFjg3 zM3Tmf_+=cr+DBv4bwvs6twuE!fVfYonTk1qtlPuHf5vGlaMB-V3I%mq%@rkq4@tU$nUC6VD zXW5sPUhUpOQwF804b&iUF5zNLpQKmM@e*tao%lfVZnM`!-Pmx9EVldZgf~Mq)es;3n^MUH zLzA%`qFGGXD7OtfnKG!OASG^{*HNIJXd`NaZgW;IdANZHh^zaHE282BY8t@OC&l9}b6ON>5fEz4G)Yio_OVe5k16T*AxJZEjDg z^>xtx9_$TVbxc51nZsCK9`(hT=aw=|9RH!#VooGRi!sCH@VDQ|b*!<_{zl-I(+ zWw+q_KTzoE6fG)Q9VRzU&wOM}T0I=eOzs`^l1Z!xPz6+QNuZ$@KyE?wjP$dGAV-Qa z6}m)Ip*%H49*7ILi#;@GTIN3s+f+qOb zR`F!=)U-0I#{lz%z)|reul32aBADlh0ElMkbT8-0vH5?#`ZHAOfs<^sV5^kiwUs7= z?Z}vzL#S9XZk=tW1urh!uh(ym@s~fYWCL`PlIODmPh`53-T%I#{`b5E(=PQQgpr2( z1>I)9S3o1cV<>yWyy_b<^C2J)KI}XS$n}jc2#cz3VbJ65u&&NEMmRy4SdKQu7o? zxssX~d6RS~NMbHNK5`xoxvcMS${@u!80N);KscZu4ZmYrq*rty$8KZ^<}_{tGPo8d zp2&fL5;)1BLnSSsN8kOdjG%b(NSI_8$gEa3GBZyg@dQkKf~PKda+&$pSF5e^k?ASu z&Vdz*YvQA1k>i^o1vmZgK)TTgztJs?gbzyK_D5plF9`?DrTXbn6L>&ikj{eA*5+C+Oe?3hnOd^1lSPKT*3fXGaFzCTnX~QI3xhQ)lRE zN6ckUm;{vPN}J)IoSXY46<#cIsHQ{8JM4e4AR!8;`30K9g}3%ApFo$#aFy&m9k^iz z_!M+phhIz;D%YRS?Ct#A^|GQuT2G5)a?bv^_o2vi?4Cw}^I!zbHQRv(Ghm9rvif5Z9liFljPtbge*vxy zFFZlV8ao&te*{cO-Vq4xDX}D~q^Qj4a|F3<_9iPp?NG&jttWqfAJmOZ|Ac^*#@i7a z_R0DJNQ0afiz+NGc3z90>w8b;1Eapc=tgVmKs#GE4WNY6WM{s>Ah?;g{P^@p!4mK} zmO$K{vovV2HfH=A5Zn0vGjY{|o6gT=?*`lAdqJ=H#u}yS^^#wfxa;t?&e>##dba#s z2YdLd%BS!A^VbrJKI0eBq$^banl+$nt%E~zfkj6oH%eyty>A0lm8VWm>O%dU<_j+k zq7KR)BXtLq#A9cBiD%NoH?$2u0xScwymzv0FHL=g>Ko?%Rlr2Hyb=s807rbx26&$) zjL95&HBjbkUH^ulAkFFvr-b0%miu)3@MP?n5}J}pI32%7rEcEM`VO*(dr;Ja+@x%W znLE1+%K)$6zW99O?sd({ApzHsQipF;XhyaDy*L_w$e zNP@*%3Sp1M7i<}jncm;uuW9E7ABA3wfwBk;IIBx9b!iSN&`NjZ-~E*NG?`Py+Uzel z1W)#_AP1dh2T$*j*YNeVblts{o-`PWG{-&94zO)Z@b#-Ai%)Vbvf0)4>a+2L`D4E0#VXthyxEQ(E?Zi?&S2OhVD@l;( z6_$!{X|2;-Rc~9gzEzyIuoKGc?_G-dIpG$W?nT(UKF@f0W0o__HkkN-gQ(L8WNE(} zfvvQgt5{i9y5F}+$tDfzqOMXM5u`G&4`N)F~X1wi0;!)>YrnD|t}Q!RW}#*zc;5 zq?CIP^V;T(x=UwLNlvr?NeCcpJ+~;7o|^RKvKm@d6sy-99fgq?K!F+vpC4d#fNXS~ zE2j0*iXcHd$W$mHRxYZXo;%xvDh{%)nm`vY5)Cc$`1Z!!OBk7KiUYhcQn=b3?zD5@ z`m4!?e(Zm-h5xf8u6TU!>cUPb9{UIMf;li(6bqp5sp`45uOwl7$&@~(a74t!#s=;? z|E=$mQGy+4MYcKPy?^S}9SFYgLGtZv*b09etGn9D`?!b$&X&LaQMKRUh~ON1l=uei z+YTK$n1oDIn23gB2~JbD&ZR7&rD#v`mrCchn%%`SFL#6%v){NHJSsu1a&_nLK6x+@ ze^);xx1x$@GOgYxLC8`TCu4^`0WgWw1nt^43?AXV>=7-SV^d#Wqt64BXE*1_E@^ee zViYKhKZ|e`da`N@Aqg5_Et)k4Z|X(}+|q?Qcynz*!4cw(cbWjsD}}+!)dnRQdh;?E zQS2?WxG5UQuEI32hjMlhVv>}34&@Lxk*9Z1fr(oS(2G}7AQ0xfii(MTS$Vls&+mJ7 z{0ce>nKDp%!$tX!iJswW_67C`-jV4e?%=B!JI%GN3*2?9_iNOu!SAaP=k`tm_*$`% zi)r_NDw@Imx^^RTNN@(maVG4nnr-o$qYFkUWKN@}%JuuenY9(}|E#x#g~CRukkbd@ zYx`dfRC&u{_`vxzc6)E%F&k>hciL8v-$FlX{qFwii(HhHEJ2(qQ=-n$>$N(T=E;&p zj8cMvZ0?;SilO7fZAgXVa6&=ZOUnG=nDX??)yJ* zWBf(jTe16ysx9k_5elD!i;kojq{-6VUW0f(a9Th@P3p2?x@&r zbjSe}6?W5V@I!Pxt2@t#A}oc^Fg&B+2t&_&sG)*G@HSFJtp7_9Q9)%8dF|AHf6}q{ zX+&_E$>|X5(t1b(bRM~Gg|)d7V=qJYYc^2Z?G;N9isd*%%O-Ofs&JT^;VhmRu1WSbamYM7xY!4ania z?CCp#L!eRpy$&yOwrXd4J47#N zy;EH>6yZ`EXnq_%i6l~9l}6dUV=nzEnvOHr`fBiS3TZ5!E6;cVzBZmpW(a{NX`|Mvs!eh#^Rr}Pz*QsKru-d1Suh(1+U53pmvbxE&4JPZl?0-A zHa&H865#+lBmZA~qR=Ozt(4IaSeoe8;!a)0U+IzMgG2El~Z%(m$XT*aN%< zKwJ80oWtR@Bcs}#q0(z^!CN0XmC!S_v)o_(kNe)1dC(9_TxH;{wXN&p!cf@0(JL4& z|Bt#?-P5-`GpV=X?^ZMJ_8aMU<0BIyR?l;Ki9Ip)HV%~*usWid|5ETsJ(Ml`|300& z2BFc{M2NJ!K`Ch0EKpy|-Sg&FO>E_2h8OwS*ACv}yqMW!X?GD+stcoVjiaEh1cnrD9OUn^0fO{5;A%EMf=`}9l->ZJ*Y8Kz z;!S_f?3MqX)2wqVX~EWY4iiM>Y4A-fF zW}Hx%xZHzTwfgTzNPo_ac4Co6mep+{O|u5-DXa?!&cWxeMTzrva{oEfpK&KJFcKHe zwn-pM3jifPE)T*6X;`WFj{hRP5Bl;8?^4-SI(&-AODfyWgA$gSy%k&dkVZbTOByM= z4wl9Glu@$y@0(@!W4uR;Q6mXc#MLPI9LHPx)a-s;Pacz1cG#5LhUZKX4I=RW2D(kIfyt+f;AO}&UG#AiWg&P3Npo)XxNWVcrhNguF^4 zgiZ7@I4nU&edG)NjG=8BuHDC$c$IQq8-3NL{)FI3PZ=?tDYd=@fHhS(C?~|ql1zcE2bVYN!` zayXP%9m>6fj$xnu5T!DGwlHLTX@31OmA<#yleU?X)D2%pVu}w2cPG$yry{%2?#kwj zOQatvCNjA@F(-H&J*TTQ&Bs@b+?Z{6HB;I|l8==#lWa?>?X2Z5OXDROrj&54wf$BN zmJMf~kdf27TR#!6Mz7v8sHJxBB?Y}la0l0@#|hGxf#kD;b3XYLOA5XZ^NSP^)#7<# z_k(Cmt2(~KMxf(#l{CI~{Y0Un&)NKf0AxS@=7piPEbHB z8nbg$vNAUi-W=&mQop`$(domNXTJ4LbE_=LFhr(5H%1if!NlMBJskMA=|x6HxqK!Q zRmYdz)OjCVty)v^Q;Xa_)O+;xqq}0mCUH&ybq(5sT>VF8PH@N_J0f3uq=hPyN@o z9c3Z*))jrSZn0JsQdlKgBYd%KP08(*?4mG#Gd-XY% zn{>?+p>+5CmTpPRO(vyR!bdnf+#%ezGf6eRaalPsBtcb3IWql@KnMX;B5cRBEd*|5 z=1#pX@nmpeo0gL_V+P0k$$3OfQ>rHY_&G^LJ@CU`wdq z%kSCnjqONUSm>R8=+EGSR@zOU;r-3OSZQ^sljP@RiDGcWB!@a3N#DZL$j;hD*>HNo z=k1(H=ehi-?K&oV9V{*d*w@wS_x$)L?p-7 z+^o|NZ?$RZ>b3ZA%bQ8Ng=1A>o0D*?y18`p+-axTqTUTMhV>12OVf99t($Dr?>w(M z`@I(y(+8>L4?%`{Kkv@qW$VML;<|qR-Quj*fWZ>kx2Yq#@i;WOO1egThf#8TZv8;= zdK1~?hg(;R{%Fd3!;8-zshouZB04zV-s$BetdY8m~G@zeDEpccF;i zP|4H3^YpD?&O6uR-PYMgLr~8J3C3+_K1Dw$3nJ zh(F>XF}p%5m}GDDE9u&Mn}3}U5yy!wW-t1B&nWJ=I<(X#?850h-A*QKMqT)K!Gy{% z_0Cy%kSt0zbNxP)6G@+Sh;$>`oE$>XYHJmeFd5(8#pUH~T371(`7X5&o9qG7N7t++ zc9gw+4P*2(BO9x@bd8vk6MtOr{d#xkwBDzaI(~;?m%Ltc7^=q+H+?u7R$)ytl$?gP zUuQ(`+^O9C&E1wNS!G-b-l-J3w~8I%xLeVXxx`7LD&i^f?Ggj|Jf}OZl|(nI9ZptL z!mIgufk5gP+VhW5t|*bwhxQ(mJM)Yhd8fy(`lWj_=aH5ijQbk84M)ZQl}qaX4?py2 zyTdcC8#4L?(ECPmVjB0VmZzUg30rtFpHlp_XDluZ+LW6VE=OHX-$>=#|4hnBH?qDJ zh(z?z(w~dcuX54N7-w@-F&r`G^yrRu-8>3^ls&KueWm-F^3L;UCTp&!5(Er6H1{1P zYbN6jDjX&$ST+dy``FX(&Jp)dB%R$*Fw_oMP$Jr)x!hu}yQiy1KZ7a&Q#N@yecSVP z4aKfVG#y+2P&NtEUDL3{O%h-FKST8-;jFnR5d+yjlIhZxhACtn>O`P z_PEoVI)r`CUNQGHh&))q$*I0c>JObBjJNSJ3t65#-5-eCA>zR%_~hVQeHO^;;wCKd z)Oc0mkwM~3A~8u--E5iSeI@&VS0Y;m&(uny$6rJ=cb@?#@Tc(lo^2ls>|vqh(I|0x z)j^z;IwsLaU(R;fbWYuGCB=V1i_3*U*81|VgtLrw^BcX6?D#K?EsyGCBnOMvB4t-x z2y!vgdhMaV|4xT7)mEyRe4I}@s%OvseRPz~SA&i-ijOm{J+UNhz>liu%w0d{+rJ!T zZKQ3m^Z!qESNhLp_Vy`LV>+s(9a=i3osg=cEkY(G-3AG&W364Is9mTvt*9=hwWM=f zDk#7H2(7jDUF;@E(o&5jVi$@;iKLdqbGI{}=P!8PJm<~*=A3hX&pF?7uIqCx=dfOMLy=4AV zGA$%9;#%xcX-4xoYLtg`y7r<$kX7c!55%b?xkS=$-BCj+w1j=K`{vbYFH?eP+qzg) zJO}1}B{Q@fWx)0Jq5aDQ-cRqYc~`zYE5$(QPe)Jog>bOaY}Si~>?_bnLVs|nirP!W z+0F)8FSzzrDE;r?((f4)HXJS|7S#=9jnfTn`dw%Jm3~|ez zWaO1k#LCCj#9w;u{(Rk_RpHJ+Fz2dt@2D4=t2**d^X2KatPw0+g3AUI9(5PLNs2-D zFw{zz&f6v>_9QX*B0@0Dqi`#$p~>9OXW1N6gEA1`RY?@n9Hy#le;fWaO9=i=SwKl# zK62-JWUenIF6VVYsLYtT&JFp}>jt!HKX^KasHM zFC)sXbxl=hdAc8cnq4@Q;?QUK6gm$4VEA4IDFs&EeDKX`CUfMeUvJE70>c+=kn@3R zWCB&@n&Rv@KUO<*fbh>;0bQKn_;2iDZ_T0$1SlU1lk;9-GK_SXyRy_R)M;O7+ zHi~%y?wo%j`z3k;rz%E1--3q4L2*h96CKJvl06h-zU(& zYQ9(g!Tbx#{FBIS>VuK{Q~&XG{lWj1#Rh9T}4zikwJT{%UJ-0pk)t z3*4pk{cEI00=l@pgr~!Hn#565m|1KgrZ-ng%VJ2NI3Z$OWHjgJ6bMO4i;sx%ib`9XYATuYoLNjo+2=jwsByh}Q3$@$CI9wfvXRtU)Z zbHucu8KtjiTRVG_urnAbSHnaIe`we8{1-+ zLttZ6AaJNM@4>vo_RssZXma$#M;qnlq1V+f&%lPfg=9X*$ZoKxRpN`l)p;%e``xp} zb-AdjK=u3W-Dvb9$x=t_A)r9B)%M84jMSTY-L-)}MHOm2)mDr_*z>`vnIDS9#AF_! zu3fgdNmA_y9DkT2)w*=;q(C=od_=L0Ub)L z&tVI)*%!UNfawT49ZrBcyaevK-a)tzENk8zm&ehbkLYi9iAEkVEC4gy3}Nw*tXSKr z=HX1{$6g<5kvjKs?E2#VGBz2QUuoh63DL-Ub;(GkvptKLY*xsx+#$ z;Oz8TTB^)wt#(I71aym(>Z!=e0{jhqM2`A}5d5B&>MQ>gJ+c2EgT_c;N#Khhbs2r}in3(n*DQHx9+cFVCfQ&w$s{?lL zHtxm1O9#Ve9?g2{vL4>|fKeP!AE8^<`@l!2Ao^+z29%=1+tI%R{QxulwjZ#H;+R%y zQ7RJj=TtpozWy!!@AjqgHD&%-=A009P;pT3I4#tcpry%epFwC*43CKnw}JtpxNsmJ z^cik$ES(gd)n5QHiMdNBrA)a*-iK1&OKiLTHCKfSheTbZsP4=iRP4gNtxq<4W4yY3 zqT#nyN-u%%j-Ja+X>z#tPt&Kva?J>UslOL}?x8%)(lZ!P$kFy1awRsxDMOmQESII_ z59uQTsM=~}gm*4;c;-CvE7qOMn(aG}(aggrEp%m-93BrSXm-2bLT(dzc>bO$g~02| z&RI7hurj<;lm{1=$Ix!%OvB-lLegGSI2|+j?v&2I{V*iCae2T+J9r^~ac^sBvU>H) zL2NNDxdlc^Q@6#9r!avzeQP;di&x67GoW4Izfi5|Xs=DlLfB+tET(i<-ShCKT9QLuSlvpkgXJ>%FXCbvw8F7OX6(gw={vD%U96 z>hp-Fc>wov`D2+%qZf0W)MuOk6vsRy3a}4T+z81|um+s<@yI#|ysC3E`o0zpzYqb1 zm&ge+d3ak4zzicRWS)l^w<#@aSI zUQ4s`ZRo@4z8L{s!Vp!?BQ%~`uTK|jcK2tX-MllQENV=6sOSXU8FfZyd1>VoGum8f?86 zK8pJ7U@(kWtQeq^K9)852-D4Qm&i%R++zVHMftYlozH)-H-s$o~!AxSSu^bW|WL% zLE4+(!n9XaZ|rY@myecAFm#eKj;a&Z1wGD_A>=~)Z9gNHTN43l4cxJ)>3yfodFM86 zd^-SDs>N-8#xi|52s+W@7&W_((5SNad}pBC z4zpK3$TtUq^u`YMu_)w}p*^P+OUIs#Ro>(VMYE#iW1-D8*o2*rUzzs~6+Q40Wt1`Z z&UnW;zM>iq#}(x&?EbOMMxsR86eMhnW?n}+Sa3^g|JE{k+^G_ZZPcLX54O6l(6;vO zWAlWg;tZyPj}n`#60Qa(zYmWD`i}=pqC++nh0q~Gka+T+B_zc%jHwWFz~8A$Oaffiijc@n4$_qVQE=e>v6gCj6UT?w*8W#oLg z^<(FUg;zvnZdfR3HRB+I_@qJcwiz(Fd8J6R^Ff%Lj z+katOzT2y&gW`Dv5-A?*V^iF*!#~9*H;`KF#2w8J)pqlv9Y_H%)xVr@!U9Ee@4xdSQozO3byHku!=HbA0IDnx{yAyogZOV}7&seGwjM&r$f+-(>dR7-LwCsK z>`8)tvYtjit|fmEwi0UicehV2H;i~MrP3!?MJ%?$bZwv5&|*zmt=FKJ98&RQxFCaZ z^V#IBhaY8EXBr?Xmg~<_&kSd7Se?nj$8`9G*_zdcp$3gq7BE_rc~r9H`Z!9YPH8~} zX7243e?7}fdZPBmHn=7l!jEVEND&pyaXl-47`7(~L&@QBF$(58|Ln4tSeQc=Dg-~@ zypShK@vpQ?Fzd^kppGmYFF8>5ri-rzowS3;IGVY+B!;NO+hMW@r3~eqIUrT0(pSk= zuNY`bTq;e=V8|+Vnjm-$%502!fN$cwjA5^DR$on0O;ivb*`8qsi7E+MWihg#i})k` zSWpc26vQcjDepn%>v>7F)HmgXrtIM!+yX$r=#Eo?E-5-$r(aU~Y!<}G>;+x`X1zfa!x@6%VQ30FZd3%0dR*7(XO)#tAxD78-6+PWD^6S+)jx9M5lQ+mHj80bh z3z*{?if#iBd+I;xf@=p#bDLt-$R^O~EYdrNv$z_kkBT`ZlrFnfE=-#`!E3Au!RI9| z@eZ?ZIiWqhs3BPiX`0w_|6|r!HNPfKyIkFlX8bA8p{3x9HSQa}6wR$lX<(_J_*w-4 zAO(MVta3`Mtvc$oXI?&|5VBP;VTihJzOM3Xc9o|}5{be5zCxemAEVy~w-6MR3~%3v zAIVC|PMxceF4KR8Vqm%Fb~IEtD45>N3DgVxQKRh^wH&c zb;0gkzD*G;YtBr4Ii=QCQ#*Y4lX;)%sb z=ZaqB`};=B-kWT)O7E^eB{U06oLODUWjLk@;&e1VXQe0`bm$fOd{do4)c@xtR()-c X)y4ezZaKQ^Yv-unuU{*>;vV-u+DB7E diff --git a/docs/reference/add_connectivity_penalties-3.png b/docs/reference/add_connectivity_penalties-3.png index d50ee6781dce0e558e56b854b31bedcfe0aab4b6..f8c72fc61a3e023697322a5873f40460a027f5a1 100644 GIT binary patch literal 44760 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

4Ri7Z{)fgBCAAG`7cw|w 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 literal 42288 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

    4i&G6~ z&`uV#T(1`D>3VWe`a#&IO$PdUUVW`2iN7o{AaCs4YD`mrYqK#s**L3!&f6=9!bX*X ztwi5Kk7pg;jZgNz^6vbWSPhr=1CY9!*pgS#@?lw0Zv)NVY;OqNX;*%s<#V;A*AP{k z;BJMVk@t(zUiQkL%g~Gmw>%^TvfhAOW?XYwp;b5KFdL*9o9jDP{mb&=29>_==2_KC)WR|nnS;Xtb(OO;FMuq+RAij!jqR+5cQ|c@=F#N=ry@MECnvp z=a?w)@}Y>Bmq=fn*nCL!y^XP^>Z^P)B4ZaN9v}ObEG?ZlQK#abcF*lMReK(v6f=o% ziB*!g__*Wg&;|dZZ=LQMKwi`om>j*xU6HOd|7dqNV0l2a1RMW{SI`yjBrD4iSdcAB zRwMpfqg_@;S6t}2PFnrBQL5CDr08H{ImHAfT{Chkjm>N9tzTzZyH@l*rUxd3-DwZv z(RnwbqRR)C8rUUylv2#O|6Or$jx*M~Xo{r~aX~?C>BCWAI-p^h6^Vr0;pm>$o1HxF ziU#nUe=e~=?Cp-Q&f&YRPSMdnR8blwR>obTCW2%hQfqxF_URXlZ~JFT8gpmR=FBX^ zU+r3jLdA9FvzcczI&^{>xU1?Div!m*d-i~cF{`J%s#LEJrfLzp-cVtKcVoY-nLn-j>Gte@)N;5`cy62RdCuos>R%kBs=wFgDzT2_ zg6%4UtG^KG5FHSifQ~7=ZzdR=_+DDO?eg3mhvjSKVI5Z0%8-R>Z8*>8%WgT&z;M{* z4`XjnHv~?o%=+FgHhU8E*U*EhsC}be%j`Clh-Fz$} z(pN|s!|f#2lrmPAmq-}PaB$<4Qzq*Xg46xZgO`<0=HjA_nJeS7TKsmuL}du(k@e-( zQVGe11r$dJ(Xn+gYF~pnOpU@Sdz`Z$n{(GpipZX~?{Bb))Lc?lS5HY+Q8c%a?D`hXH}zrwMpUu-A?m)b2f^15pN8OD$OUv1)f^Aw%1ifeu%9Ut`#1NPNwIAP)Md#n zw1x+a9}z;=*~jy|-_J%AaI+oxEm|f~XGityZCti`Y|ky`am~fq9k1Q^*oC5jsQ)qV z9P6->R&SRh)UY>*M`4cXbG3#am5XL_!1JCwLcH*ck{ZZra|S3t2UJ2;`ZAkuE!06c zhLuMi-!3E#xQ$VJUlb9cl%^C(ijLm@O3A3_a^Ydr+WRdIfs=7>_o=ju&Ylg2%xYp! zY1s^C+1XKUbV}3FKt+#DUJWlDDK#R@o@@zSp;;iQ2z6^%-&|La75w9wXwP7)Cgq&r z)71s$q(?9=Nz3!*y<|6F=9>;;X6u5zT0=3NET=tMr(#t2mtWU^K{2j{{uCUgB(1Di zHB&HD9saa0@#m8Lng;VhQi_d%oZ=11%lT1-S@$p1qVUUlp$W}fyuS;?8JuIZJXDaE zhW+D;*R>Xhp_!Mj`#N%82#O6&a3HRQCkCg7ya|?e;LO;aoBl4kvYbTSP&aP9IK0Od zSw8tSw4$)f5SJJ|gEclGau|7Ra`bon62KKZQLw)++G%fUy(RaGZo)`_y{gUFB6fny zS2D)CUb{u$Q?rk*>X#0ch4Siz7-}z>u7r&a2QzY;_%aXAFl=uZ`^jhUu81-5y{I11$|DJmXyj%Gmt3dr=Nvm%_gJyinCON}FXB)?X(}niJIeJ?7$lCy=bz<8r`u?_aUs|K}`Yk>rH5H=0yf T)W@=oP_}|7sL2<~n!fr!zQK*_ literal 28389 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 diff --git a/docs/reference/add_connectivity_penalties-6.png b/docs/reference/add_connectivity_penalties-6.png index 3e7ebc7f39c8f78ada0d58a83f5ac9cf88700369..400fddfe864f9b097a40763f8ae33f136718ad93 100644 GIT binary patch literal 45099 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%0D>imb!%Z#LhBd( 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 - - - - - - -Add connectivity penalties — add_connectivity_penalties • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add connectivity penalties — add_connectivity_penalties • prioritizr - - - - - - - - - - - - - - +

    -
    -
    -

    Add penalties to a conservation planning problem() to favor +

    Add penalties to a conservation planning problem() to favor solutions that select planning units with high connectivity between them.

    -
    # S4 method for ConservationProblem,ANY,ANY,matrix
    -add_connectivity_penalties(x, penalty, zones, data)
    +    
    Usage,
    # S4 method for ConservationProblem,ANY,ANY,matrix
    +add_connectivity_penalties(x, penalty, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,Matrix
    -add_connectivity_penalties(x, penalty, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY,Matrix
    +add_connectivity_penalties(x, penalty, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,data.frame
    -add_connectivity_penalties(x, penalty, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY,data.frame
    +add_connectivity_penalties(x, penalty, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,dgCMatrix
    -add_connectivity_penalties(x, penalty, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY,dgCMatrix
    +add_connectivity_penalties(x, penalty, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,array
    -add_connectivity_penalties(x, penalty, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY,array +add_connectivity_penalties(x, penalty, zones, data)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    penalty

    numeric penalty that is used to scale the importance +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    penalty
    +

    numeric penalty that is used to scale the importance of selecting planning units with strong connectivity between them compared -to the main problem objective (e.g. solution cost when the argument to +to the main problem objective (e.g., solution cost when the argument to x has a minimum set objective set using -add_min_set_objective()). Higher penalty values +add_min_set_objective()). Higher penalty values can be used to obtain solutions with a high degree of connectivity, and smaller penalty values can be used to obtain solutions with a small degree of connectivity. Note that negative penalty values can -be used to obtain solutions that have very little connectivity.

    zones

    matrix or Matrix object describing the +be used to obtain solutions that have very little connectivity.

    +
    zones
    +

    matrix or Matrix object describing the level of connectivity between different zones. Each row and column corresponds to a different zone in the argument to x, and cell values indicate the level of connectivity between each combination @@ -230,47 +141,44 @@

    Arg the level of connectivity between planning units allocated to the same zone. Cell values must lay between 1 and -1, where negative values favor solutions with weak connectivity. The default argument to -zones is an identity matrix (i.e. a matrix with ones along the +zones is an identity matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered to be connected when they are allocated to the same zone. This argument is required when the argument to data is a matrix or Matrix object. If the argument to data is an array or data.frame with zone data, this argument -must explicitly be set to NULL otherwise an error will be thrown.

    data

    matrix, Matrix, data.frame, or +must explicitly be set to NULL otherwise an error will be thrown.

    +
    data
    +

    matrix, Matrix, data.frame, or array object containing connectivity data. The connectivity values correspond to the strength of connectivity between different planning units. Thus connections between planning units that are associated with higher values are more favorable in the solution. -See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the penalties +See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the penalties added to it.

    -

    Details

    - +
    +
    +

    Details

    This function adds penalties to conservation planning problem to penalize solutions that have low connectivity. Specifically, it favors pair-wise connections between planning units that have high connectivity values. It was inspired by Beger et al. (2010) and can symmetric and asymmetric connectivity relationships between planning units.

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data can be specified using several different formats. These formats can be used to describe symmetric or asymmetric relationships between planning units.

    -
    - -
    data as a matrix/Matrix object

    where rows and columns represent +

    data as a matrix/Matrix object
    +

    where rows and columns represent different planning units and the value of each cell represents the strength of connectivity between two different planning units. Cells that occur along the matrix diagonal are treated as weights which @@ -280,13 +188,15 @@

    zones is to treat planning units allocated to different zones as having zero connectivity.

    -
    data as a data.frame object

    containing the fields (columns) + +

    data as a data.frame object
    +

    containing the fields (columns) "id1", "id2", and "boundary". Here, each row denotes the connectivity between two planning units following the Marxan format. The data can be used to denote symmetric or asymmetric relationships between planning units. By default, input data is assumed to be symmetric unless asymmetric data is -also included (e.g. if data is present for planning units 2 and 3, then +also included (e.g., if data is present for planning units 2 and 3, then the same amount of connectivity is expected for planning units 3 and 2, unless connectivity data is also provided for planning units 3 and 2). If the argument to x contains multiple zones, then the columns @@ -296,10 +206,12 @@

    "zone2" are present, then the argument to zones must be NULL.

    -
    data as an array object

    containing four-dimensions where cell values + +

    data as an array object
    +

    containing four-dimensions where cell values indicate the strength of connectivity between planning units when they are assigned to specific management zones. The first two -dimensions (i.e. rows and columns) indicate the strength of +dimensions (i.e., rows and columns) indicate the strength of connectivity between different planning units and the second two dimensions indicate the different management zones. Thus the data[1, 2, 3, 4] indicates the strength of @@ -307,10 +219,10 @@

    -
    - -

    Mathematical formulation

    +
    +
    +

    Mathematical formulation

    The connectivity penalties are implemented using the following equations. @@ -318,7 +230,7 @@

    References

    - +
    +
    +

    References

    Beger M, Linke S, Watts M, Game E, Treml E, Ball I, and Possingham, HP (2010) Incorporating asymmetric connectivity into spatial decision making for conservation, Conservation Letters, 3: 359--368.

    -

    See also

    - -

    See penalties for an overview of all functions for adding penalties.

    +
    +
    +

    See also

    +

    See penalties for an overview of all functions for adding penalties.

    Other penalties: -add_boundary_penalties(), -add_feature_weights(), -add_linear_penalties()

    +add_boundary_penalties(), +add_feature_weights(), +add_linear_penalties()

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(600)
    -
    -# load Matrix package for visualizing matrices
    -require(Matrix)
    -#> Loading required package: Matrix
    -
    -# load data
    -data(sim_pu_polygons, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# define function to rescale values between zero and one so that we
    -# can compare solutions from different connectivity matrices
    -rescale <- function(x, to = c(0, 1), from = range(x, na.rm = TRUE)) {
    -  (x - from[1]) / diff(from) * diff(to) + to[1]
    -}
    -
    -# create basic problem
    -p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create a symmetric connectivity matrix where the connectivity between
    -# two planning units corresponds to their shared boundary length
    -b_matrix <- boundary_matrix(sim_pu_polygons)
    -
    -# standardize matrix values to lay between zero and one
    -b_matrix[] <- rescale(b_matrix[])
    -
    -# visualize connectivity matrix
    -# \dontrun{
    -image(b_matrix)
    -
    -# }
    -# create a symmetric connectivity matrix where the connectivity between
    -# two planning units corresponds to their spatial proximity
    -# i.e. planning units that are further apart share less connectivity
    -centroids <- rgeos::gCentroid(sim_pu_polygons, byid = TRUE)
    -d_matrix <- (1 / (as(dist(centroids@coords), "Matrix") + 1))
    -
    -# standardize matrix values to lay between zero and one
    -d_matrix[] <- rescale(d_matrix[])
    -
    -# remove connections between planning units without connectivity to
    -# reduce run-time
    -d_matrix[d_matrix < 0.7] <- 0
    -
    -# visualize connectivity matrix
    -# \dontrun{
    -image(d_matrix)
    -
    -# }
    -# create a symmetric connectivity matrix where the connectivity
    -# between adjacent two planning units corresponds to their combined
    -# value in a field in the planning unit attribute data
    -# for example, this field could describe the extent of native vegetation in
    -# each planning unit and we could use connectivity penalties to identify
    -# solutions that cluster planning units together that both contain large
    -# amounts of native vegetation
    -c_matrix <- connectivity_matrix(sim_pu_polygons, "cost")
    -
    -# standardize matrix values to lay between zero and one
    -c_matrix[] <- rescale(c_matrix[])
    -
    -# visualize connectivity matrix
    -# \dontrun{
    -image(c_matrix)
    -
    -# }
    -# create an asymmetric connectivity matrix. Here, connectivity occurs between
    -# adjacent planning units and, due to rivers flowing southwards
    -# through the study area, connectivity from northern planning units to
    -# southern planning units is ten times stronger than the reverse.
    -ac_matrix <- matrix(0, length(sim_pu_polygons), length(sim_pu_polygons))
    -ac_matrix <- as(ac_matrix, "Matrix")
    -adjacent_units <- rgeos::gIntersects(sim_pu_polygons, byid = TRUE)
    -for (i in seq_len(length(sim_pu_polygons))) {
    -  for (j in seq_len(length(sim_pu_polygons))) {
    -    # find if planning units are adjacent
    -    if (adjacent_units[i, j]) {
    -      # find if planning units lay north and south of each other
    -      # i.e. they have the same x-coordinate
    -      if (centroids@coords[i, 1] == centroids@coords[j, 1]) {
    -        if (centroids@coords[i, 2] > centroids@coords[j, 2]) {
    -          # if i is north of j add 10 units of connectivity
    -          ac_matrix[i, j] <- ac_matrix[i, j] + 10
    -        } else if (centroids@coords[i, 2] < centroids@coords[j, 2]) {
    -          # if i is south of j add 1 unit of connectivity
    -          ac_matrix[i, j] <- ac_matrix[i, j] + 1
    -        }
    -      }
    -    }
    -  }
    -}
    -
    -# standardize matrix values to lay between zero and one
    -ac_matrix[] <- rescale(ac_matrix[])
    -
    -# visualize asymmetric connectivity matrix
    -# \dontrun{
    -image(ac_matrix)
    -
    -# }
    -# create penalties
    -penalties <- c(10, 25)
    -
    -# create problems using the different connectivity matrices and penalties
    -p2 <- list(p1,
    -           p1 %>% add_connectivity_penalties(penalties[1], data = b_matrix),
    -           p1 %>% add_connectivity_penalties(penalties[2], data = b_matrix),
    -           p1 %>% add_connectivity_penalties(penalties[1], data = d_matrix),
    -           p1 %>% add_connectivity_penalties(penalties[2], data = d_matrix),
    -           p1 %>% add_connectivity_penalties(penalties[1], data = c_matrix),
    -           p1 %>% add_connectivity_penalties(penalties[2], data = c_matrix),
    -           p1 %>% add_connectivity_penalties(penalties[1], data = ac_matrix),
    -           p1 %>% add_connectivity_penalties(penalties[2], data = ac_matrix))
    -
    -# assign names to the problems
    -names(p2) <- c("basic problem",
    -               paste0("b_matrix (", penalties,")"),
    -               paste0("d_matrix (", penalties,")"),
    -               paste0("c_matrix (", penalties,")"),
    -               paste0("ac_matrix (", penalties,")"))
    -# \dontrun{
    -# solve problems
    -s2 <- lapply(p2, solve)
    -
    -# plot solutions
    -par(mfrow = c(3, 3))
    -for (i in seq_along(s2)) {
    -  plot(s2[[i]], main = names(p2)[i], cex = 1.5, col = "white")
    -  plot(s2[[i]][s2[[i]]$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -}
    -
    -# }
    -
    -# create minimal multi-zone problem and limit solver to one minute
    -# to obtain solutions in a short period of time
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(0.15, nrow = 5, ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(time_limit = 60, verbose = FALSE)
    -
    -# create matrix showing which planning units are adjacent to other units
    -a_matrix <- adjacency_matrix(sim_pu_zones_stack)
    -
    -# visualize matrix
    -# \dontrun{
    -image(a_matrix)
    -
    -# }
    -# create a zone matrix where connectivities are only present between
    -# planning units that are allocated to the same zone
    -zm1 <- as(diag(3), "Matrix")
    -
    -# print zone matrix
    -print(zm1)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    .    .
    -#> [2,]    .    1    .
    -#> [3,]    .    .    1
    -
    -# create a zone matrix where connectivities are strongest between
    -# planning units allocated to different zones
    -zm2 <- matrix(1, ncol = 3, nrow = 3)
    -diag(zm2) <- 0
    -zm2 <- as(zm2, "Matrix")
    -
    -# print zone matrix
    -print(zm2)
    -#> 3 x 3 Matrix of class "dsyMatrix"
    -#>      [,1] [,2] [,3]
    -#> [1,]    0    1    1
    -#> [2,]    1    0    1
    -#> [3,]    1    1    0
    -
    -# create a zone matrix that indicates that connectivities between planning
    -# units assigned to the same zone are much higher than connectivities
    -# assigned to different zones
    -zm3 <- matrix(0.1, ncol = 3, nrow = 3)
    -diag(zm3) <- 1
    -zm3 <- as(zm3, "Matrix")
    -
    -# print zone matrix
    -print(zm3)
    -#> 3 x 3 Matrix of class "dsyMatrix"
    -#>      [,1] [,2] [,3]
    -#> [1,]  1.0  0.1  0.1
    -#> [2,]  0.1  1.0  0.1
    -#> [3,]  0.1  0.1  1.0
    -
    -# create a zone matrix that indicates that connectivities between planning
    -# units allocated to zone 1 are very high, connectivities between planning
    -# units allocated to zones 1 and 2 are moderately high, and connectivities
    -# planning units allocated to other zones are low
    -zm4 <- matrix(0.1, ncol = 3, nrow = 3)
    -zm4[1, 1] <- 1
    -zm4[1, 2] <- 0.5
    -zm4[2, 1] <- 0.5
    -zm4 <- as(zm4, "Matrix")
    -
    -# print zone matrix
    -print(zm4)
    -#> 3 x 3 Matrix of class "dsyMatrix"
    -#>      [,1] [,2] [,3]
    -#> [1,]  1.0  0.5  0.1
    -#> [2,]  0.5  0.1  0.1
    -#> [3,]  0.1  0.1  0.1
    -
    -# create a zone matrix with strong connectivities between planning units
    -# allocated to the same zone, moderate connectivities between planning
    -# unit allocated to zone 1 and zone 2, and negative connectivities between
    -# planning units allocated to zone 3 and the other two zones
    -zm5 <- matrix(-1, ncol = 3, nrow = 3)
    -zm5[1, 2] <- 0.5
    -zm5[2, 1] <- 0.5
    -diag(zm5) <- 1
    -zm5 <- as(zm5, "Matrix")
    -
    -# print zone matrix
    -print(zm5)
    -#> 3 x 3 Matrix of class "dsyMatrix"
    -#>      [,1] [,2] [,3]
    -#> [1,]  1.0  0.5   -1
    -#> [2,]  0.5  1.0   -1
    -#> [3,] -1.0 -1.0    1
    -
    -# create vector of penalties to use creating problems
    -penalties2 <- c(5, 15)
    -
    -# create multi-zone problems using the adjacent connectivity matrix and
    -# different zone matrices
    -p4 <- list(
    -  p3,
    -  p3 %>% add_connectivity_penalties(penalties2[1], zm1, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[2], zm1, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[1], zm2, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[2], zm2, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[1], zm3, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[2], zm3, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[1], zm4, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[2], zm4, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[1], zm5, a_matrix),
    -  p3 %>% add_connectivity_penalties(penalties2[2], zm5, a_matrix))
    -
    -# assign names to the problems
    -names(p4) <- c("basic problem",
    -               paste0("zm", rep(seq_len(5), each = 2), " (",
    -                      rep(penalties2, 2), ")"))
    -# \dontrun{
    -# solve problems
    -s4 <- lapply(p4, solve)
    -s4 <- lapply(s4, category_layer)
    -s4 <- stack(s4)
    -
    -# plot solutions
    -plot(s4, main = names(p4), axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create an array to manually specify the connectivities between
    -# each planning unit when they are allocated to each different zone
    -# for real-world problems, these connectivities would be generated using
    -# data - but here these connectivity values are assigned as random
    -# ones or zeros
    -c_array <- array(0, c(rep(ncell(sim_pu_zones_stack[[1]]), 2), 3, 3))
    -for (z1 in seq_len(3))
    -  for (z2 in seq_len(3))
    -    c_array[, , z1, z2] <- round(runif(ncell(sim_pu_zones_stack[[1]]) ^ 2,
    -                                       0, 0.505))
    -
    -# create a problem with the manually specified connectivity array
    -# note that the zones argument is set to NULL because the connectivity
    -# data is an array
    -p5 <- list(p3,
    -           p3 %>% add_connectivity_penalties(15, zones = NULL, c_array))
    -
    -
    -# assign names to the problems
    -names(p5) <- c("basic problem", "connectivity array")
    -# \dontrun{
    -# solve problems
    -s5 <- lapply(p5, solve)
    -s5 <- lapply(s5, category_layer)
    -s5 <- stack(s5)
    -
    -# plot solutions
    -plot(s5, main = names(p5), axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(600)
    +
    +# load Matrix package for visualizing matrices
    +require(Matrix)
    +#> Loading required package: Matrix
    +
    +# load data
    +data(sim_pu_polygons, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# define function to rescale values between zero and one so that we
    +# can compare solutions from different connectivity matrices
    +rescale <- function(x, to = c(0, 1), from = range(x, na.rm = TRUE)) {
    +  (x - from[1]) / diff(from) * diff(to) + to[1]
    +}
    +
    +# create basic problem
    +p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create a symmetric connectivity matrix where the connectivity between
    +# two planning units corresponds to their shared boundary length
    +b_matrix <- boundary_matrix(sim_pu_polygons)
    +
    +# standardize matrix values to lay between zero and one
    +b_matrix[] <- rescale(b_matrix[])
    +
    +# visualize connectivity matrix
    +# \dontrun{
    +image(b_matrix)
    +
    +# }
    +# create a symmetric connectivity matrix where the connectivity between
    +# two planning units corresponds to their spatial proximity
    +# i.e., planning units that are further apart share less connectivity
    +centroids <- rgeos::gCentroid(sim_pu_polygons, byid = TRUE)
    +d_matrix <- (1 / (as(dist(centroids@coords), "Matrix") + 1))
    +
    +# standardize matrix values to lay between zero and one
    +d_matrix[] <- rescale(d_matrix[])
    +
    +# remove connections between planning units without connectivity to
    +# reduce run-time
    +d_matrix[d_matrix < 0.7] <- 0
    +
    +# visualize connectivity matrix
    +# \dontrun{
    +image(d_matrix)
    +
    +# }
    +# create a symmetric connectivity matrix where the connectivity
    +# between adjacent two planning units corresponds to their combined
    +# value in a field in the planning unit attribute data
    +# for example, this field could describe the extent of native vegetation in
    +# each planning unit and we could use connectivity penalties to identify
    +# solutions that cluster planning units together that both contain large
    +# amounts of native vegetation
    +c_matrix <- connectivity_matrix(sim_pu_polygons, "cost")
    +
    +# standardize matrix values to lay between zero and one
    +c_matrix[] <- rescale(c_matrix[])
    +
    +# visualize connectivity matrix
    +# \dontrun{
    +image(c_matrix)
    +
    +# }
    +# create an asymmetric connectivity matrix. Here, connectivity occurs between
    +# adjacent planning units and, due to rivers flowing southwards
    +# through the study area, connectivity from northern planning units to
    +# southern planning units is ten times stronger than the reverse.
    +ac_matrix <- matrix(0, length(sim_pu_polygons), length(sim_pu_polygons))
    +ac_matrix <- as(ac_matrix, "Matrix")
    +adjacent_units <- rgeos::gIntersects(sim_pu_polygons, byid = TRUE)
    +for (i in seq_len(length(sim_pu_polygons))) {
    +  for (j in seq_len(length(sim_pu_polygons))) {
    +    # find if planning units are adjacent
    +    if (adjacent_units[i, j]) {
    +      # find if planning units lay north and south of each other
    +      # i.e., they have the same x-coordinate
    +      if (centroids@coords[i, 1] == centroids@coords[j, 1]) {
    +        if (centroids@coords[i, 2] > centroids@coords[j, 2]) {
    +          # if i is north of j add 10 units of connectivity
    +          ac_matrix[i, j] <- ac_matrix[i, j] + 10
    +        } else if (centroids@coords[i, 2] < centroids@coords[j, 2]) {
    +          # if i is south of j add 1 unit of connectivity
    +          ac_matrix[i, j] <- ac_matrix[i, j] + 1
    +        }
    +      }
    +    }
    +  }
    +}
    +
    +# standardize matrix values to lay between zero and one
    +ac_matrix[] <- rescale(ac_matrix[])
    +
    +# visualize asymmetric connectivity matrix
    +# \dontrun{
    +image(ac_matrix)
    +
    +# }
    +# create penalties
    +penalties <- c(10, 25)
    +
    +# create problems using the different connectivity matrices and penalties
    +p2 <- list(p1,
    +           p1 %>% add_connectivity_penalties(penalties[1], data = b_matrix),
    +           p1 %>% add_connectivity_penalties(penalties[2], data = b_matrix),
    +           p1 %>% add_connectivity_penalties(penalties[1], data = d_matrix),
    +           p1 %>% add_connectivity_penalties(penalties[2], data = d_matrix),
    +           p1 %>% add_connectivity_penalties(penalties[1], data = c_matrix),
    +           p1 %>% add_connectivity_penalties(penalties[2], data = c_matrix),
    +           p1 %>% add_connectivity_penalties(penalties[1], data = ac_matrix),
    +           p1 %>% add_connectivity_penalties(penalties[2], data = ac_matrix))
    +
    +# assign names to the problems
    +names(p2) <- c("basic problem",
    +               paste0("b_matrix (", penalties,")"),
    +               paste0("d_matrix (", penalties,")"),
    +               paste0("c_matrix (", penalties,")"),
    +               paste0("ac_matrix (", penalties,")"))
    +# \dontrun{
    +# solve problems
    +s2 <- lapply(p2, solve)
    +
    +# plot solutions
    +par(mfrow = c(3, 3))
    +for (i in seq_along(s2)) {
    +  plot(s2[[i]], main = names(p2)[i], cex = 1.5, col = "white")
    +  plot(s2[[i]][s2[[i]]$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +}
    +
    +# }
    +
    +# create minimal multi-zone problem and limit solver to one minute
    +# to obtain solutions in a short period of time
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(0.15, nrow = 5, ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(time_limit = 60, verbose = FALSE)
    +
    +# create matrix showing which planning units are adjacent to other units
    +a_matrix <- adjacency_matrix(sim_pu_zones_stack)
    +
    +# visualize matrix
    +# \dontrun{
    +image(a_matrix)
    +
    +# }
    +# create a zone matrix where connectivities are only present between
    +# planning units that are allocated to the same zone
    +zm1 <- as(diag(3), "Matrix")
    +
    +# print zone matrix
    +print(zm1)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    .    .
    +#> [2,]    .    1    .
    +#> [3,]    .    .    1
    +
    +# create a zone matrix where connectivities are strongest between
    +# planning units allocated to different zones
    +zm2 <- matrix(1, ncol = 3, nrow = 3)
    +diag(zm2) <- 0
    +zm2 <- as(zm2, "Matrix")
    +
    +# print zone matrix
    +print(zm2)
    +#> 3 x 3 Matrix of class "dsyMatrix"
    +#>      [,1] [,2] [,3]
    +#> [1,]    0    1    1
    +#> [2,]    1    0    1
    +#> [3,]    1    1    0
    +
    +# create a zone matrix that indicates that connectivities between planning
    +# units assigned to the same zone are much higher than connectivities
    +# assigned to different zones
    +zm3 <- matrix(0.1, ncol = 3, nrow = 3)
    +diag(zm3) <- 1
    +zm3 <- as(zm3, "Matrix")
    +
    +# print zone matrix
    +print(zm3)
    +#> 3 x 3 Matrix of class "dsyMatrix"
    +#>      [,1] [,2] [,3]
    +#> [1,]  1.0  0.1  0.1
    +#> [2,]  0.1  1.0  0.1
    +#> [3,]  0.1  0.1  1.0
    +
    +# create a zone matrix that indicates that connectivities between planning
    +# units allocated to zone 1 are very high, connectivities between planning
    +# units allocated to zones 1 and 2 are moderately high, and connectivities
    +# planning units allocated to other zones are low
    +zm4 <- matrix(0.1, ncol = 3, nrow = 3)
    +zm4[1, 1] <- 1
    +zm4[1, 2] <- 0.5
    +zm4[2, 1] <- 0.5
    +zm4 <- as(zm4, "Matrix")
    +
    +# print zone matrix
    +print(zm4)
    +#> 3 x 3 Matrix of class "dsyMatrix"
    +#>      [,1] [,2] [,3]
    +#> [1,]  1.0  0.5  0.1
    +#> [2,]  0.5  0.1  0.1
    +#> [3,]  0.1  0.1  0.1
    +
    +# create a zone matrix with strong connectivities between planning units
    +# allocated to the same zone, moderate connectivities between planning
    +# unit allocated to zone 1 and zone 2, and negative connectivities between
    +# planning units allocated to zone 3 and the other two zones
    +zm5 <- matrix(-1, ncol = 3, nrow = 3)
    +zm5[1, 2] <- 0.5
    +zm5[2, 1] <- 0.5
    +diag(zm5) <- 1
    +zm5 <- as(zm5, "Matrix")
    +
    +# print zone matrix
    +print(zm5)
    +#> 3 x 3 Matrix of class "dsyMatrix"
    +#>      [,1] [,2] [,3]
    +#> [1,]  1.0  0.5   -1
    +#> [2,]  0.5  1.0   -1
    +#> [3,] -1.0 -1.0    1
    +
    +# create vector of penalties to use creating problems
    +penalties2 <- c(5, 15)
    +
    +# create multi-zone problems using the adjacent connectivity matrix and
    +# different zone matrices
    +p4 <- list(
    +  p3,
    +  p3 %>% add_connectivity_penalties(penalties2[1], zm1, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[2], zm1, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[1], zm2, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[2], zm2, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[1], zm3, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[2], zm3, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[1], zm4, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[2], zm4, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[1], zm5, a_matrix),
    +  p3 %>% add_connectivity_penalties(penalties2[2], zm5, a_matrix))
    +
    +# assign names to the problems
    +names(p4) <- c("basic problem",
    +               paste0("zm", rep(seq_len(5), each = 2), " (",
    +                      rep(penalties2, 2), ")"))
    +# \dontrun{
    +# solve problems
    +s4 <- lapply(p4, solve)
    +s4 <- lapply(s4, category_layer)
    +s4 <- stack(s4)
    +
    +# plot solutions
    +plot(s4, main = names(p4), axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create an array to manually specify the connectivities between
    +# each planning unit when they are allocated to each different zone
    +# for real-world problems, these connectivities would be generated using
    +# data - but here these connectivity values are assigned as random
    +# ones or zeros
    +c_array <- array(0, c(rep(ncell(sim_pu_zones_stack[[1]]), 2), 3, 3))
    +for (z1 in seq_len(3))
    +  for (z2 in seq_len(3))
    +    c_array[, , z1, z2] <- round(runif(ncell(sim_pu_zones_stack[[1]]) ^ 2,
    +                                       0, 0.505))
    +
    +# create a problem with the manually specified connectivity array
    +# note that the zones argument is set to NULL because the connectivity
    +# data is an array
    +p5 <- list(p3,
    +           p3 %>% add_connectivity_penalties(15, zones = NULL, c_array))
    +
    +
    +# assign names to the problems
    +names(p5) <- c("basic problem", "connectivity array")
    +# \dontrun{
    +# solve problems
    +s5 <- lapply(p5, solve)
    +s5 <- lapply(s5, category_layer)
    +s5 <- stack(s5)
    +
    +# plot solutions
    +plot(s5, main = names(p5), axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_contiguity_constraints.html b/docs/reference/add_contiguity_constraints.html index b9450b2af..4fc61ab91 100644 --- a/docs/reference/add_contiguity_constraints.html +++ b/docs/reference/add_contiguity_constraints.html @@ -1,103 +1,20 @@ - - - - - - - -Add contiguity constraints — add_contiguity_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add contiguity constraints — add_contiguity_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Add constraints to a conservation planning problem() to ensure +

    Add constraints to a conservation planning problem() to ensure that all selected planning units are spatially connected with each other and form a single contiguous unit.

    -
    # S4 method for ConservationProblem,ANY,ANY
    -add_contiguity_constraints(x, zones, data)
    +    
    Usage,
    # S4 method for ConservationProblem,ANY,ANY
    +add_contiguity_constraints(x, zones, data)
     
    -# S4 method for ConservationProblem,ANY,data.frame
    -add_contiguity_constraints(x, zones, data)
    +# S4 method for ConservationProblem,ANY,data.frame
    +add_contiguity_constraints(x, zones, data)
     
    -# S4 method for ConservationProblem,ANY,matrix
    -add_contiguity_constraints(x, zones, data)
    +# S4 method for ConservationProblem,ANY,matrix +add_contiguity_constraints(x, zones, data)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    zones

    matrix or Matrix object describing the +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    zones
    +

    matrix or Matrix object describing the connection scheme for different zones. Each row and column corresponds to a different zone in the argument to x, and cell values must -contain binary numeric values (i.e. one or zero) that indicate +contain binary numeric values (i.e., one or zero) that indicate if connected planning units (as specified in the argument to data) should be still considered connected if they are allocated to different zones. The cell values along the diagonal @@ -218,54 +131,55 @@

    Arg arguments to zones must be symmetric, and that a row or column has a value of one then the diagonal element for that row or column must also have a value of one. The default argument to zones is an identity -matrix (i.e. a matrix with ones along the matrix diagonal and zeros +matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered connected if they -are both allocated to the same zone.

    data

    NULL, matrix, Matrix, data.frame +are both allocated to the same zone.

    +
    data
    +

    NULL, matrix, Matrix, data.frame object showing which planning units are connected with each other. The argument defaults to NULL which means that the connection data is calculated automatically using the -adjacency_matrix() function. -See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the constraints +adjacency_matrix() function. +See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Details

    - +
    +
    +

    Details

    This function uses connection data to identify solutions that form a single contiguous unit. It was inspired by the mathematical formulations detailed in Önal and Briers (2006).

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data can be specified using the following formats.

    -
    - -
    data as a NULL value

    indicating that connection data should be -calculated automatically using the adjacency_matrix() function. +

    data as a NULL value
    +

    indicating that connection data should be +calculated automatically using the adjacency_matrix() function. This is the default argument. Note that the connection data must be manually defined using one of the other formats below when the planning unit data -in the argument to x is not spatially referenced (e.g. +in the argument to x is not spatially referenced (e.g., in data.frame or numeric format).

    -
    data as a matrix/Matrix object

    where rows and columns represent + +

    data as a matrix/Matrix object
    +

    where rows and columns represent different planning units and the value of each cell indicates if the two planning units are connected or not. Cell values should be binary -numeric values (i.e. one or zero). Cells that occur along the +numeric values (i.e., one or zero). Cells that occur along the matrix diagonal have no effect on the solution at all because each planning unit cannot be a connected with itself.

    -
    data as a data.frame object

    containing the fields (columns) + +

    data as a data.frame object
    +

    containing the fields (columns) "id1", "id2", and "boundary". Here, each row denotes the connectivity between two planning units following the Marxan format. The field boundary should contain @@ -274,209 +188,209 @@

    -
    - -

    Notes

    +
    +
    +

    Notes

    In early versions, this function was named as the -add_connected_constraints() function.

    -

    References

    - +add_connected_constraints() function.

    +
    +
    +

    References

    Önal H and Briers RA (2006) Optimal selection of a connected reserve network. Operations Research, 54: 379--388.

    -

    See also

    - -

    See constraints for an overview of all functions for adding constraints.

    +
    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with added connected constraints
    -p2 <- p1 %>% add_contiguity_constraints()
    -# \dontrun{
    -# solve problems
    -s <- stack(solve(p1), solve(p2))
    -
    -# plot solutions
    -plot(s, main = c("basic solution", "connected solution"), axes = FALSE,
    -     box = FALSE)
    -
    -# }
    -# create minimal problem with multiple zones, and limit the solver to
    -# 30 seconds to obtain solutions in a feasible period of time
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(time_limit = 30, verbose = FALSE)
    -
    -# create problem with added constraints to ensure that the planning units
    -# allocated to each zone form a separate contiguous unit
    -z4 <- diag(3)
    -print(z4)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    0    0
    -#> [2,]    0    1    0
    -#> [3,]    0    0    1
    -p4 <- p3 %>% add_contiguity_constraints(z4)
    -
    -# create problem with added constraints to ensure that the planning
    -# units allocated to each zone form a separate contiguous unit,
    -# except for planning units allocated to zone 2 which do not need
    -# form a single contiguous unit
    -z5 <- diag(3)
    -z5[3, 3] <- 0
    -print(z5)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    0    0
    -#> [2,]    0    1    0
    -#> [3,]    0    0    0
    -p5 <- p3 %>% add_contiguity_constraints(z5)
    -
    -# create problem with added constraints that ensure that the planning
    -# units allocated to zones 1 and 2 form a contiguous unit
    -z6 <- diag(3)
    -z6[1, 2] <- 1
    -z6[2, 1] <- 1
    -print(z6)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    1    0
    -#> [2,]    1    1    0
    -#> [3,]    0    0    1
    -p6 <- p3 %>% add_contiguity_constraints(z6)
    -# \dontrun{
    -# solve problems
    -s2 <- lapply(list(p3, p4, p5, p6), solve)
    -s2 <- lapply(s2, category_layer)
    -s2 <- stack(s2)
    -
    -# plot solutions
    -plot(s2, axes = FALSE, box = FALSE,
    -     main = c("basic solution", "p4", "p5", "p6"))
    -
    -# }
    -# create a problem that has a main "reserve zone" and a secondary
    -# "corridor zone" to connect up import areas. Here, each feature has a
    -# target of 30% of its distribution. If a planning unit is allocated to the
    -# "reserve zone", then the prioritization accrues 100% of the amount of
    -# each feature in the planning unit. If a planning unit is allocated to the
    -# "corridor zone" then the prioritization accrues 40% of the amount of each
    -# feature in the planning unit. Also, the cost of managing a planning unit
    -# in the "corridor zone" is 45% of that when it is managed as the
    -# "reserve zone". Finally, the problem has constraints which
    -# ensure that all of the selected planning units form a single contiguous
    -# unit, so that the planning units allocated to the "corridor zone" can
    -# link up the planning units allocated to the "reserve zone"
    -
    -# create planning unit data
    -pus <- sim_pu_zones_stack[[c(1, 1)]]
    -pus[[2]] <- pus[[2]] * 0.45
    -print(pus)
    -#> class      : RasterStack 
    -#> dimensions : 10, 10, 100, 2  (nrow, ncol, ncell, nlayers)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> names      : layer.1.1, layer.1.2 
    -#> min values : 190.13276,  85.55974 
    -#> max values : 215.86384,  97.13873 
    -#> 
    -
    -# create biodiversity data
    -fts <- zones(sim_features, sim_features * 0.4,
    -             feature_names = names(sim_features),
    -             zone_names = c("reserve zone", "corridor zone"))
    -print(fts)
    -#> Zones
    -#>   zones: reserve zone, corridor zone (2 zones)
    -#>   features: layer.1, layer.2, layer.3, ... (5 features)
    -#>   data type: RasterStack
    -
    -# create targets
    -targets <- tibble::tibble(feature = names(sim_features),
    -                          zone = list(zone_names(fts))[rep(1, 5)],
    -                          target = cellStats(sim_features, "sum") * 0.2,
    -                          type = rep("absolute", 5))
    -print(targets)
    -#> # A tibble: 5 × 4
    -#>   feature zone      target type    
    -#>   <chr>   <list>     <dbl> <chr>   
    -#> 1 layer.1 <chr [2]>  16.7  absolute
    -#> 2 layer.2 <chr [2]>   6.24 absolute
    -#> 3 layer.3 <chr [2]>  14.4  absolute
    -#> 4 layer.4 <chr [2]>   8.53 absolute
    -#> 5 layer.5 <chr [2]>  11.3  absolute
    -
    -# create zones matrix
    -z7 <- matrix(1, ncol = 2, nrow = 2)
    -print(z7)
    -#>      [,1] [,2]
    -#> [1,]    1    1
    -#> [2,]    1    1
    -
    -# create problem
    -p7 <- problem(pus, fts) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(targets) %>%
    -      add_contiguity_constraints(z7) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problems
    -s7 <- category_layer(solve(p7))
    -
    -# plot solutions
    -plot(s7, "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with added connected constraints
    +p2 <- p1 %>% add_contiguity_constraints()
    +# \dontrun{
    +# solve problems
    +s <- stack(solve(p1), solve(p2))
    +
    +# plot solutions
    +plot(s, main = c("basic solution", "connected solution"), axes = FALSE,
    +     box = FALSE)
    +
    +# }
    +# create minimal problem with multiple zones, and limit the solver to
    +# 30 seconds to obtain solutions in a feasible period of time
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(time_limit = 30, verbose = FALSE)
    +
    +# create problem with added constraints to ensure that the planning units
    +# allocated to each zone form a separate contiguous unit
    +z4 <- diag(3)
    +print(z4)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    0    0
    +#> [2,]    0    1    0
    +#> [3,]    0    0    1
    +p4 <- p3 %>% add_contiguity_constraints(z4)
    +
    +# create problem with added constraints to ensure that the planning
    +# units allocated to each zone form a separate contiguous unit,
    +# except for planning units allocated to zone 2 which do not need
    +# form a single contiguous unit
    +z5 <- diag(3)
    +z5[3, 3] <- 0
    +print(z5)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    0    0
    +#> [2,]    0    1    0
    +#> [3,]    0    0    0
    +p5 <- p3 %>% add_contiguity_constraints(z5)
    +
    +# create problem with added constraints that ensure that the planning
    +# units allocated to zones 1 and 2 form a contiguous unit
    +z6 <- diag(3)
    +z6[1, 2] <- 1
    +z6[2, 1] <- 1
    +print(z6)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    1    0
    +#> [2,]    1    1    0
    +#> [3,]    0    0    1
    +p6 <- p3 %>% add_contiguity_constraints(z6)
    +# \dontrun{
    +# solve problems
    +s2 <- lapply(list(p3, p4, p5, p6), solve)
    +s2 <- lapply(s2, category_layer)
    +s2 <- stack(s2)
    +
    +# plot solutions
    +plot(s2, axes = FALSE, box = FALSE,
    +     main = c("basic solution", "p4", "p5", "p6"))
    +
    +# }
    +# create a problem that has a main "reserve zone" and a secondary
    +# "corridor zone" to connect up import areas. Here, each feature has a
    +# target of 30% of its distribution. If a planning unit is allocated to the
    +# "reserve zone", then the prioritization accrues 100% of the amount of
    +# each feature in the planning unit. If a planning unit is allocated to the
    +# "corridor zone" then the prioritization accrues 40% of the amount of each
    +# feature in the planning unit. Also, the cost of managing a planning unit
    +# in the "corridor zone" is 45% of that when it is managed as the
    +# "reserve zone". Finally, the problem has constraints which
    +# ensure that all of the selected planning units form a single contiguous
    +# unit, so that the planning units allocated to the "corridor zone" can
    +# link up the planning units allocated to the "reserve zone"
    +
    +# create planning unit data
    +pus <- sim_pu_zones_stack[[c(1, 1)]]
    +pus[[2]] <- pus[[2]] * 0.45
    +print(pus)
    +#> class      : RasterStack 
    +#> dimensions : 10, 10, 100, 2  (nrow, ncol, ncell, nlayers)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> names      : layer.1.1, layer.1.2 
    +#> min values : 190.13276,  85.55974 
    +#> max values : 215.86384,  97.13873 
    +#> 
    +
    +# create biodiversity data
    +fts <- zones(sim_features, sim_features * 0.4,
    +             feature_names = names(sim_features),
    +             zone_names = c("reserve zone", "corridor zone"))
    +print(fts)
    +#> Zones
    +#>   zones: reserve zone, corridor zone (2 zones)
    +#>   features: layer.1, layer.2, layer.3, ... (5 features)
    +#>   data type: RasterStack
    +
    +# create targets
    +targets <- tibble::tibble(feature = names(sim_features),
    +                          zone = list(zone_names(fts))[rep(1, 5)],
    +                          target = cellStats(sim_features, "sum") * 0.2,
    +                          type = rep("absolute", 5))
    +print(targets)
    +#> # A tibble: 5 × 4
    +#>   feature zone      target type    
    +#>   <chr>   <list>     <dbl> <chr>   
    +#> 1 layer.1 <chr [2]>  16.7  absolute
    +#> 2 layer.2 <chr [2]>   6.24 absolute
    +#> 3 layer.3 <chr [2]>  14.4  absolute
    +#> 4 layer.4 <chr [2]>   8.53 absolute
    +#> 5 layer.5 <chr [2]>  11.3  absolute
    +
    +# create zones matrix
    +z7 <- matrix(1, ncol = 2, nrow = 2)
    +print(z7)
    +#>      [,1] [,2]
    +#> [1,]    1    1
    +#> [2,]    1    1
    +
    +# create problem
    +p7 <- problem(pus, fts) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(targets) %>%
    +      add_contiguity_constraints(z7) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problems
    +s7 <- category_layer(solve(p7))
    +
    +# plot solutions
    +plot(s7, "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_cplex_solver.html b/docs/reference/add_cplex_solver.html index 11dccd2be..ba8a91e12 100644 --- a/docs/reference/add_cplex_solver.html +++ b/docs/reference/add_cplex_solver.html @@ -1,106 +1,23 @@ - - - - - - - -Add a CPLEX solver — add_cplex_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a CPLEX solver — add_cplex_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Specify that the -IBM CPLEX software -(IBM 2017) should be used to solve a conservation planning problem(). +IBM CPLEX software +(IBM 2017) should be used to solve a conservation planning problem(). This function can also be used to customize the behavior of the solver. It requires the cplexAPI package to be installed (see below for installation instructions).

    -
    add_cplex_solver(
    -  x,
    -  gap = 0.1,
    -  time_limit = .Machine$integer.max,
    -  presolve = TRUE,
    -  threads = 1,
    -  verbose = TRUE
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    gap

    numeric gap to optimality. This gap is relative +

    Usage,
    add_cplex_solver(
    +  x,
    +  gap = 0.1,
    +  time_limit = .Machine$integer.max,
    +  presolve = TRUE,
    +  threads = 1,
    +  verbose = TRUE
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    gap
    +

    numeric gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10% from optimality).

    time_limit

    numeric time limit (seconds) for generating solutions. +The default value is 0.1 (i.e., 10% from optimality).

    +
    time_limit
    +

    numeric time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. .Machine$integer.max), effectively meaning that solver -will keep running until a solution within the optimality gap is found.

    presolve

    logical attempt to simplify the -problem before solving it? Defaults to TRUE.

    threads

    integer number of threads to use for the -optimization algorithm. The default value is 1.

    verbose

    logical should information be printed while solving -optimization problems? Defaults to TRUE.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the solver +(i.e., .Machine$integer.max), effectively meaning that solver +will keep running until a solution within the optimality gap is found.

    +
    presolve
    +

    logical attempt to simplify the +problem before solving it? Defaults to TRUE.

    +
    threads
    +

    integer number of threads to use for the +optimization algorithm. The default value is 1.

    +
    verbose
    +

    logical should information be printed while solving +optimization problems? Defaults to TRUE.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the solver added to it.

    -

    Details

    - -

    IBM CPLEX is a +

    +
    +

    Details

    +

    IBM CPLEX is a commercial optimization software. It is faster than -the available open source solvers (e.g. add_lpsymphony_solver() and -add_rsymphony_solver(). +the available open source solvers (e.g., add_lpsymphony_solver() and +add_rsymphony_solver(). Although formal benchmarks examining the performance of this solver for conservation planning problems have yet to be completed, preliminary analyses suggest that it performs slightly slower than the Gurobi -solver (i.e. add_gurobi_solver()). +solver (i.e., add_gurobi_solver()). We recommend using this solver if the Gurobi solver is not available. Licenses are available for the IBM CPLEX software to academics at no cost -(see https://www.ibm.com/products/ilog-cplex-optimization-studio).

    -

    Installation

    - +(see https://www.ibm.com/products/ilog-cplex-optimization-studio).

    +
    + +
    +

    References

    IBM (2017) IBM ILOG CPLEX Optimization Studio CPLEX User's Manual. Version 12 Release 8. IBM ILOG CPLEX Division, Incline Village, NV.

    -

    See also

    - -

    See solvers for an overview of all functions for adding a solver.

    +
    +
    +

    See also

    +

    See solvers for an overview of all functions for adding a solver.

    Other solvers: -add_cbc_solver(), -add_default_solver(), -add_gurobi_solver(), -add_lsymphony_solver, -add_rsymphony_solver()

    +add_cbc_solver(), +add_default_solver(), +add_gurobi_solver(), +add_lsymphony_solver, +add_rsymphony_solver()

    +
    -

    Examples

    -
    # \dontrun{
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.1) %>%
    -     add_binary_decisions() %>%
    -     add_cplex_solver(gap = 0.1, time_limit = 5, verbose = FALSE)
    -
    -# generate solution
    -s <- solve(p)
    -
    -# plot solution
    -plot(s, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.1) %>%
    +     add_binary_decisions() %>%
    +     add_cplex_solver(gap = 0.1, time_limit = 5, verbose = FALSE)
    +
    +# generate solution
    +s <- solve(p)
    +
    +# plot solution
    +plot(s, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_cuts_portfolio.html b/docs/reference/add_cuts_portfolio.html index 0fe5f6f88..8a5780b75 100644 --- a/docs/reference/add_cuts_portfolio.html +++ b/docs/reference/add_cuts_portfolio.html @@ -1,105 +1,22 @@ - - - - - - - -Add Bender's cuts portfolio — add_cuts_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add Bender's cuts portfolio — add_cuts_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Generate a portfolio of solutions for a conservation planning -problem() using Bender's cuts (discussed in Rodrigues +problem() using Bender's cuts (discussed in Rodrigues et al. 2000). This is recommended as a replacement for -add_gap_portfolio() when the Gurobi software is not +add_gap_portfolio() when the Gurobi software is not available.

    -
    add_cuts_portfolio(x, number_solutions = 10L)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    number_solutions

    integer number of attempts to generate -different solutions. Defaults to 10.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the portfolio +

    Usage,
    add_cuts_portfolio(x, number_solutions = 10L)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    number_solutions
    +

    integer number of attempts to generate +different solutions. Defaults to 10.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the portfolio added to it.

    -

    Details

    - +
    +
    +

    Details

    This strategy for generating a portfolio of solutions involves solving the problem multiple times and adding additional constraints to forbid previously obtained solutions. In general, this strategy is most useful when problems take a long time to solve and benefit from having multiple threads allocated for solving an individual problem.

    -

    Notes

    - +
    +
    +

    Notes

    In early versions (< 4.0.1), this function was only compatible with -Gurobi (i.e. add_gurobi_solver()). To provide functionality with +Gurobi (i.e., add_gurobi_solver()). To provide functionality with exact algorithm solvers, this function now adds constraints to the problem formulation to generate multiple solutions.

    -

    References

    - +
    +
    +

    References

    Rodrigues AS, Cerdeira OJ, and Gaston KJ (2000) Flexibility, efficiency, and accountability: adapting reserve selection algorithms to more complex conservation problems. Ecography, 23: 565--574.

    -

    See also

    - -

    See portfolios for an overview of all functions for adding a portfolio.

    +
    +
    +

    See also

    +

    See portfolios for an overview of all functions for adding a portfolio.

    Other portfolios: -add_extra_portfolio(), -add_gap_portfolio(), -add_shuffle_portfolio(), -add_top_portfolio()

    +add_extra_portfolio(), +add_gap_portfolio(), +add_shuffle_portfolio(), +add_top_portfolio()

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem with cuts portfolio
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_cuts_portfolio(10) %>%
    -      add_default_solver(gap = 0.2, verbose = FALSE)
    -
    -# \dontrun{
    -# solve problem and generate 10 solutions within 20% of optimality
    -s1 <- solve(p1)
    -
    -# plot solutions in portfolio
    -plot(stack(s1), axes = FALSE, box = FALSE)
    -
    -# }
    -# build multi-zone conservation problem with cuts portfolio
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_cuts_portfolio(10) %>%
    -      add_default_solver(gap = 0.2, verbose = FALSE)
    -
    -# \dontrun{
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print solution
    -str(s2, max.level = 1)
    -#> List of 10
    -#>  $ solution_1 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_2 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_3 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_4 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_5 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_6 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_7 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_8 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_9 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_10:Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  - attr(*, "objective")= Named num [1:10] 11601 11815 11790 11820 11618 ...
    -#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    -#>  - attr(*, "status")= Named chr [1:10] "OPTIMAL" "OPTIMAL" "OPTIMAL" "OPTIMAL" ...
    -#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    -#>  - attr(*, "runtime")= Named num [1:10] 0.007 0.006 0.007 0.007 0.007 ...
    -#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    -
    -# plot solutions in portfolio
    -plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem with cuts portfolio
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_cuts_portfolio(10) %>%
    +      add_default_solver(gap = 0.2, verbose = FALSE)
    +
    +# \dontrun{
    +# solve problem and generate 10 solutions within 20% of optimality
    +s1 <- solve(p1)
    +
    +# plot solutions in portfolio
    +plot(stack(s1), axes = FALSE, box = FALSE)
    +
    +# }
    +# build multi-zone conservation problem with cuts portfolio
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_cuts_portfolio(10) %>%
    +      add_default_solver(gap = 0.2, verbose = FALSE)
    +
    +# \dontrun{
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print solution
    +str(s2, max.level = 1)
    +#> List of 10
    +#>  $ solution_1 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_2 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_3 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_4 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_5 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_6 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_7 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_8 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_9 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_10:Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  - attr(*, "objective")= Named num [1:10] 11601 11815 11790 11820 11618 ...
    +#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    +#>  - attr(*, "status")= Named chr [1:10] "OPTIMAL" "OPTIMAL" "OPTIMAL" "OPTIMAL" ...
    +#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    +#>  - attr(*, "runtime")= Named num [1:10] 0.026 0.006 0.007 0.006 0.007 ...
    +#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    +
    +# plot solutions in portfolio
    +plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/add_default_decisions.html b/docs/reference/add_default_decisions.html index 335c3c602..7ca4eb629 100644 --- a/docs/reference/add_default_decisions.html +++ b/docs/reference/add_default_decisions.html @@ -1,103 +1,20 @@ - - - - - - - -Add default decisions — add_default_decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add default decisions — add_default_decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    This function adds the default decision types to a conservation planning -problem(). The default types are binary and are added using -the add_binary_decisions() function.

    +problem(). The default types are binary and are added using +the add_binary_decisions() function.

    -
    add_default_decisions(x)
    - -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    +
    Usage,
    add_default_decisions(x)
    -

    Value

    - -

    Object (i.e. ConservationProblem) with the decisions added +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the decisions added to it.

    -

    See also

    - -

    See decisions for an overview of all functions for adding decisions.

    +
    +
    +

    See also

    +

    See decisions for an overview of all functions for adding decisions.

    Other decisions: -add_binary_decisions(), -add_proportion_decisions(), -add_semicontinuous_decisions()

    +add_binary_decisions(), +add_proportion_decisions(), +add_semicontinuous_decisions()

    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_default_solver.html b/docs/reference/add_default_solver.html index 7126da15a..9c1ad2a2a 100644 --- a/docs/reference/add_default_solver.html +++ b/docs/reference/add_default_solver.html @@ -1,106 +1,23 @@ - - - - - - - -Add a default solver — add_default_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a default solver — add_default_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Identify the best solver currently installed on the system and specify that -it should be used to solve a conservation planning problem(). +it should be used to solve a conservation planning problem(). For information on the performance of different solvers, please see Schuster et al. (2020) for benchmarks comparing the run time and solution quality of some of the available solvers when applied to different sized datasets.

    -
    add_default_solver(x, ...)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    ...

    arguments passed to the solver.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the solver +

    Usage,
    add_default_solver(x, ...)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    ...
    +

    arguments passed to the solver.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the solver added to it.

    -

    Details

    - +
    +
    +

    Details

    Ranked from best to worst, the available solvers that can be used are: -add_gurobi_solver(), add_cplex_solver(), add_cbc_solver(), -add_lpsymphony_solver(), and finally add_rsymphony_solver().

    -

    References

    - +add_gurobi_solver(), add_cplex_solver(), add_cbc_solver(), +add_lpsymphony_solver(), and finally add_rsymphony_solver().

    +
    +
    +

    References

    Schuster R, Hanson JO, Strimas-Mackey M, and Bennett JR (2020). Exact integer linear programming solvers outperform simulated annealing for solving conservation planning problems. PeerJ, 8: e9258.

    -

    See also

    - -

    See solvers for an overview of all functions for adding a solver.

    +
    +
    +

    See also

    +

    See solvers for an overview of all functions for adding a solver.

    Other solvers: -add_cbc_solver(), -add_cplex_solver(), -add_gurobi_solver(), -add_lsymphony_solver, -add_rsymphony_solver()

    +add_cbc_solver(), +add_cplex_solver(), +add_gurobi_solver(), +add_lsymphony_solver, +add_rsymphony_solver()

    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_extra_portfolio.html b/docs/reference/add_extra_portfolio.html index 1f5d6725f..aca66fd97 100644 --- a/docs/reference/add_extra_portfolio.html +++ b/docs/reference/add_extra_portfolio.html @@ -1,106 +1,23 @@ - - - - - - - -Add an extra portfolio — add_extra_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add an extra portfolio — add_extra_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Generate a portfolio of solutions for a conservation planning -problem() by storing feasible solutions +problem() by storing feasible solutions discovered during the optimization process. This method is useful for quickly obtaining multiple solutions, but does not provide any guarantees on the number of solutions, or the quality of solutions.

    -
    add_extra_portfolio(x)
    - -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    +
    Usage,
    add_extra_portfolio(x)
    -

    Value

    - -

    Object (i.e. ConservationProblem) with the portfolio +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the portfolio added to it.

    -

    Details

    - +
    +
    +

    Details

    This strategy for generating a portfolio requires problems to -be solved using the Gurobi software suite (i.e. using -add_gurobi_solver(). Specifically, version 8.0.0 (or greater) +be solved using the Gurobi software suite (i.e., using +add_gurobi_solver(). Specifically, version 8.0.0 (or greater) of the gurobi package must be installed.

    -

    See also

    - -

    See portfolios for an overview of all functions for adding a portfolio.

    +
    +
    +

    See also

    +

    See portfolios for an overview of all functions for adding a portfolio.

    Other portfolios: -add_cuts_portfolio(), -add_gap_portfolio(), -add_shuffle_portfolio(), -add_top_portfolio()

    +add_cuts_portfolio(), +add_gap_portfolio(), +add_shuffle_portfolio(), +add_top_portfolio()

    +
    -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(600)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create minimal problem with a portfolio for extra solutions
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.05) %>%
    -      add_extra_portfolio() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem and generate portfolio
    -s1 <- solve(p1)
    -
    -# print number of solutions found
    -print(length(s1))
    -#> [1] 10
    -
    -# plot solutions
    -plot(stack(s1), axes = FALSE, box = FALSE)
    -
    -
    -# create multi-zone problem with a portfolio for extra solutions
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_extra_portfolio() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem and generate portfolio
    -s2 <- solve(p2)
    -
    -# print number of solutions found
    -print(length(s2))
    -#> [1] 10
    -
    -# plot solutions in portfolio
    -plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(600)
    +
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create minimal problem with a portfolio for extra solutions
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.05) %>%
    +      add_extra_portfolio() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem and generate portfolio
    +s1 <- solve(p1)
    +
    +# print number of solutions found
    +print(length(s1))
    +#> [1] 10
    +
    +# plot solutions
    +plot(stack(s1), axes = FALSE, box = FALSE)
    +
    +
    +# create multi-zone problem with a portfolio for extra solutions
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_extra_portfolio() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem and generate portfolio
    +s2 <- solve(p2)
    +
    +# print number of solutions found
    +print(length(s2))
    +#> [1] 10
    +
    +# plot solutions in portfolio
    +plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/add_feature_contiguity_constraints-2.png b/docs/reference/add_feature_contiguity_constraints-2.png index e875fa618fb4a8ee53414a587a73a5995f237b3c..ac18e537ec650f6f84b136aaeba3e0facee0cfed 100644 GIT binary patch literal 23784 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 literal 23783 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 OK7Pdd=YpR+fBheFja%*j diff --git a/docs/reference/add_feature_contiguity_constraints.html b/docs/reference/add_feature_contiguity_constraints.html index b71bf3e09..f4368a743 100644 --- a/docs/reference/add_feature_contiguity_constraints.html +++ b/docs/reference/add_feature_contiguity_constraints.html @@ -1,61 +1,5 @@ - - - - - - - -Add feature contiguity constraints — add_feature_contiguity_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add feature contiguity constraints — add_feature_contiguity_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Add constraints to a problem() to ensure that each feature is +

    Add constraints to a problem() to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of those implemented in the -add_contiguity_constraints() function, because they ensure that +add_contiguity_constraints() function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit. Additionally, this function can use data showing the distribution of dispersible habitat for each @@ -200,32 +117,28 @@

    Add feature contiguity constraints

    designated for their conservation.

    -
    # S4 method for ConservationProblem,ANY,Matrix
    -add_feature_contiguity_constraints(x, zones, data)
    +    
    Usage,
    # S4 method for ConservationProblem,ANY,Matrix
    +add_feature_contiguity_constraints(x, zones, data)
     
    -# S4 method for ConservationProblem,ANY,data.frame
    -add_feature_contiguity_constraints(x, zones, data)
    +# S4 method for ConservationProblem,ANY,data.frame
    +add_feature_contiguity_constraints(x, zones, data)
     
    -# S4 method for ConservationProblem,ANY,matrix
    -add_feature_contiguity_constraints(x, zones, data)
    +# S4 method for ConservationProblem,ANY,matrix
    +add_feature_contiguity_constraints(x, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY
    -add_feature_contiguity_constraints(x, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY +add_feature_contiguity_constraints(x, zones, data)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    zones

    matrix, Matrix or list object describing +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    zones
    +

    matrix, Matrix or list object describing the connection scheme for different zones. For matrix or and Matrix arguments, each row and column corresponds to a different zone in the argument to x, and cell values must -contain binary numeric values (i.e. one or zero) that indicate +contain binary numeric values (i.e., one or zero) that indicate if connected planning units (as specified in the argument to data) should be still considered connected if they are allocated to different zones. The cell values along the diagonal @@ -238,13 +151,11 @@

    Arg be a list of matrix or Matrix objects that shows the specific scheme for each feature using the conventions described above. The default argument to zones is an identity -matrix (i.e. a matrix with ones along the matrix diagonal and zeros +matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered connected if they -are both allocated to the same zone.

    data

    NULL, matrix, Matrix, data.frame +are both allocated to the same zone.

    +
    data
    +

    NULL, matrix, Matrix, data.frame or list of matrix, Matrix, or data.frame objects. The argument to data shows which planning units should be treated as being connected when implementing constraints to ensure that features @@ -253,18 +164,17 @@

    Arg which sets of planning units should be treated as being connected for which features using a list of objects. The default argument is NULL which means that the connection data is calculated -automatically using the adjacency_matrix() function and so +automatically using the adjacency_matrix() function and so all adjacent planning units are treated as being connected for all -features. See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the constraints +features. See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Details

    - +
    +
    +

    Details

    This function uses connection data to identify solutions that represent features in contiguous units of dispersible habitat. It was inspired by the mathematical formulations detailed in @@ -275,34 +185,38 @@

    Details cannot be used with feature data that have negative vales. Please note that adding these constraints to a problem will drastically increase the amount of time required to solve it.

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data can be specified using the following formats.

    -
    - -
    data as a NULL value

    connection +

    data as a NULL value
    +

    connection data should be calculated automatically -using the adjacency_matrix() function. This is the default +using the adjacency_matrix() function. This is the default argument and means that all adjacent planning units are treated as potentially dispersible for all features. Note that the connection data must be manually defined using one of the other formats below when the planning unit data -in the argument to x is not spatially referenced (e.g. +in the argument to x is not spatially referenced (e.g., in data.frame or numeric format).

    -
    data as amatrix/Matrix object

    where rows and columns represent + +

    data as amatrix/Matrix object
    +

    where rows and columns represent different planning units and the value of each cell indicates if the two planning units are connected or not. Cell values should be binary -numeric values (i.e. one or zero). Cells that occur along the +numeric values (i.e., one or zero). Cells that occur along the matrix diagonal have no effect on the solution at all because each planning unit cannot be a connected with itself. Note that pairs of connected planning units are treated as being potentially dispersible for all features.

    -
    data as a data.frame object

    containing the fields (columns) + +

    data as a data.frame object
    +

    containing the fields (columns) "id1", "id2", and "boundary". Here, each row denotes the connectivity between two planning units following the Marxan format. The field boundary should contain @@ -311,13 +225,15 @@

    -
    data as a list object

    containing matrix, Matrix, or + +

    data as a list object
    +

    containing matrix, Matrix, or data.frame objects showing which planning units should be treated as connected for each feature. Each element in the list should correspond to a different feature (specifically, @@ -326,15 +242,16 @@

    -
    - -

    Notes

    +
    +
    +

    Notes

    In early versions, it was named as the add_corridor_constraints function.

    -

    References

    - +
    +
    +

    References

    Önal H and Briers RA (2006) Optimal selection of a connected reserve network. Operations Research, 54: 379--388.

    Cardeira JO, Pinto LS, Cabeza M and Gaston KJ (2010) Species specific @@ -343,162 +260,161 @@

    R

    Hanson JO, Fuller RA, & Rhodes JR (2019) Conventional methods for enhancing connectivity in conservation planning do not always maintain gene flow. Journal of Applied Ecology, 56: 913--922.

    -

    See also

    - -

    See constraints for an overview of all functions for adding constraints.

    +
    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# create minimal problem
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.3) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with contiguity constraints
    -p2 <- p1 %>% add_contiguity_constraints()
    -
    -# create problem with constraints to represent features in contiguous
    -# units
    -p3 <- p1 %>% add_feature_contiguity_constraints()
    -
    -# create problem with constraints to represent features in contiguous
    -# units that contain highly suitable habitat values
    -# (specifically in the top 1.5th percentile)
    -cm4 <- lapply(seq_len(nlayers(sim_features)), function(i) {
    -  # create connectivity matrix using the i'th feature's habitat data
    -  m <- connectivity_matrix(sim_pu_raster, sim_features[[i]])
    -  # convert matrix to TRUE/FALSE values in top 20th percentile
    -  m <- m > quantile(as.vector(m), 1 - 0.015, names = FALSE)
    -  # convert matrix from TRUE/FALSE to sparse matrix with 0/1s
    -  m <- as(m, "dgCMatrix")
    -  # remove 0s from the sparse matrix
    -  m <- Matrix::drop0(m)
    -  # return matrix
    -  m
    -})
    -p4 <- p1 %>% add_feature_contiguity_constraints(data = cm4)
    -# \dontrun{
    -# solve problems
    -s1 <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    -
    -# plot solutions
    -plot(s1,  axes = FALSE, box = FALSE,
    -     main = c("basic solution", "contiguity constraints",
    -              "feature contiguity constraints",
    -              "feature contiguity constraints with data"))
    -
    -# }
    -# create minimal problem with multiple zones, and limit the solver to
    -# 30 seconds to obtain solutions in a feasible period of time
    -p5 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(time_limit = 30, verbose = FALSE)
    -
    -# create problem with contiguity constraints that specify that the
    -# planning units used to conserve each feature in different management
    -# zones must form separate contiguous units
    -p6 <- p5 %>% add_feature_contiguity_constraints(diag(3))
    -
    -# create problem with contiguity constraints that specify that the
    -# planning units used to conserve each feature must form a single
    -# contiguous unit if the planning units are allocated to zones 1 and 2
    -# and do not need to form a single contiguous unit if they are allocated
    -# to zone 3
    -zm7 <- matrix(0, ncol = 3, nrow = 3)
    -zm7[seq_len(2), seq_len(2)] <- 1
    -print(zm7)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    1    0
    -#> [2,]    1    1    0
    -#> [3,]    0    0    0
    -p7 <- p5 %>% add_feature_contiguity_constraints(zm7)
    -
    -# create problem with contiguity constraints that specify that all of
    -# the planning units in all three of the zones must conserve first feature
    -# in a single contiguous unit but the planning units used to conserve the
    -# remaining features do not need to be contiguous in any way
    -zm8 <- lapply(seq_len(number_of_features(sim_features_zones)), function(i)
    -  matrix(ifelse(i == 1, 1, 0), ncol = 3, nrow = 3))
    -print(zm8)
    -#> [[1]]
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    1    1
    -#> [2,]    1    1    1
    -#> [3,]    1    1    1
    -#> 
    -#> [[2]]
    -#>      [,1] [,2] [,3]
    -#> [1,]    0    0    0
    -#> [2,]    0    0    0
    -#> [3,]    0    0    0
    -#> 
    -#> [[3]]
    -#>      [,1] [,2] [,3]
    -#> [1,]    0    0    0
    -#> [2,]    0    0    0
    -#> [3,]    0    0    0
    -#> 
    -#> [[4]]
    -#>      [,1] [,2] [,3]
    -#> [1,]    0    0    0
    -#> [2,]    0    0    0
    -#> [3,]    0    0    0
    -#> 
    -#> [[5]]
    -#>      [,1] [,2] [,3]
    -#> [1,]    0    0    0
    -#> [2,]    0    0    0
    -#> [3,]    0    0    0
    -#> 
    -p8 <- p5 %>% add_feature_contiguity_constraints(zm8)
    -# \dontrun{
    -# solve problems
    -s2 <- lapply(list(p5, p6, p7, p8), solve)
    -s2 <- stack(lapply(s2, category_layer))
    -
    -# plot solutions
    -plot(s2, main = c("p5", "p6", "p7", "p8"), axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# create minimal problem
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.3) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with contiguity constraints
    +p2 <- p1 %>% add_contiguity_constraints()
    +
    +# create problem with constraints to represent features in contiguous
    +# units
    +p3 <- p1 %>% add_feature_contiguity_constraints()
    +
    +# create problem with constraints to represent features in contiguous
    +# units that contain highly suitable habitat values
    +# (specifically in the top 1.5th percentile)
    +cm4 <- lapply(seq_len(nlayers(sim_features)), function(i) {
    +  # create connectivity matrix using the i'th feature's habitat data
    +  m <- connectivity_matrix(sim_pu_raster, sim_features[[i]])
    +  # convert matrix to TRUE/FALSE values in top 20th percentile
    +  m <- m > quantile(as.vector(m), 1 - 0.015, names = FALSE)
    +  # convert matrix from TRUE/FALSE to sparse matrix with 0/1s
    +  m <- as(m, "dgCMatrix")
    +  # remove 0s from the sparse matrix
    +  m <- Matrix::drop0(m)
    +  # return matrix
    +  m
    +})
    +p4 <- p1 %>% add_feature_contiguity_constraints(data = cm4)
    +# \dontrun{
    +# solve problems
    +s1 <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    +
    +# plot solutions
    +plot(s1,  axes = FALSE, box = FALSE,
    +     main = c("basic solution", "contiguity constraints",
    +              "feature contiguity constraints",
    +              "feature contiguity constraints with data"))
    +
    +# }
    +# create minimal problem with multiple zones, and limit the solver to
    +# 30 seconds to obtain solutions in a feasible period of time
    +p5 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(time_limit = 30, verbose = FALSE)
    +
    +# create problem with contiguity constraints that specify that the
    +# planning units used to conserve each feature in different management
    +# zones must form separate contiguous units
    +p6 <- p5 %>% add_feature_contiguity_constraints(diag(3))
    +
    +# create problem with contiguity constraints that specify that the
    +# planning units used to conserve each feature must form a single
    +# contiguous unit if the planning units are allocated to zones 1 and 2
    +# and do not need to form a single contiguous unit if they are allocated
    +# to zone 3
    +zm7 <- matrix(0, ncol = 3, nrow = 3)
    +zm7[seq_len(2), seq_len(2)] <- 1
    +print(zm7)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    1    0
    +#> [2,]    1    1    0
    +#> [3,]    0    0    0
    +p7 <- p5 %>% add_feature_contiguity_constraints(zm7)
    +
    +# create problem with contiguity constraints that specify that all of
    +# the planning units in all three of the zones must conserve first feature
    +# in a single contiguous unit but the planning units used to conserve the
    +# remaining features do not need to be contiguous in any way
    +zm8 <- lapply(seq_len(number_of_features(sim_features_zones)), function(i)
    +  matrix(ifelse(i == 1, 1, 0), ncol = 3, nrow = 3))
    +print(zm8)
    +#> [[1]]
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    1    1
    +#> [2,]    1    1    1
    +#> [3,]    1    1    1
    +#> 
    +#> [[2]]
    +#>      [,1] [,2] [,3]
    +#> [1,]    0    0    0
    +#> [2,]    0    0    0
    +#> [3,]    0    0    0
    +#> 
    +#> [[3]]
    +#>      [,1] [,2] [,3]
    +#> [1,]    0    0    0
    +#> [2,]    0    0    0
    +#> [3,]    0    0    0
    +#> 
    +#> [[4]]
    +#>      [,1] [,2] [,3]
    +#> [1,]    0    0    0
    +#> [2,]    0    0    0
    +#> [3,]    0    0    0
    +#> 
    +#> [[5]]
    +#>      [,1] [,2] [,3]
    +#> [1,]    0    0    0
    +#> [2,]    0    0    0
    +#> [3,]    0    0    0
    +#> 
    +p8 <- p5 %>% add_feature_contiguity_constraints(zm8)
    +# \dontrun{
    +# solve problems
    +s2 <- lapply(list(p5, p6, p7, p8), solve)
    +s2 <- stack(lapply(s2, category_layer))
    +
    +# plot solutions
    +plot(s2, main = c("p5", "p6", "p7", "p8"), axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_feature_weights.html b/docs/reference/add_feature_weights.html index 2d828339a..6901f679c 100644 --- a/docs/reference/add_feature_weights.html +++ b/docs/reference/add_feature_weights.html @@ -1,108 +1,25 @@ - - - - - - - -Add feature weights — add_feature_weights • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add feature weights — add_feature_weights • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -198,36 +115,31 @@

    Add feature weights

    about how the budget should be allocated.

    -
    # S4 method for ConservationProblem,numeric
    -add_feature_weights(x, weights)
    -
    -# S4 method for ConservationProblem,matrix
    -add_feature_weights(x, weights)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    weights

    numeric or matrix of weights. -See the Weights format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the weights +

    Usage,
    # S4 method for ConservationProblem,numeric
    +add_feature_weights(x, weights)
    +
    +# S4 method for ConservationProblem,matrix
    +add_feature_weights(x, weights)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    weights
    +

    numeric or matrix of weights. +See the Weights format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the weights added to it.

    -

    Details

    - +
    +
    +

    Details

    Weights can only be applied to problems that have an objective -that is budget limited (e.g. add_max_cover_objective()). +that is budget limited (e.g., add_max_cover_objective()). They can be applied to problems that aim to maximize phylogenetic -representation (add_max_phylo_div_objective()) to favor the +representation (add_max_phylo_div_objective()) to favor the representation of specific features over the representation of some phylogenetic branches. Weights cannot be negative values and must have values that are equal to or larger than zero. @@ -236,262 +148,262 @@

    Details that the optimization process will favor cheaper solutions over solutions that meet feature targets (or occurrences) when feature weights are lower than 0.01.

    -

    Weights format

    - +
    +
    +

    Weights format

    The argument to weights can be specified using the following formats.

    -
    - -
    weights as a numeric vector

    containing weights for each feature. +

    weights as a numeric vector
    +

    containing weights for each feature. Note that this format cannot be used to specify weights for problems with multiple zones.

    -
    weights as a matrix object

    containing weights + +

    weights as a matrix object
    +

    containing weights for each feature in each zone. Here, each row corresponds to a different feature in argument to x, each column corresponds to a different zone in argument to x, and each cell contains the weight value for a given feature that the solution can to secure in a given zone. Note that if the problem contains targets created using -add_manual_targets() then a matrix should be +add_manual_targets() then a matrix should be supplied containing a single column that indicates that weight for fulfilling each target.

    -
    - -

    See also

    -

    See penalties for an overview of all functions for adding penalties.

    +
    +
    +

    See also

    +

    See penalties for an overview of all functions for adding penalties.

    Other penalties: -add_boundary_penalties(), -add_connectivity_penalties(), -add_linear_penalties()

    +add_boundary_penalties(), +add_connectivity_penalties(), +add_linear_penalties()

    +
    -

    Examples

    -
    # load ape package
    -require(ape)
    -#> Loading required package: ape
    -#> 
    -#> Attaching package: ‘ape’
    -#> The following objects are masked from ‘package:raster’:
    -#> 
    -#>     rotate, zoom
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_phylogeny, sim_pu_zones_stack,
    -     sim_features_zones)
    -
    -# create minimal problem that aims to maximize the number of features
    -# adequately conserved given a total budget of 3800. Here, each feature
    -# needs 20% of its habitat for it to be considered adequately conserved
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_features_objective(budget = 3800) %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create weights that assign higher importance to features with less
    -# suitable habitat in the study area
    -(w2 <- exp((1 / cellStats(sim_features, "sum")) * 200))
    -#>   layer.1   layer.2   layer.3   layer.4   layer.5 
    -#>  11.03585 606.63186  16.11015 108.65716  34.01731 
    -
    -# create problem using rarity weights
    -p2 <- p1 %>% add_feature_weights(w2)
    -
    -# create manually specified weights that assign higher importance to
    -# certain features. These weights could be based on a pre-calculated index
    -# (e.g. an index measuring extinction risk where higher values
    -# denote higher extinction risk)
    -w3 <- c(0, 0, 0, 100, 200)
    -p3 <- p1 %>% add_feature_weights(w3)
    -# \dontrun{
    -# solve problems
    -s1 <- stack(solve(p1), solve(p2), solve(p3))
    -
    -# plot solutions
    -plot(s1, main = c("equal weights", "rarity weights", "manual weights"),
    -     axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# plot the example phylogeny
    -# \dontrun{
    -par(mfrow = c(1, 1))
    -plot(sim_phylogeny, main = "simulated phylogeny")
    -
    -# }
    -# create problem with a maximum phylogenetic diversity objective,
    -# where each feature needs 10% of its distribution to be secured for
    -# it to be adequately conserved and a total budget of 1900
    -p4 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_phylo_div_objective(1900, sim_phylogeny) %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s4 <- solve(p4)
    -
    -# plot solution
    -plot(s4, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find out which features have their targets met
    -r4 <- eval_target_coverage_summary(p4, s4)
    -print(r4, width = Inf)
    -#> # A tibble: 5 × 9
    -#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1 layer.1 FALSE         83.3            8.33          7.35              0.975
    -#> 2 layer.2 TRUE          31.2            3.12          3.13              0    
    -#> 3 layer.3 FALSE         72.0            7.20          5.78              1.42 
    -#> 4 layer.4 TRUE          42.7            4.27          4.50              0    
    -#> 5 layer.5 FALSE         56.7            5.67          5.24              0.431
    -#>   relative_target relative_held relative_shortfall
    -#>             <dbl>         <dbl>              <dbl>
    -#> 1             0.1        0.0883            0.0117 
    -#> 2             0.1        0.100             0      
    -#> 3             0.1        0.0803            0.0197 
    -#> 4             0.1        0.106             0      
    -#> 5             0.1        0.0924            0.00760
    -
    -# plot the example phylogeny and color the represented features in red
    -plot(sim_phylogeny, main = "represented features",
    -     tip.color = replace(rep("black", nlayers(sim_features)),
    -                         which(r4$met), "red"))
    -
    -# }
    -# we can see here that the third feature ("layer.3", i.e.
    -# sim_features[[3]]) is not represented in the solution. Let us pretend
    -# that it is absolutely critical this feature is adequately conserved
    -# in the solution. For example, this feature could represent a species
    -# that plays important role in the ecosystem, or a species that is
    -# important commercial activities (e.g. eco-tourism). So, to generate
    -# a solution that conserves the third feature whilst also aiming to
    -# maximize phylogenetic diversity, we will create a set of weights that
    -# assign a particularly high weighting to the third feature
    -w5 <- c(0, 0, 1000, 0, 0)
    -
    -# we can see that this weighting (i.e. w5[3]) has a much higher value than
    -# the branch lengths in the phylogeny so solutions that represent this
    -# feature be much closer to optimality
    -print(sim_phylogeny$edge.length)
    -#> [1] 1.42105185 0.34712544 0.04100098 0.30612447 0.30612447 0.59604770 1.17212960
    -#> [8] 1.17212960
    -# \dontrun{
    -# create problem with high weighting for the third feature and solve it
    -s5 <- p4 %>% add_feature_weights(w5) %>% solve()
    -
    -# plot solution
    -plot(s5, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find which features have their targets met
    -r5 <- eval_target_coverage_summary(p4, s5)
    -print(r5, width = Inf)
    -#> # A tibble: 5 × 9
    -#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1 layer.1 FALSE         83.3            8.33          7.35              0.975
    -#> 2 layer.2 TRUE          31.2            3.12          3.13              0    
    -#> 3 layer.3 FALSE         72.0            7.20          5.78              1.42 
    -#> 4 layer.4 TRUE          42.7            4.27          4.50              0    
    -#> 5 layer.5 FALSE         56.7            5.67          5.24              0.431
    -#>   relative_target relative_held relative_shortfall
    -#>             <dbl>         <dbl>              <dbl>
    -#> 1             0.1        0.0883            0.0117 
    -#> 2             0.1        0.100             0      
    -#> 3             0.1        0.0803            0.0197 
    -#> 4             0.1        0.106             0      
    -#> 5             0.1        0.0924            0.00760
    -
    -# plot the example phylogeny and color the represented features in red
    -# here we can see that this solution only adequately conserves the
    -# third feature. This means that, given the budget, we are faced with the
    -# trade-off of conserving either the third feature, or a phylogenetically
    -# diverse set of three different features.
    -plot(sim_phylogeny, main = "represented features",
    -     tip.color = replace(rep("black", nlayers(sim_features)),
    -                         which(r5$met), "red"))
    -
    -# }
    -# create multi-zone problem with maximum features objective,
    -# with 10% representation targets for each feature, and set
    -# a budget such that the total maximum expenditure in all zones
    -# cannot exceed 3000
    -p6 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_features_objective(3000) %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create weights that assign equal weighting for the representation
    -# of each feature in each zone except that it does not matter if
    -# feature 1 is represented in zone 1 and it really important
    -# that feature 3 is really in zone 1
    -w7 <- matrix(1, ncol = 3, nrow = 5)
    -w7[1, 1] <- 0
    -w7[3, 1] <- 100
    -
    -# create problem with weights
    -p7 <- p6 %>% add_feature_weights(w7)
    -# \dontrun{
    -# solve problems
    -s6 <- solve(p6)
    -s7 <- solve(p7)
    -
    -# plot solutions
    -plot(stack(category_layer(s6), category_layer(s7)),
    -     main = c("equal weights", "manual weights"), axes = FALSE, box = FALSE)
    -
    -# }
    -# create minimal problem to show the correct method for setting
    -# weights for problems with manual targets
    -p8 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_features_objective(budget = 3000) %>%
    -      add_manual_targets(data.frame(feature = c("layer.1", "layer.4"),
    -                                    type = "relative",
    -                                    target = 0.1)) %>%
    -      add_feature_weights(matrix(c(1, 200), ncol = 1)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s8 <- solve(p8)
    -
    -# plot solution
    -plot(s8, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load ape package
    +require(ape)
    +#> Loading required package: ape
    +#> 
    +#> Attaching package: ‘ape’
    +#> The following objects are masked from ‘package:raster’:
    +#> 
    +#>     rotate, zoom
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_phylogeny, sim_pu_zones_stack,
    +     sim_features_zones)
    +
    +# create minimal problem that aims to maximize the number of features
    +# adequately conserved given a total budget of 3800. Here, each feature
    +# needs 20% of its habitat for it to be considered adequately conserved
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_features_objective(budget = 3800) %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create weights that assign higher importance to features with less
    +# suitable habitat in the study area
    +(w2 <- exp((1 / cellStats(sim_features, "sum")) * 200))
    +#>   layer.1   layer.2   layer.3   layer.4   layer.5 
    +#>  11.03585 606.63186  16.11015 108.65716  34.01731 
    +
    +# create problem using rarity weights
    +p2 <- p1 %>% add_feature_weights(w2)
    +
    +# create manually specified weights that assign higher importance to
    +# certain features. These weights could be based on a pre-calculated index
    +# (e.g., an index measuring extinction risk where higher values
    +# denote higher extinction risk)
    +w3 <- c(0, 0, 0, 100, 200)
    +p3 <- p1 %>% add_feature_weights(w3)
    +# \dontrun{
    +# solve problems
    +s1 <- stack(solve(p1), solve(p2), solve(p3))
    +
    +# plot solutions
    +plot(s1, main = c("equal weights", "rarity weights", "manual weights"),
    +     axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# plot the example phylogeny
    +# \dontrun{
    +par(mfrow = c(1, 1))
    +plot(sim_phylogeny, main = "simulated phylogeny")
    +
    +# }
    +# create problem with a maximum phylogenetic diversity objective,
    +# where each feature needs 10% of its distribution to be secured for
    +# it to be adequately conserved and a total budget of 1900
    +p4 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_phylo_div_objective(1900, sim_phylogeny) %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s4 <- solve(p4)
    +
    +# plot solution
    +plot(s4, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find out which features have their targets met
    +r4 <- eval_target_coverage_summary(p4, s4)
    +print(r4, width = Inf)
    +#> # A tibble: 5 × 9
    +#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1 layer.1 FALSE         83.3            8.33          7.35              0.975
    +#> 2 layer.2 TRUE          31.2            3.12          3.13              0    
    +#> 3 layer.3 FALSE         72.0            7.20          5.78              1.42 
    +#> 4 layer.4 TRUE          42.7            4.27          4.50              0    
    +#> 5 layer.5 FALSE         56.7            5.67          5.24              0.431
    +#>   relative_target relative_held relative_shortfall
    +#>             <dbl>         <dbl>              <dbl>
    +#> 1             0.1        0.0883            0.0117 
    +#> 2             0.1        0.100             0      
    +#> 3             0.1        0.0803            0.0197 
    +#> 4             0.1        0.106             0      
    +#> 5             0.1        0.0924            0.00760
    +
    +# plot the example phylogeny and color the represented features in red
    +plot(sim_phylogeny, main = "represented features",
    +     tip.color = replace(rep("black", nlayers(sim_features)),
    +                         which(r4$met), "red"))
    +
    +# }
    +# we can see here that the third feature ("layer.3", i.e.,
    +# sim_features[[3]]) is not represented in the solution. Let us pretend
    +# that it is absolutely critical this feature is adequately conserved
    +# in the solution. For example, this feature could represent a species
    +# that plays important role in the ecosystem, or a species that is
    +# important commercial activities (e.g., eco-tourism). So, to generate
    +# a solution that conserves the third feature whilst also aiming to
    +# maximize phylogenetic diversity, we will create a set of weights that
    +# assign a particularly high weighting to the third feature
    +w5 <- c(0, 0, 1000, 0, 0)
    +
    +# we can see that this weighting (i.e., w5[3]) has a much higher value than
    +# the branch lengths in the phylogeny so solutions that represent this
    +# feature be much closer to optimality
    +print(sim_phylogeny$edge.length)
    +#> [1] 1.42105185 0.34712544 0.04100098 0.30612447 0.30612447 0.59604770 1.17212960
    +#> [8] 1.17212960
    +# \dontrun{
    +# create problem with high weighting for the third feature and solve it
    +s5 <- p4 %>% add_feature_weights(w5) %>% solve()
    +
    +# plot solution
    +plot(s5, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find which features have their targets met
    +r5 <- eval_target_coverage_summary(p4, s5)
    +print(r5, width = Inf)
    +#> # A tibble: 5 × 9
    +#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1 layer.1 FALSE         83.3            8.33          7.35              0.975
    +#> 2 layer.2 TRUE          31.2            3.12          3.13              0    
    +#> 3 layer.3 FALSE         72.0            7.20          5.78              1.42 
    +#> 4 layer.4 TRUE          42.7            4.27          4.50              0    
    +#> 5 layer.5 FALSE         56.7            5.67          5.24              0.431
    +#>   relative_target relative_held relative_shortfall
    +#>             <dbl>         <dbl>              <dbl>
    +#> 1             0.1        0.0883            0.0117 
    +#> 2             0.1        0.100             0      
    +#> 3             0.1        0.0803            0.0197 
    +#> 4             0.1        0.106             0      
    +#> 5             0.1        0.0924            0.00760
    +
    +# plot the example phylogeny and color the represented features in red
    +# here we can see that this solution only adequately conserves the
    +# third feature. This means that, given the budget, we are faced with the
    +# trade-off of conserving either the third feature, or a phylogenetically
    +# diverse set of three different features.
    +plot(sim_phylogeny, main = "represented features",
    +     tip.color = replace(rep("black", nlayers(sim_features)),
    +                         which(r5$met), "red"))
    +
    +# }
    +# create multi-zone problem with maximum features objective,
    +# with 10% representation targets for each feature, and set
    +# a budget such that the total maximum expenditure in all zones
    +# cannot exceed 3000
    +p6 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_features_objective(3000) %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create weights that assign equal weighting for the representation
    +# of each feature in each zone except that it does not matter if
    +# feature 1 is represented in zone 1 and it really important
    +# that feature 3 is really in zone 1
    +w7 <- matrix(1, ncol = 3, nrow = 5)
    +w7[1, 1] <- 0
    +w7[3, 1] <- 100
    +
    +# create problem with weights
    +p7 <- p6 %>% add_feature_weights(w7)
    +# \dontrun{
    +# solve problems
    +s6 <- solve(p6)
    +s7 <- solve(p7)
    +
    +# plot solutions
    +plot(stack(category_layer(s6), category_layer(s7)),
    +     main = c("equal weights", "manual weights"), axes = FALSE, box = FALSE)
    +
    +# }
    +# create minimal problem to show the correct method for setting
    +# weights for problems with manual targets
    +p8 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_features_objective(budget = 3000) %>%
    +      add_manual_targets(data.frame(feature = c("layer.1", "layer.4"),
    +                                    type = "relative",
    +                                    target = 0.1)) %>%
    +      add_feature_weights(matrix(c(1, 200), ncol = 1)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s8 <- solve(p8)
    +
    +# plot solution
    +plot(s8, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_gap_portfolio.html b/docs/reference/add_gap_portfolio.html index f027ee2fe..3a162e15c 100644 --- a/docs/reference/add_gap_portfolio.html +++ b/docs/reference/add_gap_portfolio.html @@ -1,106 +1,23 @@ - - - - - - - -Add a gap portfolio — add_gap_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a gap portfolio — add_gap_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Generate a portfolio of solutions for a conservation planning -problem() by finding a certain number of solutions that +problem() by finding a certain number of solutions that are all within a pre-specified optimality gap. This method is useful for generating multiple solutions that can be used to calculate selection frequencies for moderate and large-sized problems (similar to Marxan).

    -
    add_gap_portfolio(x, number_solutions, pool_gap = 0.1)
    +
    Usage,
    add_gap_portfolio(x, number_solutions, pool_gap = 0.1)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    number_solutions

    integer number of solutions required.

    pool_gap

    numeric gap to optimality for solutions in the portfolio. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    number_solutions
    +

    integer number of solutions required.

    +
    pool_gap
    +

    numeric gap to optimality for solutions in the portfolio. This relative gap specifies a threshold worst-case performance for solutions in the portfolio. For example, value of 0.1 will result in the portfolio returning solutions that are within 10% of an optimal solution. -Note that the gap specified in the solver (i.e. -add_gurobi_solver() must be less than or equal to the gap -specified to generate the portfolio. Defaults to 0.1.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the portfolio +Note that the gap specified in the solver (i.e., +add_gurobi_solver() must be less than or equal to the gap +specified to generate the portfolio. Defaults to 0.1.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the portfolio added to it.

    -

    Details

    - +
    +
    +

    Details

    This strategy for generating a portfolio requires problems to -be solved using the Gurobi software suite (i.e. using -add_gurobi_solver(). Specifically, version 9.0.0 (or greater) +be solved using the Gurobi software suite (i.e., using +add_gurobi_solver(). Specifically, version 9.0.0 (or greater) of the gurobi package must be installed. Note that the number of solutions returned may be less than the argument to number_solutions, if the total number of solutions that meet the optimality gap is less than the number of solutions requested. Also, note that this portfolio function only works with problems -that have binary decisions (i.e. specified using add_binary_decisions()).

    -

    See also

    - -

    See portfolios for an overview of all functions for adding a portfolio.

    +that have binary decisions (i.e., specified using +add_binary_decisions()).

    +
    +
    +

    See also

    +

    See portfolios for an overview of all functions for adding a portfolio.

    Other portfolios: -add_cuts_portfolio(), -add_extra_portfolio(), -add_shuffle_portfolio(), -add_top_portfolio()

    +add_cuts_portfolio(), +add_extra_portfolio(), +add_shuffle_portfolio(), +add_top_portfolio()

    +
    -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(600)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create minimal problem with a portfolio containing 10 solutions within 20%
    -# of optimality
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.05) %>%
    -      add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem and generate portfolio
    -s1 <- solve(p1)
    -
    -# print number of solutions found
    -print(length(s1))
    -#> [1] 5
    -
    -# plot solutions
    -plot(stack(s1), axes = FALSE, box = FALSE)
    -
    -
    -# create multi-zone  problem with a portfolio containing 10 solutions within
    -# 20% of optimality
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem and generate portfolio
    -s2 <- solve(p2)
    -
    -# print number of solutions found
    -print(length(s2))
    -#> [1] 5
    -
    -# plot solutions in portfolio
    -plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(600)
    +
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create minimal problem with a portfolio containing 10 solutions within 20%
    +# of optimality
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.05) %>%
    +      add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem and generate portfolio
    +s1 <- solve(p1)
    +
    +# print number of solutions found
    +print(length(s1))
    +#> [1] 5
    +
    +# plot solutions
    +plot(stack(s1), axes = FALSE, box = FALSE)
    +
    +
    +# create multi-zone  problem with a portfolio containing 10 solutions within
    +# 20% of optimality
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem and generate portfolio
    +s2 <- solve(p2)
    +
    +# print number of solutions found
    +print(length(s2))
    +#> [1] 5
    +
    +# plot solutions in portfolio
    +plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_gurobi_solver.html b/docs/reference/add_gurobi_solver.html index ad0df63dd..a3cdc4b32 100644 --- a/docs/reference/add_gurobi_solver.html +++ b/docs/reference/add_gurobi_solver.html @@ -1,106 +1,23 @@ - - - - - - - -Add a Gurobi solver — add_gurobi_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a Gurobi solver — add_gurobi_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Specify that the Gurobi software +

    Specify that the Gurobi software (Gurobi Optimization LLC 2021) should be used to solve a -conservation planning problem(). This function can also be used to +conservation planning problem(). This function can also be used to customize the behavior of the solver. It requires the gurobi package to be installed (see below for installation instructions).

    -
    add_gurobi_solver(
    -  x,
    -  gap = 0.1,
    -  time_limit = .Machine$integer.max,
    -  presolve = 2,
    -  threads = 1,
    -  first_feasible = FALSE,
    -  numeric_focus = FALSE,
    -  node_file_start = Inf,
    -  start_solution = NULL,
    -  verbose = TRUE
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    gap

    numeric gap to optimality. This gap is relative +

    Usage,
    add_gurobi_solver(
    +  x,
    +  gap = 0.1,
    +  time_limit = .Machine$integer.max,
    +  presolve = 2,
    +  threads = 1,
    +  first_feasible = FALSE,
    +  numeric_focus = FALSE,
    +  node_file_start = Inf,
    +  start_solution = NULL,
    +  verbose = TRUE
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    gap
    +

    numeric gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10% from optimality).

    time_limit

    numeric time limit (seconds) for generating solutions. +The default value is 0.1 (i.e., 10% from optimality).

    +
    time_limit
    +

    numeric time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. .Machine$integer.max), effectively meaning that solver -will keep running until a solution within the optimality gap is found.

    presolve

    integer number indicating how intensively the +(i.e., .Machine$integer.max), effectively meaning that solver +will keep running until a solution within the optimality gap is found.

    +
    presolve
    +

    integer number indicating how intensively the solver should try to simplify the problem before solving it. Available options are: (-1) automatically determine the intensity of pre-solving, (0) disable pre-solving, (1) conservative level of pre-solving, and (2) very aggressive level of pre-solving . -The default value is 2.

    threads

    integer number of threads to use for the -optimization algorithm. The default value is 1.

    first_feasible

    logical should the first feasible solution be +The default value is 2.

    +
    threads
    +

    integer number of threads to use for the +optimization algorithm. The default value is 1.

    +
    first_feasible
    +

    logical should the first feasible solution be be returned? If first_feasible is set to TRUE, the solver will return the first solution it encounters that meets all the constraints, regardless of solution quality. Note that the first feasible solution is not an arbitrary solution, rather it is derived from the relaxed solution, and is therefore often reasonably close to optimality. -Defaults to FALSE.

    numeric_focus

    logical should extra attention be paid +Defaults to FALSE.

    +
    numeric_focus
    +

    logical should extra attention be paid to verifying the accuracy of numerical calculations? This may be useful when dealing problems that may suffer from numerical instability issues. Beware that it will likely substantially increase run time (sets the Gurobi NumericFocus parameter -to 3). Defaults to FALSE.

    node_file_start

    numeric threshold amount of memory (in GB). +to 3). Defaults to FALSE.

    +
    node_file_start
    +

    numeric threshold amount of memory (in GB). Once the amount of memory (RAM) used to store information for solving the optimization problem exceeds this parameter value, the solver will begin storing this information on disk (using the *Gurobi( NodeFileStart parameter). This functionality is useful if the system has insufficient memory to -solve a given problem (e.g. solving the problem with default settings +solve a given problem (e.g., solving the problem with default settings yields the OUT OF MEMORY error message) and a system with more memory is not readily available. For example, a value of 4 indicates that the solver will start using the disk after it uses more than 4 GB of memory to store information on solving the problem. Defaults to Inf such that the solver will not attempt -to store information on disk when solving a given problem.

    start_solution

    NULL or object containing the starting solution +to store information on disk when solving a given problem.

    +
    start_solution
    +

    NULL or object containing the starting solution for the solver. Defaults to NULL such that no starting solution is used. To specify a starting solution, the argument to start_solution should -be in the same format as the planning units (i.e. a NULL, numeric, -matrix, data.frame, Raster, Spatial, -or sf::sf() object). -See the Start solution format section for more information.

    verbose

    logical should information be printed while solving -optimization problems? Defaults to TRUE.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the solver +be in the same format as the planning units (i.e., a NULL, numeric, +matrix, data.frame, Raster, Spatial, +or sf::sf() object). +See the Start solution format section for more information.

    +
    verbose
    +

    logical should information be printed while solving +optimization problems? Defaults to TRUE.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the solver added to it.

    -

    Details

    - -

    Gurobi is a +

    +
    +

    Details

    +

    Gurobi is a state-of-the-art commercial optimization software with an R package interface. It is by far the fastest of the solvers available for generating prioritizations, however, it is not freely @@ -316,34 +212,36 @@

    Details please see Schuster et al. (2020) for benchmarks comparing the run time and solution quality of different solvers when applied to different sized datasets.

    -

    Installation

    - +
    +
    +

    Installation

    Please see the Gurobi Installation Guide vignette for details on installing the Gurobi software and the gurobi package. -You can access this vignette online -or using the following code:

    vignette("gurobi_installation", package = "prioritizr")
    -
    - -

    Start solution format

    +You can access this vignette online +or using the following code:

    vignette("gurobi_installation", package = "prioritizr")
    +
    +
    +

    Start solution format

    Broadly speaking, the argument to start_solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to start_solution must be a +

    x has numeric planning units
    +

    The argument to start_solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to start_solution.

    -
    x has matrix planning units

    The argument to start_solution must be a + +

    x has matrix planning units
    +

    The argument to start_solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -351,8 +249,10 @@

    Raster planning units +

    The argument to start_solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -360,35 +260,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to start_solution.

    -
    x has Spatial planning units

    The argument to start_solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to start_solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to start_solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to start_solution.

    -
    x has sf::sf() planning units

    The argument to start_solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to start_solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to start_solution must also have the same @@ -397,86 +303,85 @@

    References

    +

    +
    +

    References

    Gurobi Optimization LLC (2021) Gurobi Optimizer Reference Manual. -https://www.gurobi.com.

    +https://www.gurobi.com.

    Schuster R, Hanson JO, Strimas-Mackey M, and Bennett JR (2020). Exact integer linear programming solvers outperform simulated annealing for solving conservation planning problems. PeerJ, 8: e9258.

    -

    See also

    - -

    See solvers for an overview of all functions for adding a solver.

    +
    +
    +

    See also

    +

    See solvers for an overview of all functions for adding a solver.

    Other solvers: -add_cbc_solver(), -add_cplex_solver(), -add_default_solver(), -add_lsymphony_solver, -add_rsymphony_solver()

    +add_cbc_solver(), +add_cplex_solver(), +add_default_solver(), +add_lsymphony_solver, +add_rsymphony_solver()

    +
    -

    Examples

    -
    # \dontrun{
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.1) %>%
    -     add_binary_decisions() %>%
    -     add_gurobi_solver(gap = 0, verbose = FALSE)
    -
    -# generate solution
    -s <- solve(p)
    -
    -# plot solution
    -plot(s, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# create a similar problem with boundary length penalties and
    -# specify the solution from the previous run as a starting solution
    -p2 <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.1) %>%
    -     add_boundary_penalties(10) %>%
    -     add_binary_decisions() %>%
    -     add_gurobi_solver(gap = 0, start_solution = s, verbose = FALSE)
    -
    -# generate solution
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(s2, main = "solution with boundary penalties", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.1) %>%
    +     add_binary_decisions() %>%
    +     add_gurobi_solver(gap = 0, verbose = FALSE)
    +
    +# generate solution
    +s <- solve(p)
    +
    +# plot solution
    +plot(s, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# create a similar problem with boundary length penalties and
    +# specify the solution from the previous run as a starting solution
    +p2 <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.1) %>%
    +     add_boundary_penalties(10) %>%
    +     add_binary_decisions() %>%
    +     add_gurobi_solver(gap = 0, start_solution = s, verbose = FALSE)
    +
    +# generate solution
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(s2, main = "solution with boundary penalties", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_linear_constraints.html b/docs/reference/add_linear_constraints.html index 49a0e4931..25c84981f 100644 --- a/docs/reference/add_linear_constraints.html +++ b/docs/reference/add_linear_constraints.html @@ -1,102 +1,19 @@ - - - - - - - -Add linear constraints — add_linear_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add linear constraints — add_linear_constraints • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Add constraints to a conservation planning problem() to ensure +

    Add constraints to a conservation planning problem() to ensure that all selected planning units meet certain criteria.

    -
    # S4 method for ConservationProblem,ANY,ANY,character
    -add_linear_constraints(x, threshold, sense, data)
    +    
    Usage,
    # S4 method for ConservationProblem,ANY,ANY,character
    +add_linear_constraints(x, threshold, sense, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,numeric
    -add_linear_constraints(x, threshold, sense, data)
    +# S4 method for ConservationProblem,ANY,ANY,numeric
    +add_linear_constraints(x, threshold, sense, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,matrix
    -add_linear_constraints(x, threshold, sense, data)
    +# S4 method for ConservationProblem,ANY,ANY,matrix
    +add_linear_constraints(x, threshold, sense, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,Matrix
    -add_linear_constraints(x, threshold, sense, data)
    +# S4 method for ConservationProblem,ANY,ANY,Matrix
    +add_linear_constraints(x, threshold, sense, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,Raster
    -add_linear_constraints(x, threshold, sense, data)
    +# S4 method for ConservationProblem,ANY,ANY,Raster
    +add_linear_constraints(x, threshold, sense, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,dgCMatrix
    -add_linear_constraints(x, threshold, sense, data)
    +# S4 method for ConservationProblem,ANY,ANY,dgCMatrix +add_linear_constraints(x, threshold, sense, data)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    threshold

    numeric value. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    threshold
    +

    numeric value. This threshold value is also known as a "right-hand-side" value -per integer programming terminology.

    sense

    character sense for the constraint. Available -options include ">=", "<=", or "=" values.

    data

    character, numeric, -Raster, matrix, or Matrix object +per integer programming terminology.

    +
    sense
    +

    character sense for the constraint. Available +options include ">=", "<=", or "=" values.

    +
    data
    +

    character, numeric, +Raster, matrix, or Matrix object containing the constraint values. These constraint values are also known as constraint coefficients per integer programming terminology. -See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the constraints +See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Details

    - +
    +
    +

    Details

    This function adds general purpose constraints that can be used to ensure that solutions meet certain criteria (see Examples section below for details). For example, these constraints can be used to add multiple budgets. They can also be used to ensure that the total number of planning units -allocated to a certain administrative area (e.g. country) does not exceed -a certain threshold (e.g. 30% of its total area). Furthermore, +allocated to a certain administrative area (e.g., country) does not exceed +a certain threshold (e.g., 30% of its total area). Furthermore, they can also be used to ensure that features have a minimal level -of representation (e.g. 30%) when using an objective +of representation (e.g., 30%) when using an objective function that aims to enhance feature representation given a budget -(e.g. add_min_shortfall_objective()).

    -

    Mathematical formulation

    - +(e.g., add_min_shortfall_objective()).

    +
    +
    +

    Mathematical formulation

    The linear constraints are implemented using the following @@ -259,29 +168,29 @@

    Data format

    - +
    +
    +

    Data format

    The argument to data can be specified using the following formats.

    -
    - -
    data as character vector

    containing field (column) name(s) that +

    data as character vector
    +

    containing field (column) name(s) that contain penalty values for planning units. This format is only compatible if the planning units in the argument to x are a -Spatial, sf::sf(), or +Spatial, sf::sf(), or data.frame object. The fields (columns) must have numeric values, and must not contain any missing (NA) values. For problems that contain a single zone, the argument to data must @@ -289,28 +198,34 @@

    data must contain a field name for each zone.

    -
    data as a numeric vector

    containing values for + +

    data as a numeric vector
    +

    containing values for planning units. These values must not contain any missing (NA) values. Note that this format is only available for planning units that contain a single zone.

    -
    data as a matrix/Matrix object

    containing numeric values + +

    data as a matrix/Matrix object
    +

    containing numeric values that specify data for each planning unit. Each row corresponds to a planning unit, each column corresponds to a zone, and each cell indicates the data for penalizing a planning unit when it is allocated to a given zone.

    -
    data as a Raster object

    containing values for planning + +

    data as a Raster object
    +

    containing values for planning units. This format is only compatible if the planning units in the argument to x are -Spatial, sf::sf(), or -Raster objects. -If the planning unit data are a Spatial or -sf::sf() object, then the values are calculated by overlaying the +Spatial, sf::sf(), or +Raster objects. +If the planning unit data are a Spatial or +sf::sf() object, then the values are calculated by overlaying the planning units with the argument to data and calculating the sum of the values associated with each planning unit. If the planning unit data are a -Raster object then the values are calculated by extracting the +Raster object then the values are calculated by extracting the cell values (note that the planning unit data and the argument to data must have exactly the same dimensionality, extent, and missingness). @@ -318,189 +233,187 @@

    -
    - -

    See also

    -

    See constraints for an overview of all functions for adding constraints.

    +
    +
    +

    See also

    + - -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# \dontrun{
    -# create a baseline problem with minimum shortfall objective
    -p0 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_shortfall_objective(1800) %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve problem
    -s0 <- solve(p0)
    -
    -# plot solution
    -plot(s0, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# now let's create some modified versions of this baseline problem by
    -# adding additional criteria using linear constraints
    -
    -# first, let's create a modified version of p0 that contains
    -# an additional budget of 1600 based on a secondary cost dataset
    -
    -# create a secondary cost dataset by simulating values
    -# (note this requires the RandomFields package to be installed)
    -sim_pu_raster2 <- simulate_cost(sim_pu_raster)
    -
    -# plot the primary cost dataset (sim_pu_raster) and
    -# the secondary cost dataset (sim_pu_raster2)
    -plot(stack(sim_pu_raster, sim_pu_raster2),
    -     main = c("sim_pu_raster", "sim_pu_raster2"),
    -     axes = FALSE, box = FALSE)
    -
    -
    -# create a modified version of p0 with linear constraints that
    -# specify that the planning units in the solution must not have
    -# values in sim_pu_raster2 that sum to a total greater than 1600
    -p1 <- p0 %>%
    -      add_linear_constraints(threshold = 1600,
    -                             sense = "<=",
    -                             data = sim_pu_raster2)
    -
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solutions s1 and s2 to compare them
    -plot(stack(s0, s1), main = c("s0", "s1"), axes = FALSE, box = FALSE)
    -
    -
    -# second, let's create a modified version of p0 that contains
    -# additional constraints to ensure that each feature has
    -# at least 8% of its overall distribution represented by the solution
    -
    -# to achieve this, we need to calculate the total amount of each feature
    -# within the planning units so we can, in turn, set the constraint thresholds
    -feat_abund <- feature_abundances(p0)$absolute_abundance
    -
    -# create a modified version of p0 with additional constraints for each
    -# feature to specify that the planning units in the solution must
    -# secure at least 8% of the total abundance for each feature
    -p2 <- p0
    -for (i in seq_len(nlayers(sim_features))) {
    -  p2 <- p2 %>%
    -        add_linear_constraints(threshold = feat_abund[i] * 0.08,
    -                               sense = ">=",
    -                               data = sim_features[[i]])
    -}
    -
    -# overall, p2 could be described as an optimization problem
    -# that maximizes feature representation as much as possible
    -# towards securing 20% of the total amount of each feature,
    -# whilst ensuring that (i) the total cost of the solution does
    -# not exceed 1800 (per cost values in sim_pu_raster) and (ii)
    -# the solution secures at least 8% of the total amount of each feature
    -# (if 20% is not possible due to the budget)
    -
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solutions s0 and s2 to compare them
    -plot(stack(s0, s2), main = c("s1", "s2"), axes = FALSE, box = FALSE)
    -
    -
    -# third, let's create a modified version of p0 that contains
    -# additional constraints to ensure that the solution equitably
    -# distributes conservation effort across different administrative areas
    -# (e.g. countries) within the study region
    -
    -# to begin with, we will simulate a dataset describing the spatial extent of
    -# four different administrative areas across the study region
    -sim_admin <- sim_pu_raster
    -sim_admin <- aggregate(sim_admin, fact = 5)
    -values(sim_admin) <- seq_along(values(sim_admin))
    -sim_admin <- resample(sim_admin, sim_pu_raster, method = "ngb")
    -sim_admin <- mask(sim_admin, sim_pu_raster)
    -
    -# plot administrative areas layer,
    -# we can see that the administrative areas subdivide
    -# the study region into four quadrants, and the sim_admin object is a
    -# RasterLayer with integer values denoting ids for the administrative areas
    -plot(sim_admin)
    -
    -
    -# next we will convert the sim_admin RasterLayer object into a RasterStack
    -# object (with a layer for each administrative area) indicating which
    -# planning units belong to each administrative area using binary
    -# (presence/absence) values
    -sim_admin2 <- binary_stack(sim_admin)
    -
    -# plot administrative areas stack
    -plot(sim_admin2)
    -
    -
    -# we will now calculate the total amount of planning units associated
    -# with each administrative area, so that we can set the constraint threshold
    -
    -# since we are using raster data, we won't bother explicitly
    -# accounting for the total area of each planning unit (because all
    -# planning units have the same area in raster formats) -- but if we were
    -# using vector data then we would need to account for the area of each unit
    -admin_total <- rowSums(rij_matrix(sim_pu_raster, sim_admin2))
    -
    -# create a modified version of p0 with additional constraints for each
    -# administrative area to specify that the planning units in the solution must
    -# not encompass more than 10% of the total extent of the administrative
    -# area
    -p3 <- p0
    -for (i in seq_len(nlayers(sim_admin2))) {
    -  p3 <- p3 %>%
    -        add_linear_constraints(threshold = admin_total[i] * 0.1,
    -                               sense = "<=",
    -                               data = sim_admin2[[i]])
    -}
    -
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solutions s0 and s3 to compare them
    -plot(stack(s0, s3), main = c("s0", "s3"), axes = FALSE, box = FALSE)
    -
    -# }
    -
    +add_contiguity_constraints(),
    +add_feature_contiguity_constraints(),
    +add_locked_in_constraints(),
    +add_locked_out_constraints(),
    +add_mandatory_allocation_constraints,ConservationProblem-method,
    +add_manual_bounded_constraints(),
    +add_manual_locked_constraints()

    +
    + +
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# \dontrun{
    +# create a baseline problem with minimum shortfall objective
    +p0 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_shortfall_objective(1800) %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve problem
    +s0 <- solve(p0)
    +
    +# plot solution
    +plot(s0, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# now let's create some modified versions of this baseline problem by
    +# adding additional criteria using linear constraints
    +
    +# first, let's create a modified version of p0 that contains
    +# an additional budget of 1600 based on a secondary cost dataset
    +
    +# create a secondary cost dataset by simulating values
    +# (note this requires the RandomFields package to be installed)
    +sim_pu_raster2 <- simulate_cost(sim_pu_raster)
    +
    +# plot the primary cost dataset (sim_pu_raster) and
    +# the secondary cost dataset (sim_pu_raster2)
    +plot(stack(sim_pu_raster, sim_pu_raster2),
    +     main = c("sim_pu_raster", "sim_pu_raster2"),
    +     axes = FALSE, box = FALSE)
    +
    +
    +# create a modified version of p0 with linear constraints that
    +# specify that the planning units in the solution must not have
    +# values in sim_pu_raster2 that sum to a total greater than 1600
    +p1 <- p0 %>%
    +      add_linear_constraints(threshold = 1600,
    +                             sense = "<=",
    +                             data = sim_pu_raster2)
    +
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solutions s1 and s2 to compare them
    +plot(stack(s0, s1), main = c("s0", "s1"), axes = FALSE, box = FALSE)
    +
    +
    +# second, let's create a modified version of p0 that contains
    +# additional constraints to ensure that each feature has
    +# at least 8% of its overall distribution represented by the solution
    +
    +# to achieve this, we need to calculate the total amount of each feature
    +# within the planning units so we can, in turn, set the constraint thresholds
    +feat_abund <- feature_abundances(p0)$absolute_abundance
    +
    +# create a modified version of p0 with additional constraints for each
    +# feature to specify that the planning units in the solution must
    +# secure at least 8% of the total abundance for each feature
    +p2 <- p0
    +for (i in seq_len(nlayers(sim_features))) {
    +  p2 <- p2 %>%
    +        add_linear_constraints(threshold = feat_abund[i] * 0.08,
    +                               sense = ">=",
    +                               data = sim_features[[i]])
    +}
    +
    +# overall, p2 could be described as an optimization problem
    +# that maximizes feature representation as much as possible
    +# towards securing 20% of the total amount of each feature,
    +# whilst ensuring that (i) the total cost of the solution does
    +# not exceed 1800 (per cost values in sim_pu_raster) and (ii)
    +# the solution secures at least 8% of the total amount of each feature
    +# (if 20% is not possible due to the budget)
    +
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solutions s0 and s2 to compare them
    +plot(stack(s0, s2), main = c("s1", "s2"), axes = FALSE, box = FALSE)
    +
    +
    +# third, let's create a modified version of p0 that contains
    +# additional constraints to ensure that the solution equitably
    +# distributes conservation effort across different administrative areas
    +# (e.g., countries) within the study region
    +
    +# to begin with, we will simulate a dataset describing the spatial extent of
    +# four different administrative areas across the study region
    +sim_admin <- sim_pu_raster
    +sim_admin <- aggregate(sim_admin, fact = 5)
    +values(sim_admin) <- seq_along(values(sim_admin))
    +sim_admin <- resample(sim_admin, sim_pu_raster, method = "ngb")
    +sim_admin <- mask(sim_admin, sim_pu_raster)
    +
    +# plot administrative areas layer,
    +# we can see that the administrative areas subdivide
    +# the study region into four quadrants, and the sim_admin object is a
    +# RasterLayer with integer values denoting ids for the administrative areas
    +plot(sim_admin)
    +
    +
    +# next we will convert the sim_admin RasterLayer object into a RasterStack
    +# object (with a layer for each administrative area) indicating which
    +# planning units belong to each administrative area using binary
    +# (presence/absence) values
    +sim_admin2 <- binary_stack(sim_admin)
    +
    +# plot administrative areas stack
    +plot(sim_admin2)
    +
    +
    +# we will now calculate the total amount of planning units associated
    +# with each administrative area, so that we can set the constraint threshold
    +
    +# since we are using raster data, we won't bother explicitly
    +# accounting for the total area of each planning unit (because all
    +# planning units have the same area in raster formats) -- but if we were
    +# using vector data then we would need to account for the area of each unit
    +admin_total <- rowSums(rij_matrix(sim_pu_raster, sim_admin2))
    +
    +# create a modified version of p0 with additional constraints for each
    +# administrative area to specify that the planning units in the solution must
    +# not encompass more than 10% of the total extent of the administrative
    +# area
    +p3 <- p0
    +for (i in seq_len(nlayers(sim_admin2))) {
    +  p3 <- p3 %>%
    +        add_linear_constraints(threshold = admin_total[i] * 0.1,
    +                               sense = "<=",
    +                               data = sim_admin2[[i]])
    +}
    +
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solutions s0 and s3 to compare them
    +plot(stack(s0, s3), main = c("s0", "s3"), axes = FALSE, box = FALSE)
    +
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/add_linear_penalties-1.png b/docs/reference/add_linear_penalties-1.png index d22618d209c861750b69c280bff84189a5e0d8d8..977b687daab1d64f92306569f319cd05525a101e 100644 GIT binary patch literal 19282 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 literal 18945 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 literal 18697 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

    !jau@aFFR&NwLJ@k1*TOYgtYM>< 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 BbIt$& diff --git a/docs/reference/add_linear_penalties-3.png b/docs/reference/add_linear_penalties-3.png index c19539155c900c17e9872f93c4df3eb081c7909f..d29f32773802a463f107f7659eac59018a74fb78 100644 GIT binary patch literal 18814 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⪚PLA0S?zCaWnkM4|9mvc&6_p%ItO^!#2 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 X@ax?9FC&HG&LL;5&J>;Yy7j*RE|`&B literal 18469 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 - - - - - - -Add linear penalties — add_linear_penalties • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add linear penalties — add_linear_penalties • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +

    - - - - - - - - - - - + diff --git a/docs/reference/add_locked_in_constraints-2.png b/docs/reference/add_locked_in_constraints-2.png index 203f6da383717f359ae41129ea7b0b19ddee4212..e0ef5d151cb50074dfbe89bdd3634c1f701e648c 100644 GIT binary patch literal 13423 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 literal 13323 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 ycfrPe<05kd8V`hE93#pzi?LYT@`>YjCIC-9Z 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 - - - - - - -Add locked in constraints — add_locked_in_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add locked in constraints — add_locked_in_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + diff --git a/docs/reference/add_locked_out_constraints-2.png b/docs/reference/add_locked_out_constraints-2.png index 203f6da383717f359ae41129ea7b0b19ddee4212..e0ef5d151cb50074dfbe89bdd3634c1f701e648c 100644 GIT binary patch literal 13423 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 literal 13323 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 literal 13323 zcmeHtd012Dw)aMBsii_st)e2t)`41qI588VwH7@pQ5hlv2^9qd!e|&0!XT%m7J-5a zDnp6`LjnPbfDjUbRuLgY#0UWb$Q0d#FoYx~A>{4^&-XpweZKoV_ngyz?zz6t1A(3W zu6OPCUF)~j`mKHGxU0*mPd9uD0KlrlhrT}v04w4EVELI(K8CLJ=N8XEA0MAN;_^N8 z2@S;oMiTU~GUAX|Gys^UX@B&x#%Ip~z$W1E_xs)B3PggW`<1c2y$X4Qd@N7zi!XA3 zOCNo+?U#k)`wm`s;&A#z*-nr3d3B8&EbO|2OYshi-|V5fMsjjGpL+&AIq$nBbc%Ai z_J#Z3&hIdNmSwo+`(Mrghd1ec_esjariL~SQ{Dk1!lE1as-kEvLg=g_iYIGdVYtz9 zE;s%apGa0YbK_(5bjto&zt~}?sD^Mm^}DMFrgF?Z!^^}pXCJi`9KN$!w~**oZLojK zTl3P5!NwcQ!X~q^JwpXi-vB_TtA2?Yq{YY4@zQrIgv|>_jg|qxq?-|RZFPKt^y28E zIu}gA4TRyspXfxLGNmT|@-tSHE+jm(36K4~k?E4?t9fMB@rm|fc3$!@Y!Z67^m`3g zHr6Pw1;94QF9k&Qq*dqv5l`&DUk(6)w>6Y*mD8^%&mqPUIsmXU6~9?Sn*4xtkU|ow zO+LZ^$&rZL{9?U9cY(Vpg>_tgLJFtCXhnnNN#FFIO7|aUaC7g{3F@fJ4=bFVT2z2JP zq&npzkOk6t&D6xR&idL1i1?V~ML$*l=3QO7E369B^{QoN1njAgC)axnH9b}rg1h2i z%rT|6>8G!)kA%hEs=~!I4NQgAbGmP_8QuN{QsK$vclI1}4tyiVc2`1whca*=b5W=V!EVG%v zE1?qYXo#YOa4g+yK_^3D$kC-Vk`*UTF@Cbf9(5qrt|2%3|mSb`RrnrWdp|( z_J=;e{&jZwMu2VS>@BDrUP$s$r-E~)1HxNzN-=l^nu_#^S~Q5Ac^8;kUN_t96}9+7 zyAl81o1I?(K*i1!W592t2`{OBtfu*d0HcnPN^#S|CKnGO+)i`MdqBMTNT&wGIZf~b zD7Xa{m|{AB1c21P#OVP~=BR}WsnwX1%-fBX$|HpdSsh1m3!2}gKFN&LM({0fx@gQH58doTx<%;BnJi$8EvV{tJ-W3o?zK%= zz46+quNdE!^gSUDPC$;Zu7GU->?|?z7&__OFIwJ^+t8#;1w+>Q$cA(Jor2R_F(Hxe z0@wQk2L0-Y``eG!W=s_u01>&LYZaBf2G{WXDb0g(z`GJN1%2q2Q7D!0Y;9J2OO6Ej z;PF&UFdtK$UsUgs#V~=EH0f`GY$$a#G*^~Jq-t(ARw&U1R!Kg1>BWAFPk$S__t+mn zYEvX>r-JRSQT(4cvNITu+oAptc5Izl28iHk6irOqIrEPPDOj(FK?#-WI=4=v&XEko z=k$lRzTPTK>Ny-Hd130*S;O?^_P3C5t>5CH>9X~@BSCwFS7I=}gq%sz;~a^xX`U2j z*&sIYPAKS|rX~8dC~V#93(uBJ#Z@;3wkS|li<4W0nk$erCUjnlTZL6q!@Dx;|JuI} zNG1i4KnSbswhrw@dXO5uX4JP8$`!mG^bljL4hAWwPf7A7O|BFos~YLbp8{YnVouO! z-rme`Z)qcKPAZkM3^GgqV9D?G?{KdItD zs{~)_{frPX$UK?R`f~LoZ%6FzmtRbmCn~-S zmsndOMGwC=;Te%f{0nFveNP~a*t`r5IfFQSZ3yada~19UWOdvT>D9GKn|i;NTRSGS z$lYB~ktLNrXtf&WGfn43Q-`RD(l24789Y?s2+p-fcMOmQ%k(KicTF_?kyXM2uTKWb z46Pl_rYFqE=;@hVv@(o`dNLqwx7}${F(FXe2sQ^1$>Y`CkMu(uDau(8Cuv2V6r55g zNJqeeQJQLGMHbz(#~ta*r1E8seWH-W5NUn>im}zPzuF0^s3k*B47;@Z_CI5=6RT|? zr!0Nb2pX4~K_OC>ZR?J-lYS0}I+QP$1EOTCs~*4>&q}P>?2j;h$Auo;0>UqAq$yxA zSkCh|FFNPJjAboQK=#$tiZTWjd0yuKFnSwwnN;oIPCAcQ{6Vjvm{1-ur*K1q2G}_M z%SROjMhU|b#B{u-B)|mRVQv=ap`HS*k+ZA0V(Y4Vm)6FM{51!$?;d6kF3Q5ESHNeS z9J@?oou02Izk_>wdr!{1o+c9tuFa<+-*qnXk*YT@{eg&^P<-t?6XWUC5%Z(GqN=Kv zB{tq}?(k#&#{A&SHT5U(!|cs<#!1040+W@wMUk` ze`S;I=u*FcKm!^&6o5WI`|I!u?f>hpLih!Zo!>z24Gq8JkeNb*{X4WiG(HIUH{XL` zEeOX<9Fmror=2>{I?=9@5SuCod@GM3{`j;pi8T;Zml1d8wq$%c|A+PtO!}ad#+_Gi zVMTGdyl4qW9!A5sGtWLe;mNP{fGy4-m~W4fuM={wT^M^RikEf9CYCr(Cq#^#_v8v< zxfr62jZK1)W#nX~{1{>xz+h{FQ@VW6%tY`j$o63I2c*B2rvZL}q85YCz3kI$Kg#b884@PRiqQr+0pX zT-S*du!WN43A-7Sdrg|b`zebgYN)ItS~_&y^EYXo;)~Rw_xuqHJy_uKGsec9Wzm#U z=$U_22I0|oFFa4{=WO-*FWXm81*UFy?P@O+?`mj)}XZI(DD(TtmhYsv$?>|+OJ z_vK+1sgk64q_@bQSLkM>$38z7^1jQqq@359TZ%$HrP>7GTSxtBuW|ua; zUPi>F>^H5jx(S6-$bl*d?s&1*g}6A{!l8V|`lzN_a6PY*6SG2?y?92tipv~%Mq`f6 zL9TGPnJ@~wfA3z`5wh&Em6NckycKc}L`0eGP=Y_bwBNtfEbyS|O>!rdc0DfF10Pa* z0wWe%AA8!}clY?F9U>Ok0s+VHUd47`-AjV>s&+GAL2KjSsJ*8qB%HKQP5HfVge=85U-i1GgXT)MX}zg&9FuGr<4tEX zFupdk7F2LGXOLTT%GVD1`^_OEeGHfW*g2PNu+ted=@ZZd&PUjPVWVZ{x zQYD{~SN+%~)4M)nt*RS$!5iDua^3#8?@0j?IeRG2U079FslX||aIpsF~_#AM3q=SP3t9tAHAOqBOLh_o}F#PPx z0~IwH85xqFy!B3;GH#rWj&(;>nAuA0c7Q(z>>q#2 zNkp@a9oV*!*2qalLv-#m%?kj(!H1JTmym zNCsbgM{>)$m*bqs^_A4>Q@F?Mp8?M3rJT5#vbi-=s;W3Q) z7lTi=b))`7dxi};x*$jrA7jxUw8Lmj9h!Z+wNhnvIuwl|lZQl)L;a~c#O%AA!65kv z3r-mu z>eNo@8%P&kT%bi~-{!Eg9NYzVQx-cWxAPw3UFoZ{E|l>N(IN3gmeCbpgw$oVQ4NJg zv63Jz>~5Vn;xg#nY2(n)Jvb{Hpd5iCs>~5@ANt-1>dxM5B8X}y`Nr_$5y~!x3PY3& zjPTvZs*8U_j ziM8nAW?1vk*;5ej5IduwTh3nW`ZHdxYqRarjBuTK!Fq##Om6)i+QmZIo1!wm8 zB~|NdYiG8&zl>J7mX;7*e2g46LWvmU4m876WawA>!48 z`4gI({{epDV1bZ*75FTFefB(Nxk5Ts~s59IQmt8)lV>Nn7&0gw#c>cw9 z8f|bu^!zh&84Z~lt*6qFK#)$N1E|Wyw+ZSXX)$;Re+<94uwdi?uP}2M?bcKMtlQOU z6|0sI52eI6c-@p*umnW9H~TX0dM&Kz-L<5bTyR3Yu<%iJ;L>*g8Pv&AM+f*!gfQl1 zA^gGZb<-%Q@#7cE*PZA;y+n1anVY&6974<8$<#p~QY=2=VM8+MjE;bXShC_;P@jSo zz5Y!#8_*)}pC~nf4qcAgwW%8UcY{Sxm3P5CJX^HT6GgKsFY2=pJ5!a-CF_lP;X0}) zSs+gVAM@NO^`1tiv3w4%ePS}H!9jD`^<&YFrIQ~KWeVjH ziYn%{a|(UQkqWGfqGq?|Q>V)@3{JjaK>UZm1>KBB&mnZ6HD!xrW`?E*7u)6n4| zsw>9git@2wyaNPM(>x> zpfU*MiQJ>Tyjdqtlp&$UdUC^JPJK)iFJrjAw_%}2cohrk&=q zI`XhjZTg^1!UhgT?MsD|Xl$QS;I zigiV>DU7{bx@8=)zb~-V%nwKm`^Zo8g|^l#W1WR++?T}xdJ6?$6I9K5U?HQie)}bL zGKfBXdpU27mZA{{$?`rDS`%rrhae(}F>?_dhRV`g{ya5j@z-I9qphK^o`+5g22U{Y zeO{igj6BdCiQA&)nJ#@ni|r};lxACPDBVHLD-stjh*xnz_`nQO{Ln7&NhyP+yj$K1 z^jbp+G(;bTl$xD6NV?xhZM@U?_edBL^~=u(#pTW~EY>WfTP|6!nQKIoo;gEo+{9485F!sqXSa`~AJ$=zFbwB5M`HMhyy zcLCmeovJZDTB_iM7d<>yMR9JJT&`EJO$WtxAwJKqXKl|LYKmXVr_@3`gUpzy)9GKxI^Txn(#d(zj#7wT>$Du!-^I{0C=ElU8189FVMNTkLd zFeh;h*K^1d;jTvUZg+};*oj{+!5NF1KUJx^0WuA&G~Y>46O(Xfw*z{rtoNu?vcb6W6H1d|`X|z^GR;>F9eP?CHpZA=~ic>u_>SD`Z5y z+8P%c1@N=a^?1;rgKyBaX0cH6ha^DiT2A2Bc zG@q|>jBHO(+=1lRDCV97`x^!7$#yN#Kh>&5Ki0yp&{v>hZaLA(pgL=*^TL=WNyf2u zZFm-rwGjLNd?5pwyisZ({K3Kz3Yyb1bfM(uWir@OauL3ep_tQ@FLp!ZkXXzDP8ZQow_&4Qrki*?T8gaOqkcuqq$i z^Li6i6j>UtN)JqD^s9SnGQbFWXk*Xf-tM`0@3>v7rXRDZB;>L=Vy&#K=~Q zG2)vso0^QesJO&44bbZ{en3&6cbrcttXO6b1(1zpMYhtAeNYw(V&iphqNu=M|lo- zOB-$KZB*RF)f5TTG+aX}ZoKVK->9t!dA>X$)!-%*BRK>MmK_O)ctS&4BGdVl?;b6U z=*E$;1oxmUOdv9e^iQ7@|8bXt`6LsEe{A}ojL>g30B5g<(u`Dx=N1(+hsY3x3G+q7 zXypbBGqCZetQqvXQq-?wRx`$KE*KkY%XrkSaVzc*PiYPWdfLe3APRkvP# z`_sNpPEKYOCLK7qV4ia=eNpdXW6m!-G=TBS2Qk8Dg$BNdw|iXPTf5=Q&tBcBTxosd zYyd5t`16-dwyEc5;+CT5#{IUf*~MsAgDUcJW_0X4aHY@NQx(d1bgq_L z=WRL3+~T-d|He!8ImG4YpnUZ?yrn}CsgB>}9BqiJPg+3g`4Pm+l5LE0e9W}H2&aRz zEe&>rPXMzl^SG3gj*%<-Ct7NpS0`Nx%)jk_A?nSS5{IKv)lD&%ZTe9)&%&H<2nyCC zIe`|hs{{f2{^CjwXk1o8w$rNE5cW^oa1u6e2+!h z!i6sOd1x1-J$JDaq2h_E!=2_|zxz1$iVx?KQ-1iwlQ%z>%m-MFXDJeMvdrVp&)Z!g zASUes$+$Z5SjKp*oX(+{H&{at=lpfn(p!oDE9~Tdvyc3f&t5L*S`IiBrEal>T&w)i zIp!5<+)3F}m$`RXlg48<|mv6sVM6ZgrE?NqsBreLI4bvVbvFbaV* zb!smd| z3^lXtZcL7oBjK0B0>wPz`HR9@_X^Cv2h#qNR`elA z{nr7y&lCZ$M;jnu?mBQ3^z+i(7<=_}|EEm|Lh|7$_~iZ^WX8s`XNc{DJ#ACjsYgX` zoT3bIb*EnuBQMw4_XTEl#kdHwMvgjSpj6z=}nnkks|u2{SSZ>Q+s>cmO!n8n;dpNG6BD3fM z;j6aJr~9|n=8Xx?svmS4J?Y$CIlV>kN)o@7J8p72@WzW;pJ(4vE2?n}OyNH&metaT}|SNgq{O(Sy_&WJ3!30n_ip4LZORb>{P`)kkKXa)JY z%00&|we&`pvTeFhG|{ej4Z#l-^-Na`hD2KSO%S2=$cLf=<}Fq$XWSU#OE$zMyji?r ztxH({tx}*5jtTr(sc-Ebh&a)7{j?>|#R#GjP}w^UTV - - - - - - -Add locked out constraints — add_locked_out_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add locked out constraints — add_locked_out_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + diff --git a/docs/reference/add_loglinear_targets.html b/docs/reference/add_loglinear_targets.html index 46ae1e8aa..074d92a55 100644 --- a/docs/reference/add_loglinear_targets.html +++ b/docs/reference/add_loglinear_targets.html @@ -1,106 +1,23 @@ - - - - - - - -Add targets using log-linear scaling — add_loglinear_targets • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add targets using log-linear scaling — add_loglinear_targets • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Add targets to a conservation planning problem() by log-linearly +

    Add targets to a conservation planning problem() by log-linearly interpolating the targets between thresholds based on the total amount of each feature in the study area (Rodrigues et al. 2004). Additionally, caps can be applied to targets to prevent features with massive @@ -194,73 +111,56 @@

    Add targets using log-linear scaling

    in solutions (Butchart et al. 2015).

    -
    add_loglinear_targets(
    -  x,
    -  lower_bound_amount,
    -  lower_bound_target,
    -  upper_bound_amount,
    -  upper_bound_target,
    -  cap_amount = NULL,
    -  cap_target = NULL,
    -  abundances = feature_abundances(x, na.rm = FALSE)$absolute_abundance
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    lower_bound_amount

    numeric threshold.

    lower_bound_target

    numeric relative target that should be +

    Usage,
    add_loglinear_targets(
    +  x,
    +  lower_bound_amount,
    +  lower_bound_target,
    +  upper_bound_amount,
    +  upper_bound_target,
    +  cap_amount = NULL,
    +  cap_target = NULL,
    +  abundances = feature_abundances(x, na.rm = FALSE)$absolute_abundance
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    lower_bound_amount
    +

    numeric threshold.

    +
    lower_bound_target
    +

    numeric relative target that should be applied to features with a total amount that is less -than or equal to lower_bound_amount.

    upper_bound_amount

    numeric threshold.

    upper_bound_target

    numeric relative target that should be +than or equal to lower_bound_amount.

    +
    upper_bound_amount
    +

    numeric threshold.

    +
    upper_bound_target
    +

    numeric relative target that should be applied to features with a total amount that is greater -than or equal to upper_bound_amount.

    cap_amount

    numeric total amount at which targets should be -capped. Defaults to NULL so that targets are not capped.

    cap_target

    numeric amount-based target to apply to features +than or equal to upper_bound_amount.

    +
    cap_amount
    +

    numeric total amount at which targets should be +capped. Defaults to NULL so that targets are not capped.

    +
    cap_target
    +

    numeric amount-based target to apply to features which have a total amount greater than argument to cap_amount. -Defaults to NULL so that targets are not capped.

    abundances

    numeric total amount of each feature to +Defaults to NULL so that targets are not capped.

    +
    abundances
    +

    numeric total amount of each feature to use when calculating the targets. Defaults to the feature abundances in the -study area (calculated using the feature_abundances() function.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the targets added +study area (calculated using the feature_abundances() function.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the targets added to it.

    -

    Details

    - +
    +
    +

    Details

    Targets are used to specify the minimum amount or proportion of a feature's distribution that needs to be protected. All conservation planning problems require adding targets with the exception of the maximum -cover problem (see add_max_cover_objective()), which maximizes +cover problem (see add_max_cover_objective()), which maximizes all features in the solution and therefore does not require targets.

    Seven parameters are used to calculate the targets: lower_bound_amount specifies the first range size threshold, @@ -276,20 +176,22 @@

    Details that should be used when calculating the targets.

    The target calculations do not account for the size of each planning unit. Therefore, the feature data should account for -the size of each planning unit if this is important (e.g. pixel values in -the argument to features in the function problem() could +the size of each planning unit if this is important (e.g., pixel values in +the argument to features in the function problem() could correspond to amount of land occupied by the feature in \(km^2\) units). Additionally, the function can only be applied to -ConservationProblem objects that are associated with a +ConservationProblem objects that are associated with a single zone.

    -

    Notes

    - +
    +
    +

    Notes

    Early versions (< 5.0.2.4) used different equations for calculating targets.

    -

    References

    - +
    +
    +

    References

    Rodrigues ASL, Akcakaya HR, Andelman SJ, Bakarr MI, Boitani L, Brooks TM, Chanson JS, Fishpool LDC, da Fonseca GAB, Gaston KJ, and others (2004) Global gap analysis: priority regions for expanding the global @@ -298,58 +200,57 @@

    R Buchanan, GM, Angulo A, Balmford A, Bertzky B, and others (2015) Shortfalls and solutions for meeting national and global conservation area targets. Conservation Letters, 8: 329--337.

    -

    See also

    - -

    See targets for an overview of all functions for adding targets.

    +
    +
    +

    See also

    +

    See targets for an overview of all functions for adding targets.

    Other targets: -add_absolute_targets(), -add_manual_targets(), -add_relative_targets()

    +add_absolute_targets(), +add_manual_targets(), +add_relative_targets()

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem using loglinear targets
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_loglinear_targets(10, 0.9, 100, 0.2) %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s <- solve(p)
    -
    -# plot solution
    -plot(s, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem using loglinear targets
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_loglinear_targets(10, 0.9, 100, 0.2) %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s <- solve(p)
    +
    +# plot solution
    +plot(s, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_lsymphony_solver.html b/docs/reference/add_lsymphony_solver.html index ecf54f0b2..fa85e7289 100644 --- a/docs/reference/add_lsymphony_solver.html +++ b/docs/reference/add_lsymphony_solver.html @@ -1,106 +1,23 @@ - - - - - - - -Add a SYMPHONY solver with lpsymphony — add_lsymphony_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a SYMPHONY solver with lpsymphony — add_lsymphony_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Specify that the SYMPHONY +

    Specify that the SYMPHONY software (Ralphs & Güzelsoy 2005) -- using the lpsymphony package -- -should be used to solve a conservation planning problem(). +should be used to solve a conservation planning problem(). This function can also be used to customize the behavior of the solver. It requires the lpsymphony package to be installed (see below for installation instructions).

    -
    add_lpsymphony_solver(
    -  x,
    -  gap = 0.1,
    -  time_limit = .Machine$integer.max,
    -  first_feasible = FALSE,
    -  verbose = TRUE
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    gap

    numeric gap to optimality. This gap is relative +

    Usage,
    add_lpsymphony_solver(
    +  x,
    +  gap = 0.1,
    +  time_limit = .Machine$integer.max,
    +  first_feasible = FALSE,
    +  verbose = TRUE
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    gap
    +

    numeric gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10% from optimality).

    time_limit

    numeric time limit (seconds) for generating solutions. +The default value is 0.1 (i.e., 10% from optimality).

    +
    time_limit
    +

    numeric time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. .Machine$integer.max), effectively meaning that solver -will keep running until a solution within the optimality gap is found.

    first_feasible

    logical should the first feasible solution be +(i.e., .Machine$integer.max), effectively meaning that solver +will keep running until a solution within the optimality gap is found.

    +
    first_feasible
    +

    logical should the first feasible solution be be returned? If first_feasible is set to TRUE, the solver will return the first solution it encounters that meets all the constraints, regardless of solution quality. Note that the first feasible solution is not an arbitrary solution, rather it is derived from the relaxed solution, and is therefore often reasonably close to optimality. -Defaults to FALSE.

    verbose

    logical should information be printed while solving -optimization problems? Defaults to TRUE.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the solver +Defaults to FALSE.

    +
    verbose
    +

    logical should information be printed while solving +optimization problems? Defaults to TRUE.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the solver added to it.

    -

    Details

    - -

    SYMPHONY is an +

    +
    +

    Details

    +

    SYMPHONY is an open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. This solver is provided because it may be easier to install @@ -260,84 +166,84 @@

    Details lpsymphony package will solve problems using parallel processing (unlike the Rsymphony package). As a consequence, this solver will likely generate solutions much faster than the -add_rsymphony_solver(). +add_rsymphony_solver(). Although formal benchmarks examining the performance of this solver have yet to be completed, please see Schuster et al. (2020) for benchmarks comparing the run time and solution quality of the Rsymphony solver.

    -

    Installation

    - +
    +
    +

    Installation

    The lpsymphony package is distributed through -Bioconductor. -To install the lpsymphony package, please use the following code:

    if (!require(remotes)) install.packages("remotes")
    -remotes::install_bioc("lpsymphony")
    -
    - -

    References

    +Bioconductor. +To install the lpsymphony package, please use the following code:

    if (!require(remotes)) install.packages("remotes")
    +remotes::install_bioc("lpsymphony")
    +
    +
    +

    References

    Ralphs TK and Güzelsoy M (2005) The SYMPHONY callable library for mixed integer programming. In The Next Wave in Computing, Optimization, and Decision Technologies (pp. 61--76). Springer, Boston, MA.

    Schuster R, Hanson JO, Strimas-Mackey M, and Bennett JR (2020). Exact integer linear programming solvers outperform simulated annealing for solving conservation planning problems. PeerJ, 8: e9258.

    -

    See also

    - -

    See solvers for an overview of all functions for adding a solver.

    +
    +
    +

    See also

    +

    See solvers for an overview of all functions for adding a solver.

    Other solvers: -add_cbc_solver(), -add_cplex_solver(), -add_default_solver(), -add_gurobi_solver(), -add_rsymphony_solver()

    +add_cbc_solver(), +add_cplex_solver(), +add_default_solver(), +add_gurobi_solver(), +add_rsymphony_solver()

    +
    -

    Examples

    -
    # \dontrun{
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -  add_min_set_objective() %>%
    -  add_relative_targets(0.05) %>%
    -  add_proportion_decisions() %>%
    -  add_lpsymphony_solver(time_limit = 5, verbose = FALSE)
    -
    -# generate solution
    -s <- solve(p)
    -
    -# plot solution
    -plot(s, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +  add_min_set_objective() %>%
    +  add_relative_targets(0.05) %>%
    +  add_proportion_decisions() %>%
    +  add_lpsymphony_solver(time_limit = 5, verbose = FALSE)
    +
    +# generate solution
    +s <- solve(p)
    +
    +# plot solution
    +plot(s, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_mandatory_allocation_constraints.html b/docs/reference/add_mandatory_allocation_constraints.html index 959f164f3..2b53f0308 100644 --- a/docs/reference/add_mandatory_allocation_constraints.html +++ b/docs/reference/add_mandatory_allocation_constraints.html @@ -1,103 +1,20 @@ - - - - - - - -Add mandatory allocation constraints — add_mandatory_allocation_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add mandatory allocation constraints — add_mandatory_allocation_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -188,110 +105,106 @@

    Add mandatory allocation constraints

    with problems that contain multiple zones.

    -
    # S4 method for ConservationProblem
    -add_mandatory_allocation_constraints(x)
    - -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the constraints +

    Usage,
    # S4 method for ConservationProblem
    +add_mandatory_allocation_constraints(x)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Details

    - -

    For a conservation planning problem() with multiple +

    +
    +

    Details

    +

    For a conservation planning problem() with multiple management zones, it may sometimes be desirable to obtain a solution that assigns each and every single planning unit to a zone. For example, when developing land-use plans, some decision makers may require that each and every single parcel of land has been allocated a specific land-use type. In other words are no "left over" areas. Although it might seem tempting to simply solve the problem and manually assign "left over" planning units -to a default zone afterwards (e.g. an "other", "urban", or "grazing" +to a default zone afterwards (e.g., an "other", "urban", or "grazing" land-use), this could result in highly sub-optimal solutions if there penalties for siting the default land-use adjacent to other zones. Instead, this function can be used to specify that all planning units in a -problem with multiple zones must be allocated to a management zone (i.e. +problem with multiple zones must be allocated to a management zone (i.e., zone allocation is mandatory).

    -

    See also

    - -

    See constraints for an overview of all functions for adding constraints.

    +
    + +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_zones_stack, sim_features_zones)
    -
    -# create multi-zone problem with minimum set objective
    -targets_matrix <- matrix(rpois(15, 1), nrow = 5, ncol = 3)
    -
    -# create minimal problem with minimum set objective
    -p1 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(targets_matrix) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create another problem that is the same as p1, but has constraints
    -# to mandate that every planning unit in the solution is assigned to
    -# zone
    -p2 <- p1 %>% add_mandatory_allocation_constraints()
    -# \dontrun{
    -# solve problems
    -s1 <- solve(p1)
    -s2 <- solve(p2)
    -
    -# convert solutions into category layers, where each pixel is assigned
    - # value indicating which zone it was assigned to in the zone
    -c1 <- category_layer(s1)
    -c2 <- category_layer(s2)
    -
    -# plot solution category layers
    -plot(stack(c1, c2), main = c("default", "mandatory allocation"),
    -     axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_zones_stack, sim_features_zones)
    +
    +# create multi-zone problem with minimum set objective
    +targets_matrix <- matrix(rpois(15, 1), nrow = 5, ncol = 3)
    +
    +# create minimal problem with minimum set objective
    +p1 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(targets_matrix) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create another problem that is the same as p1, but has constraints
    +# to mandate that every planning unit in the solution is assigned to
    +# zone
    +p2 <- p1 %>% add_mandatory_allocation_constraints()
    +# \dontrun{
    +# solve problems
    +s1 <- solve(p1)
    +s2 <- solve(p2)
    +
    +# convert solutions into category layers, where each pixel is assigned
    + # value indicating which zone it was assigned to in the zone
    +c1 <- category_layer(s1)
    +c2 <- category_layer(s2)
    +
    +# plot solution category layers
    +plot(stack(c1, c2), main = c("default", "mandatory allocation"),
    +     axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_manual_bounded_constraints-2.png b/docs/reference/add_manual_bounded_constraints-2.png index 1f1839ce5f63106a95a4ca3491972a1f005a57d7..17b312a154d79baa048a1ab722328464b11fc174 100644 GIT binary patch literal 20345 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 literal 20277 zcmeIa2~<{Tt@D z`Jz!9l3nB9k00NyW1MG06^I~K&G2}ZO&yxN*kT3OUiGyzfBpLZF#?x!s~*oN=mzs@ zS1TwyJs0KLr%MiivT|{26fPed#lk}AyU#_RHB1Z(Tqj5A^7C_RpSpEF#RK#x{X2u|P~%?ez;)V_~Q51J7eV!+lP ztq#aKOMS9p%n6Mbe~jida11AXdMXoFsGB;_iLRo^(!l_{_! zjK-x^HH$J=!e#^(+*@PV|Gu640`H;5g{!RE;#jY3#;f)wu4?+-D`Gsv&{W}aZuf`P z3htZM`&~ZkgFrYgM-fq(w&P4KirK{vo9O}A!rFJs)2xSv$u0L{6%Km@%&by)-&Z`N zprA+BbhRy{BUq(GBspsJ^k}(q|N`NlRz2k>l+d+tm*BwkjbjZH>|jLthUn$z-I-k zV#($%2}H6bJcWmhAIT{6CxJJ`HqCAu-M*o^T2qW83St;Ycxw(fL>6-0| zwgzRAPq-GNX#Q#ARnMQi`q2I%AS7u%xOr;4UfEbs>@V5fteP0^J{~-X5*rzkr?@YL8k6@s@%y#59M1N+rgUzyx6sUPUFOQ^ z$eoDAJy|w$)iuaZ{sDqc$0{f=lGc8mKF;+vjJGg!zMHc;)Arz^9iO?Uo3vbdkO&?Y zQkAmFzPhv=38QY>yUVNmeeL;ugXP}m5D39VHfgy!z=;3qSX4N_18KE3KguT|Y#QLf ze6u2IzAS6I7Gi!vm%=_=>4Xi;SX3AAC}Q=l!bO{$C4CeQHy#DwcCSnDXa>dRck8`?K(;rj;ZNR9H0D&9`tIw@Y=h@nI?p-H2a!yV)24; z#B$Hb7Xpbr$o2=oAPJIXr5;q0P1PqaeN5Sa%8W9(_#uX$vijv}e<;?{nd~kZ7rtWO6pjb z-wQ^TurUmLbysh~NP+KWYtsTn#-*Ei+D`en2zynGvN2o-nkjjEMD#Hg71X<`oXx43 z5YXxj-W0pqqB9IgwlsK8S1wy(;+#ROvbQl_m2>39hKfGFJadb|TWvN_{_6=_od3$l zu#6tWO6TToFxvPpySMaJ4nOcH0x8?#5)*5=t_O=pB-uV2E2=JFUlVil(4>{eE#k}B z%8M_%MQ`9T&F0py^zfD!O}gWr`lI(6*1}#rRr60+`vOxCf%~~V-sk>gRYgQJKP$Cx z(}1tlqF;h^5B!iS_&G(BJQ>a~Rh$k5WR?>nI05b3zciqTcwy-33sXKz_n)fiW%uIonprY? zHaks1%cG+f6G)Lo0h!hZJR}Q^sP=Csbs8x`i~n_0%1+!s;kIgC0}o zyj}c3K1Omr1(*f?=JDC95a|m8%6C-^ZR-RALwwA#_sy0Q-g(2HfV#A$(kM^yN{vI% zwYXu*5+~77SE1sMTQ*bWKX<4XrwYcZbrY;i4jr0A`PpC2pc~q8O&`~Yl@r3$Oo81 zYxV$`(`C|&C1BWPfMHisX>+NU!qJUt;p*#8RBSMq#!0)%?3!L73F=1XJTO~B$THs) z+MYqB@}}GdeR&8r&(uo_Y#IIiIhvbCtf0dUD@{Z3Ywk@S5I(A|vK4*^mc%J}>lxrC znq@p%AwH{3Qgn>6-pP|ILRqV zi(RMEXYmiYJBMgNJw73M3SAFAu5k~!X68i_SbB^}@y3GpJXwWcr+4=hQ0|am5C#UO zew?&8%lsaqsl0VfSYSlPwZ&0H&7iUOhfOFRqqUsVH=vpE>34n=%vqR&1{q zS9HOutr)S~*gWgcoO?23sN2*V*5Ji%s2uc9I62^-@b(|rg&_EwgdK73{m~E*%JK{-u;J6Aq#jaH&$E(L zSp^+o$y;fDXuNs5P#EE7wIRTB;M6umQ*xjCMy@9%3KO+P8+Stfr7ElH}#1sv`%6B^9v1AY6bS%BwHB}Zv5%##!;~* zF=(C}5)t7$vq-@$4Bdh3iqe?CQ` zXglt>`yMmbfBQPzd!(>tI%M4HtkPq~@B?r3n9>h#8+M@ju{E82xius`s`Ml^f{~(( zSe&9GYW$-jQX({Gn%M>-;Z6KGR-9tWu)8jpsPneA94?vX4VuYgH~G_;-Q8x5L=&B4 zu>(J ztJW)DZdAB@HoK$@uZKWN1t9kIv4{%8e6N{KaxgZss?+(=)Xt>f2hI)o`8EvW3PAHbyAzdD*_vw*>pTDnHIE1Zl2lTbCtAXyRty@X`4;on;^&q(e76{44#Fguzz+5z%H~So>fabKJ};&<)gqQ}U;9$R8RIn^`Si-8 zOfQ;ov)YB+&oRcBQ(wBz>*pSe+Mw_mBsPq%<}I75mbrlvKd*6~Eb$p43FKs{z25@E zUKf#e?$l^gT=$oR&CYMSXIOD{l|~@@zQu-AQ5m|!)ZEI*Z5mM zu?NoIeh;)rwYRI;TJe9Ua5M@<=~@Qtxw4{Ox%h!td`>~NMBZu9v-F$MBIlYh8ebu) z#HBw<463~erIfI88)3h3cWz+xA0T*dbCpqg1t5prH9>}|C$02SuU5Vfl`8?xN-}SB zto4oc!ifC>Lt~2jK~b&W0+b0JhS7`-hRu;Tv(m;VG-h$q<;QmZT43CPq8rSyAXaIz z&i1IJvMI>d>Gi>G*zfYpVv(;BG8tRgNL6p1$$^N#92eAoHqCb?MxNCrcFV6R)U>&4 z)dmTS-Pg>fTIOCx$r3~0Rc1{fuk(9plyg)3{QUe{2l1iT>M8s@YpdDUla%_WdkAY3 z&SZbho0fdqSTfD{f~!Qt<3BH!IRPu;-QvL3xU~jGt~P{It&8bNR-nSLGOWDxG9fi5 z9ltbqk&XI1SH_>@6}7zza`9S2%csud@v@hW*Kiz8Z+Wg!_c;ZMO=w;aU!q7VZ<0She&iwTo` zVfKd6d7Y_C_eNsaDNsO7C|Se_d#Ug;E}^$S#MpTJJ=ucwW;NErKYREGg(q+1RRl75 zsyLkmX2b5Jql>*BnX+Kvjz+fu)u&!XDzcc=_<`$R^J~4Yu@Ea0f%2`mXgD}b-eqJkqGeKT+>X?kOiU8hgJqH4;;*_1Cew@~Ag z$&<}?NSeq>no`~4q!4)W%Ma!CT*9akbu{Iy0Wm06&SutNX{HQHI4A%H7^TW83a_a+ zq$6&H4^jy3t$9bnHoZw1lpG|C3C>eg;?9EgT!MV;!L;Wk{1Diwx-4ytSz5;^c)z^p zeZOb4Iib4*F58F5JXm{yo zDp*d&yxv4$-R~?qG1k^vH~1xibyrid=Ll~&pk^>MwKyq4j-hV*JBQ?5VqQa7D(Xcb zaiz&SMyBcdFpbnkhrfCXmPCt_UleBwXGfbSL4EBxRXd^hM+_Ew;t|Ql)lO5i? zF0`v1mW9E9$?Q4Uy-SSdVJpNNDUU21j`TdPeffdDbkO7dDMw&vLxwa6G!*#rs@8hF z`ufVK!CZRm#HOyy)e483e@;&|3YbmZ1r;8zWm*}X{wcCnZhR`7JZ11sZq~Fv6SSo> z`WCtMl12__Y828Gp8w(Yh4(ULXTcQ9|~~jA!SD_=3a5Tz}KnCEJ{xv4M4l>%#R@W7SEVpw~#Jb z87D+K0=LogdR8t+wnLQhkZOo1nCQ8_0R`~1B|wEoOwOLx99Sf92wibD2D7IX2XonM zeQ81!$qWii-`8`n^A;2cNAq9};hr)17kj9$tZ~()sO6yal_u~ONUg!3WCO|+-PD`? zuy$fAE7u1*N8DRbS>xVD5T^!q?(6Z0YTLhs-P2Q+zPUS6#f&ojG-@`% zx-;GGcmXwPTX?#MPSiA>;FrI+CT6d*3&stO!10nZkkM3vQ~GU@;)>cH$uUmF9V>ZCx5X2#ET8`^T|`| z{p9D%f1H+2Oa+BAIaihB=X($SpFB$W6@NAH&9D7G01LToonnGjyLVVWB8|{*gkW6- z>bAmPT=f#4(T9fRomxL2Y$+&EnP&TzH`ws#b>2H?kO@u=rO`qGg-zEdhia|e=Y?-n zydCc}{r0#xu`gN=Z%3-h3{l(8eBaZn8`!z`Rf|(N{z-|2v19M)hXXy1eZmTrIV7P! zG|wU}l^pU1ZOGOyZ+K+nB(=Gu;b;G>{_WC-*&QJgz&^Qmw8r18&$g&V=%d0#(%zfo zs-0i+9%K7;27XzKf)Fcb{$BCSAbM?-tf;js^WT<3NYKJ*&6)^<=UodMT*+o*6?K9ArZQd0J*jLVccvTm!=0WAgXd z;9pn!5az(rU}p$rrG)-Owthm|$}BAM>pc9UT>I`n?&wHA$A_eq3zd zTU`8*3wRqRHyRNN6xhylHjV|?ovzy&*fnCIoY=9Ci|6`;VlK7vpI@}jzT5u2x z<9FtWXRRL19Jo%unBmCpF*c52AT^C*nA_!~@%DZixn0qf{OA;F@^?*BeJhvUpt(=0 zV`~iA4cGMRz0NOW&Owj|pc5;dg{oL)f`)9#-GYr9?V_CiN^UEBY!(}QXD`@7u3Fgv zwQ3uVX#Dd*KrG3!qj^m5*Z2B!1Hf&z#8gIO#UYmvq1ooK$=f3Dllzen!HX7 z{^SS+h`qg=;+#-3?43F~XUc-X+=REbfYlJhXu&}80A>v^vGs8pD2{IR zhLv@iTvbEQPZyLtyQ56c4@Akd_TX@I>5X~sHO zNhD(Ebp>cxJRo+=bbH8vnmQWUk}G10w@yVs2c~FcsHBBZT)@ygCxTZoJyz$@WOk^- zyb=TJSZT!Wx*h8j7V5Y_T!A`C9yYI%;!$JR=-^EdxMX0wFT}U9qd}IsOkgu6^EK7@#nI#pnxu*>K(ar z{TBiRHIpA!8!yzea=c*RN`N%XVA(X|)m@oxzgshq5C7Z?016;8*zu(K&zKOJtryso zPdxpZYW#=K6}0NK4%G!B2u=_+BdyRIpQf$Dt@UtGmbS?OyM`?+(3LKn0~UbrGx4|| zMzIk3Av4&?8(3V)4z4yDs!$$d>I*bKfVEKdp zZh2S!#9z|s{}|~6F1Y`1j>seRz;%z(n-MVj&+CnKs5;OhWRQKPHJ=60wzBDPc)U|2 zo|&O#zt3w7;;n-;fg;GBf7FKn)C}E!C#h|VD*P*JM{vB}B#ykLagt1W7U>tsKVnrkV z{aGq}=0hGG$v#4l8|*-O{L*=l`=ydVU0Yu1Cof4OA}Msm(L#vmrBb7=g>&>QN%npu zRs36c{V9@a(&ti8N;!23B2myoPr)KG=@8UW$Ut2SCyeIHg<*B-!mnOlp`tg9Rxoa= zM9v>x`l2y0FIZL)EtQ?v4yFolHGa|^8j(|~Bv_iD^*yR4q8ZS&Ik=MJTPASi zsuIVoE%2F?;~-{&=f5c_?N2n@XXSFG$kF@7u-)y27Q8D6R(e&gF=dB){N0zhG$}35 zE-lr?dM@J6dQ>JE>vSvEvP+__QhvKDN`i_XVp6UWvNI>KHVenWs?g?koKry|?8s*G z^Aot3uFdlPKZ`atj-Tn7Y3A8maO(@f{@=~Kx~R)j%^p{YaF4uu)hW+P2W}Ueke}ao zQWk|zT`nd{gg?=frVs6$LduvrS&d39@^Y6S0`&rwC*~`$f}=T8C(FZNFu3||KjkLM zS`fuw!zvq3z8F3(UYdcp_0_@Mz?q(D73l=_>@J8#YDfipRM>6Vv>7yLAzy zthA92{B+~ie%(Oz{Eu=ix;A+c2**8y1C>Ap8g>$})8?R=to|D8KQBV#=Ic zP}@A_BgnLlG7c7Q<36?Vn;Eiv&WAXB!4KeYX`|B(zkJ)A^}LG^H&oSWWkp6Tcs=@7 z{+yrWsi=07sc1Ba=?P<3;f*8*M~~88|xyl9x_NVEnfLX13EmP z`5r<#!52W#jF(1Z-wl_~ed;N(p`Faf-E$Fx5Q~rc6;<9q0%<%f3_{l;r$DQY_bqS7 zG@tv1=RHU7s8(C#)ME~mpEyJ*_09JJGR8hl1wleW@v}v+^S}jYYCf|TV)l@0v?*>M za@*G(XNR$k6w4G#ZBik#?+1@j0Co{52S)BTUPX>UG@QKY%`Zn_o8&X>{A2-^xsFTI zC?EXg+glZ0C$>o@J|^j({O%mxiWsu9JypFX{$|*hNQ6+a8?4)j`ubeAN~pTNygsN; z!{vjLeUV|c55M$^s|DXjnW6VWfvl97f%)k{t<7%T58cDWOV>$$MSi2rSe4RU#w@BM zP~RUadKFiE!o>|&Lo2O!ZGa%FdiJg0(RxuKc6SCt$)>|wk)DJY=e)jnJ(+k+$FyHiWQ|M}_pg`?)U-t4gbUSU z1+o?DI!DTB98xgkmtGhn-lY7Y%pYv+lw-tw>dq()&;@C@?{{r9Y5F(eo#;VF$zRJ3Hb7bKSj{_2lS1SD68`A>m63L5nUK@E*hetT|AQ zc$mw6+HTLT?e5Sbk9#7`Jewh8f54}pQKeizoCbmG&~?a5kfZz|<=eUs9dRzvLV9zq z+Wi9)9lbq^B-#0-_~{~lE-DzbED*Epi$Ogb0?_lWweetgO)SW_AR+{y($#zV5gG`e zYe?YbMXarIi^sf7mUeU-RhMXyy+UxhpX;^r03oT(JreT^H;0LURzu3MpcJ72Ch&B% zwX88|#CiIK^@xevhAerx#3^`qkg^;q2nCobV;^k2BO2^M@DGN$Bf#WSWpYRhUijX8 zf;L^9w!7H=cCYF1#y5j)!c4hPzo7!w3o>g{sJ#yH>4?eoO}H z4WP00cmioQ^ej0BXH4wd2#xU*$2tN!G~vMIO7wh)Y#9x`QRXrk*>`L`!!`M0d9CJF z)|GYIZ1Gsy0UL0&R;Muy+{w8f!OoZ%+Q6_4skoZp0zR-puT+ibUEi4p%UFFQ9BX5y zd|D1(;QU$c8eT_;%6XHkNYENCE2$CAfwVE`E{t#o{coE*SdiQ+apZ_igGJ0mNz zQtJMZNc>(*xVLQ@vNJe79G5ixz@c^f-{ie~x<7xgZ{#2`4OqaAdxt`+T=`CF0m)U8 z2@aBbdXuixw?T*vadj&Ca8UGka;dQZm~kunaaddA4sf6@l{6#tH}n3oLFAjuSCBzd z<>Xp?uD2t<%(61|JTN>|rg+?_+@;(3d)e9a>^*8>_U=jq;F#eP2cUq8Lgq6_z*OrG ze`+j}6oeriB1!_rs&D0fUmBPyPXn6K;poONo-{bH@{QI`dH)rr%Ax=(phceyg-TI@ zRp0x2QUk26i(tV+2Hj8lC?ni^-v;?ybSruU1M7tNJI5sb{3n7H&-`_eaPd4zNA&hI zOT#@YcmKEQ%YEeD_0j}`(2}poZAr!*4SVVfzo7ncaU`~`A|p_R++k$Yv9$h!iPrV= zZZH3KVx46)8B${4WV~i5g#7n6vBBMUTuMs{SwBE6CGTE7{0;IO^y1zZ51*UFh0_L> z{^0FkHV2Y1ZzZZ~h267R4T|nTst&(+Pfu8l33_mzVUFUNg(}am>ehi~Qmdi@t|b9; z?sMYJ>tA)pCI0u7kla6)Nd8hw-rhR|Z3CE5IwC({gX`qW?>F3W8-cdITQwr7hBA_#C_VbtjUBhKTJ(Ho z3e@%vZ2XMjTMv`3eZSRVh7NbB4NF9Q0-yZxn;?2eUl<$cR#8U1Nzz{;@oC(~y?HeplE1cQcC3Hg3^XgWkPLH)p-#;Cup@C?g-V7{~w1{H! z)WAE;uribW-*4q^z67j=#=JjUaDx50cYZ!uJt;V?qoX5clFrV_q!l$MKuVP1{2s@`8%;@|-21qoR1LBIP@N&7qtHD+~Z^*R>dv?L`!n7-K*6jIHnHcZb9w7C{@oVZrlN%0yzIQL?nh*@uJ@;LFh$QXWIB@ z^v-*J*Wy2&`XZdjQEKUC>h5gJ-Tbzg4zV&S+D0_O?^7!^%VHbT3snSL*pc1q`~*#X z(MbZLnAbC?fij1)oQc*XcT1P3NOFM@{wTEZVdcBqSvsCQWwr}ubs=sl(~=Q{1AkmG zO?Om$TM3&akai9nenuX1f;7@Ll z*CM$b=gP%8TKbJOyu_iTP19}mp>B+~{R?jr1b8EKnV>^p@TS?I=|L>cPfuEMl~9th zvaXaiFfA-^CU@1x6~&vkUtJ{EH?%6|Zhjuzz}L$98o1q=-(%cB1;F7 zlgwqpQ~GDVVRMP^XM;=S9GexqAD`#l3cdfGq|piEmy&oHBkXtM^ZEbH)do9VG}5xg z)@Ha!mG{5sI4m6gIvRHQENqa}7x1^v^xyGN+%__1bLD#Y4i~r|)7Bv?4}ZM&(hpg( ztKXy7$c$iuApf+|_X2nBg;o3yi)zZ>NwlZZ#i#)6ehe*o<>fB2n$EL3qn&y))kbkW zE1!O{m2UWsZ)M^Ea&Ot^8vG6yLfe}Z3fQX?9((X6Y2WI(kIYdUoS$&dqD7i{EBwDf zqWK05pPDUhi7=ORJlg)`+k7!cHut4O@mP@Rq$@IiG*i+pQn7D1`MnMfP7XGgcmY@q z0dE?eY(ahkAz_($r~WJq59}mBL>?Ubxl}`PW^CHY2!8WE*9WK#!{Q6BU zg(nARwkSd@EI+0DIT>MLlfP@2S!hpx*q8X_`+kP!b3+5pMf(aqr4t%rFQ59lT}MHo zb9zh%nFff_8|_y<6aXzI%(-q@CTHPl~M0&d{-ltg%lYMZPWXxuXwiYG4)FlH>zBU~lKMt)clt zIH-Zn%N17qfapuTF2=$hgc&o z8`owq-)LPsuoGRf`b&FQ!{5RdU`_JoU)8Eq^yF zQ<<=?RHM)y4b|>Vgxm3N_WhhBa8MjYPe(4b`$P|5Mx;fG7?9c1mK&x8R*R!WFgKvG zWMcj^2SQ~PD*5HxErUC)Hq#2~WP)m#Y$iK-Ld7n(yqe(y@-W3(Ah1ZhWIzstx`TNi z-40c!~&M?0YS_=4_vUJH(cZu0c#z45%1hg3F(Ly4Dh3j?m9Lo6o z14vo!#=hcW=qz+kzEM8JMLPEi9Wtj==p$r2Wtkk`urgny8PkASXq1#WSpZ^VW>_zj%WY3C@=sZ>Yao|=~Ljz-CqO&b} zh*9upA|Svyf~V!?HsM+GppYwB>6CQP7^$|Hxu6qVn6+EyNNDuE=jVooJF)f8TD>hx zlt%e`Zg0%n zsk-g*`Fbzue^qb(`|0`b6zg9td=t+9l{kPAd+X(b;+Ti!FHvm^J!mM9;*e?5z{ui? zrI%)dffTLc`EXNc?Zn$FrHzXtpDqH-(t`LvY7zZq$g;5+!5lY?hdqJisP{sk+hfeF zVp7KSB=iFBbVNqT^j%Xs9OYr6H1qA&%3n^>{*js^wm_%RiS(a>?pH$>*rf>=eWdyZHs#d%I?$p&bIj`LUI5W`!=> zJWK7mNp86~kq7b!i*rp}mk_T_{uS5;{uyjQPS&n74dd5>41qYH2)pw-b~p*tPwH`+ zR|Z(Q%2BYt@h)bvPO7UjynIa@N!$BwXYO#UeF6rq8g!8{0~=B
    v+vYp0AO6aVo z)#`7tsJw(ptw%XcQ#=NooF}g?6r2Dl0b!h8JN@PUEqLGnOMYk8$u{)sp1`w|-ao=Y zIx$Pwu*;Jn4S2pE0*iqoKpdGLYdh{5a z4+G!`cLp#xnIV_fplJ0GmJJV-DY?+fn$dd9O(J`Db_$21Ont95*maB z9S1eo3$pTPgHCt4-S?fh|8d>=FBQX|wd`LP$m34o-C2*9qk1YaMZ>gq&cz`wo@hMQGNN*^FFIU1qB4O^T%kI_QJfvEcY6`-#3MsP1EU8c z*yGon3-OFL!D9$HJl-s>$P~L83fPE(fSTG1Nha7r$^8ExKU-=d?S$(_Bk=+;n#%o-F=YT<7 zyg`6metTuSa@(=G`LGL1#zld(!|G*`tva=qx^=|ZyNn*@92h2N>A+mYevl$YgRdL` zgAcSYN>HR|Ia}##=TXmf)LF8zpm@@xXTR5!DcH=HM#Mn1&AbFm7F;j4BwmZ``d9q+|bckZQ8j)XzKap@cTJFLmS(9fWD(IGq z@EkvzJSH7iNMOZ@E`m~vxLd^tD-o%Pk|{lhJEi`cJ7)z zXFJd@>S`OwQxOc+C-h3rHC6BZaHp|~*vuBb77>WMH1xoZQ2kN0KK1hh;J+gPS95Cr z+{g1j`f~r3+5IoACddBXKh2E)e8t&+j-#LxatBhKLUweNTaGL5te>&z(EZ@`tWYdN-Q|9kq}bh>z>Jx6(@OijZUhoXN;yYy2Mc#(9DP4V|DA{ z99lL+Y@Gd`j!AQuhftRW>v%luY$NO}Wgi&TVgYxf5grx@+yvikQV8)Z$S}^Mfp6>F zC7|P@6r&nqi%jveJ`u-&+wwZ%5^xl%jSk-rb_7i8pqPNg$ul3Mc)ltOg8{TGT3-2xXC5-at&p?OjL1fNsunY18;M9tc# zRO*8EYcpDv?1@D90R%aZiTU8p(6rRa>ADDs@^#vLK$0}rHnn>&oiHT&&C~3UIR7Tc z3{(5Cgdp1Ol`a`HCIvKemmQ#fqqk4v@cd)ThG`a$Oenq zI=cio$~JG#$+Us7PH9 z>BSlq&0hNnURIbT?Jd;!zyfm|fiq8G)k;Z3|xgXAL#Q;irq`j4)2-H zPvZLRPaI1jhw+yY{_dtuu0r)Zi_(t~AsVht6GKz=ae8C9%*PPoA&}MmMiue^N}bzV?1~IQ<%drp8k?~iptYJ2HiR@=a&uDv(y{u216s9fsHOI? zpgx_i3l4Z`?(U27(O3ANO~e2EIQxq=zb$Pn0yqT*a8z5BVY|Un1E%Lj zKM4jfSn6e10}9q9$+FCcl6X zwq?@F^q~eJ@=MBY!}o9T^ad%ERcqLOf-A2Ko!j^JOaHLm`)!?V|HF^I+Myad7cqUH z>PkF<)$+_{UU?`s2mfW6-D$67lign@`Cq^OLf}6gft5$gvQlWnK16QWHhJUFV=li| J{c`^5{{{5fEbjmS diff --git a/docs/reference/add_manual_bounded_constraints.html b/docs/reference/add_manual_bounded_constraints.html index 9a933904f..9c6a9e39b 100644 --- a/docs/reference/add_manual_bounded_constraints.html +++ b/docs/reference/add_manual_bounded_constraints.html @@ -1,106 +1,23 @@ - - - - - - - -Add manually specified bound constraints — add_manual_bounded_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add manually specified bound constraints — add_manual_bounded_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Add constraints to a conservation planning problem() to ensure -that the planning unit values (e.g. proportion, binary) in a solution +

    Add constraints to a conservation planning problem() to ensure +that the planning unit values (e.g., proportion, binary) in a solution range between specific lower and upper bounds. This function offers more -fine-grained control than the add_manual_locked_constraints() +fine-grained control than the add_manual_locked_constraints() function and is is most useful for problems involving proportion-type or semi-continuous decisions.

    -
    add_manual_bounded_constraints(x, data)
    -
    -# S4 method for ConservationProblem,data.frame
    -add_manual_bounded_constraints(x, data)
    -
    -# S4 method for ConservationProblem,tbl_df
    -add_manual_bounded_constraints(x, data)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    data

    data.frame or tibble::tibble() object. -See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the constraints +

    Usage,
    add_manual_bounded_constraints(x, data)
    +
    +# S4 method for ConservationProblem,data.frame
    +add_manual_bounded_constraints(x, data)
    +
    +# S4 method for ConservationProblem,tbl_df
    +add_manual_bounded_constraints(x, data)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    data
    +

    data.frame or tibble::tibble() object. +See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data should be a data.frame with the following fields (columns):

    -
    +
    pu
    +

    integer planning unit identifier.

    -
    pu

    integer planning unit identifier.

    -
    zone

    character names of zones. Note that this +

    zone
    +

    character names of zones. Note that this argument is optional for arguments to x that contain a single zone.

    -
    lower

    numeric values indicating the minimum + +

    lower
    +

    numeric values indicating the minimum value that each planning unit can be allocated to in each zone in the solution.

    -
    upper

    numeric values indicating the maximum + +

    upper
    +

    numeric values indicating the maximum value that each planning unit can be allocated to in each zone in the solution.

    -
    - -

    See also

    -

    See constraints for an overview of all functions for adding constraints.

    +
    + +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_polygons, sim_features, sim_pu_zones_polygons,
    -     sim_features_zones)
    -
    -# create minimal problem
    -p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with locked in constraints using add_locked_constraints
    -p2 <- p1 %>% add_locked_in_constraints("locked_in")
    -
    -# create identical problem using add_manual_bounded_constraints
    -bounds_data <- data.frame(pu = which(sim_pu_polygons$locked_in),
    -                          lower = 1, upper = 1)
    -
    -p3 <- p1 %>% add_manual_bounded_constraints(bounds_data)
    -# \dontrun{
    -# solve problems
    -s1 <- solve(p1)
    -s2 <- solve(p2)
    -s3 <- solve(p3)
    -
    -# plot solutions
    -par(mfrow = c(1,3), mar = c(0, 0, 4.1, 0))
    -plot(s1, main = "none locked in")
    -plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s2, main = "add_locked_in_constraints")
    -plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s3, main = "add_bounds_constraints")
    -plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -# }
    -# create minimal problem with multiple zones
    -p4 <- problem(sim_pu_zones_polygons, sim_features_zones,
    -              c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create data.frame with the following constraints:
    -# planning units 1, 2, and 3 must be allocated to zone 1 in the solution
    -# planning units 4, and 5 must be allocated to zone 2 in the solution
    -# planning units 8 and 9 must not be allocated to zone 3 in the solution
    -bounds_data2 <- data.frame(pu = c(1, 2, 3, 4, 5, 8, 9),
    -                           zone = c(rep("zone_1", 3), rep("zone_2", 2),
    -                                    rep("zone_3", 2)),
    -                           lower = c(rep(1, 5), rep(0, 2)),
    -                           upper = c(rep(1, 5), rep(0, 2)))
    -
    -# print bounds data
    -print(bounds_data2)
    -#>   pu   zone lower upper
    -#> 1  1 zone_1     1     1
    -#> 2  2 zone_1     1     1
    -#> 3  3 zone_1     1     1
    -#> 4  4 zone_2     1     1
    -#> 5  5 zone_2     1     1
    -#> 6  8 zone_3     0     0
    -#> 7  9 zone_3     0     0
    -
    -# create problem with added constraints
    -p5 <- p4 %>% add_manual_bounded_constraints(bounds_data2)
    -# \dontrun{
    -# solve problem
    -s4 <- solve(p4)
    -s5 <- solve(p5)
    -
    -# create two new columns representing the zone id that each planning unit
    -# was allocated to in the two solutions
    -s4$solution <- category_vector(s4@data[, c("solution_1_zone_1",
    -                                           "solution_1_zone_2",
    -                                           "solution_1_zone_3")])
    -s4$solution <- factor(s4$solution)
    -
    -s4$solution_bounded <- category_vector(s5@data[, c("solution_1_zone_1",
    -                                                   "solution_1_zone_2",
    -                                                   "solution_1_zone_3")])
    -s4$solution_bounded <- factor(s4$solution_bounded)
    -
    -# plot solutions
    -spplot(s4, zcol = c("solution", "solution_bounded"), axes = FALSE,
    -       box = FALSE)
    -
    -# }
    -
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_polygons, sim_features, sim_pu_zones_polygons,
    +     sim_features_zones)
    +
    +# create minimal problem
    +p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with locked in constraints using add_locked_constraints
    +p2 <- p1 %>% add_locked_in_constraints("locked_in")
    +
    +# create identical problem using add_manual_bounded_constraints
    +bounds_data <- data.frame(pu = which(sim_pu_polygons$locked_in),
    +                          lower = 1, upper = 1)
    +
    +p3 <- p1 %>% add_manual_bounded_constraints(bounds_data)
    +# \dontrun{
    +# solve problems
    +s1 <- solve(p1)
    +s2 <- solve(p2)
    +s3 <- solve(p3)
    +
    +# plot solutions
    +par(mfrow = c(1,3), mar = c(0, 0, 4.1, 0))
    +plot(s1, main = "none locked in")
    +plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s2, main = "add_locked_in_constraints")
    +plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s3, main = "add_bounds_constraints")
    +plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +# }
    +# create minimal problem with multiple zones
    +p4 <- problem(sim_pu_zones_polygons, sim_features_zones,
    +              c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create data.frame with the following constraints:
    +# planning units 1, 2, and 3 must be allocated to zone 1 in the solution
    +# planning units 4, and 5 must be allocated to zone 2 in the solution
    +# planning units 8 and 9 must not be allocated to zone 3 in the solution
    +bounds_data2 <- data.frame(pu = c(1, 2, 3, 4, 5, 8, 9),
    +                           zone = c(rep("zone_1", 3), rep("zone_2", 2),
    +                                    rep("zone_3", 2)),
    +                           lower = c(rep(1, 5), rep(0, 2)),
    +                           upper = c(rep(1, 5), rep(0, 2)))
    +
    +# print bounds data
    +print(bounds_data2)
    +#>   pu   zone lower upper
    +#> 1  1 zone_1     1     1
    +#> 2  2 zone_1     1     1
    +#> 3  3 zone_1     1     1
    +#> 4  4 zone_2     1     1
    +#> 5  5 zone_2     1     1
    +#> 6  8 zone_3     0     0
    +#> 7  9 zone_3     0     0
    +
    +# create problem with added constraints
    +p5 <- p4 %>% add_manual_bounded_constraints(bounds_data2)
    +# \dontrun{
    +# solve problem
    +s4 <- solve(p4)
    +s5 <- solve(p5)
    +
    +# create two new columns representing the zone id that each planning unit
    +# was allocated to in the two solutions
    +s4$solution <- category_vector(s4@data[, c("solution_1_zone_1",
    +                                           "solution_1_zone_2",
    +                                           "solution_1_zone_3")])
    +s4$solution <- factor(s4$solution)
    +
    +s4$solution_bounded <- category_vector(s5@data[, c("solution_1_zone_1",
    +                                                   "solution_1_zone_2",
    +                                                   "solution_1_zone_3")])
    +s4$solution_bounded <- factor(s4$solution_bounded)
    +
    +# plot solutions
    +spplot(s4, zcol = c("solution", "solution_bounded"), axes = FALSE,
    +       box = FALSE)
    +
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_manual_locked_constraints-2.png b/docs/reference/add_manual_locked_constraints-2.png index 222b7ed695dcaf9c9c4f3101e65894c1cb1988b0..f28a39b4ac6a8bedb91b08bf54121efafb159733 100644 GIT binary patch literal 20622 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ℑ$_X| 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 x6J*@h4sYf7xq3?@{jZ{{?6WpnCuS diff --git a/docs/reference/add_manual_locked_constraints.html b/docs/reference/add_manual_locked_constraints.html index 342e2ae41..5da6d2e3c 100644 --- a/docs/reference/add_manual_locked_constraints.html +++ b/docs/reference/add_manual_locked_constraints.html @@ -1,105 +1,22 @@ - - - - - - - -Add manually specified locked constraints — add_manual_locked_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add manually specified locked constraints — add_manual_locked_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Add constraints to a conservation planning problem() to ensure +

    Add constraints to a conservation planning problem() to ensure that solutions allocate (or do not allocate) specific planning units to specific management zones. This function offers more fine-grained control -than the add_locked_in_constraints() and -add_locked_out_constraints() functions.

    +than the add_locked_in_constraints() and +add_locked_out_constraints() functions.

    -
    add_manual_locked_constraints(x, data)
    -
    -# S4 method for ConservationProblem,data.frame
    -add_manual_locked_constraints(x, data)
    -
    -# S4 method for ConservationProblem,tbl_df
    -add_manual_locked_constraints(x, data)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    data

    data.frame or tibble::tibble() object. -See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the constraints +

    Usage,
    add_manual_locked_constraints(x, data)
    +
    +# S4 method for ConservationProblem,data.frame
    +add_manual_locked_constraints(x, data)
    +
    +# S4 method for ConservationProblem,tbl_df
    +add_manual_locked_constraints(x, data)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    data
    +

    data.frame or tibble::tibble() object. +See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data should be a data.frame with the following fields (columns):

    -
    +
    pu
    +

    integer planning unit identifier.

    -
    pu

    integer planning unit identifier.

    -
    zone

    character names of zones. Note that this +

    zone
    +

    character names of zones. Note that this argument is optional for arguments to x that contain a single zone.

    -
    status

    numeric values indicating how much + +

    status
    +

    numeric values indicating how much of each planning unit should be allocated to each zone in the solution. -For example, the numeric values could be binary values (i.e. zero +For example, the numeric values could be binary values (i.e., zero or one) for problems containing binary-type decision variables -(using the add_binary_decisions() function). Alternatively, -the numeric values could be proportions (e.g. 0.5) for problems +(using the add_binary_decisions() function). Alternatively, +the numeric values could be proportions (e.g., 0.5) for problems containing proportion-type decision variables (using the -add_proportion_decisions()).

    - +add_proportion_decisions()).

    -
    -

    See also

    -

    See constraints for an overview of all functions for adding constraints.

    +
    + +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_polygons, sim_features, sim_pu_zones_polygons,
    -     sim_features_zones)
    -
    -# create minimal problem
    -p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with locked in constraints using add_locked_constraints
    -p2 <- p1 %>% add_locked_in_constraints("locked_in")
    -
    -# create identical problem using add_manual_locked_constraints
    -locked_data <- data.frame(pu = which(sim_pu_polygons$locked_in),
    -                          status = 1)
    -
    -p3 <- p1 %>% add_manual_locked_constraints(locked_data)
    -# \dontrun{
    -# solve problems
    -s1 <- solve(p1)
    -s2 <- solve(p2)
    -s3 <- solve(p3)
    -
    -# plot solutions
    -par(mfrow = c(1,3), mar = c(0, 0, 4.1, 0))
    -plot(s1, main = "none locked in")
    -plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s2, main = "add_locked_in_constraints")
    -plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s3, main = "add_manual_constraints")
    -plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -# }
    -# create minimal problem with multiple zones
    -p4 <- problem(sim_pu_zones_polygons, sim_features_zones,
    -              c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create data.frame with the following constraints:
    -# planning units 1, 2, and 3 must be allocated to zone 1 in the solution
    -# planning units 4, and 5 must be allocated to zone 2 in the solution
    -# planning units 8 and 9 must not be allocated to zone 3 in the solution
    -locked_data2 <- data.frame(pu = c(1, 2, 3, 4, 5, 8, 9),
    -                           zone = c(rep("zone_1", 3), rep("zone_2", 2),
    -                                    rep("zone_3", 2)),
    -                           status = c(rep(1, 5), rep(0, 2)))
    -
    -# print locked constraint data
    -print(locked_data2)
    -#>   pu   zone status
    -#> 1  1 zone_1      1
    -#> 2  2 zone_1      1
    -#> 3  3 zone_1      1
    -#> 4  4 zone_2      1
    -#> 5  5 zone_2      1
    -#> 6  8 zone_3      0
    -#> 7  9 zone_3      0
    -
    -# create problem with added constraints
    -p5 <- p4 %>% add_manual_locked_constraints(locked_data2)
    -# \dontrun{
    -# solve problem
    -s4 <- solve(p4)
    -s5 <- solve(p5)
    -
    -# create two new columns representing the zone id that each planning unit
    -# was allocated to in the two solutions
    -s4$solution <- category_vector(s4@data[, c("solution_1_zone_1",
    -                                           "solution_1_zone_2",
    -                                           "solution_1_zone_3")])
    -s4$solution <- factor(s4$solution)
    -
    -s4$solution_locked <- category_vector(s5@data[, c("solution_1_zone_1",
    -                                                  "solution_1_zone_2",
    -                                                  "solution_1_zone_3")])
    -s4$solution_locked <- factor(s4$solution_locked)
    -
    -# plot solutions
    -spplot(s4, zcol = c("solution", "solution_locked"), axes = FALSE,
    -       box = FALSE)
    -
    -# }
    -
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_polygons, sim_features, sim_pu_zones_polygons,
    +     sim_features_zones)
    +
    +# create minimal problem
    +p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with locked in constraints using add_locked_constraints
    +p2 <- p1 %>% add_locked_in_constraints("locked_in")
    +
    +# create identical problem using add_manual_locked_constraints
    +locked_data <- data.frame(pu = which(sim_pu_polygons$locked_in),
    +                          status = 1)
    +
    +p3 <- p1 %>% add_manual_locked_constraints(locked_data)
    +# \dontrun{
    +# solve problems
    +s1 <- solve(p1)
    +s2 <- solve(p2)
    +s3 <- solve(p3)
    +
    +# plot solutions
    +par(mfrow = c(1,3), mar = c(0, 0, 4.1, 0))
    +plot(s1, main = "none locked in")
    +plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s2, main = "add_locked_in_constraints")
    +plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s3, main = "add_manual_constraints")
    +plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +# }
    +# create minimal problem with multiple zones
    +p4 <- problem(sim_pu_zones_polygons, sim_features_zones,
    +              c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create data.frame with the following constraints:
    +# planning units 1, 2, and 3 must be allocated to zone 1 in the solution
    +# planning units 4, and 5 must be allocated to zone 2 in the solution
    +# planning units 8 and 9 must not be allocated to zone 3 in the solution
    +locked_data2 <- data.frame(pu = c(1, 2, 3, 4, 5, 8, 9),
    +                           zone = c(rep("zone_1", 3), rep("zone_2", 2),
    +                                    rep("zone_3", 2)),
    +                           status = c(rep(1, 5), rep(0, 2)))
    +
    +# print locked constraint data
    +print(locked_data2)
    +#>   pu   zone status
    +#> 1  1 zone_1      1
    +#> 2  2 zone_1      1
    +#> 3  3 zone_1      1
    +#> 4  4 zone_2      1
    +#> 5  5 zone_2      1
    +#> 6  8 zone_3      0
    +#> 7  9 zone_3      0
    +
    +# create problem with added constraints
    +p5 <- p4 %>% add_manual_locked_constraints(locked_data2)
    +# \dontrun{
    +# solve problem
    +s4 <- solve(p4)
    +s5 <- solve(p5)
    +
    +# create two new columns representing the zone id that each planning unit
    +# was allocated to in the two solutions
    +s4$solution <- category_vector(s4@data[, c("solution_1_zone_1",
    +                                           "solution_1_zone_2",
    +                                           "solution_1_zone_3")])
    +s4$solution <- factor(s4$solution)
    +
    +s4$solution_locked <- category_vector(s5@data[, c("solution_1_zone_1",
    +                                                  "solution_1_zone_2",
    +                                                  "solution_1_zone_3")])
    +s4$solution_locked <- factor(s4$solution_locked)
    +
    +# plot solutions
    +spplot(s4, zcol = c("solution", "solution_locked"), axes = FALSE,
    +       box = FALSE)
    +
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_manual_targets.html b/docs/reference/add_manual_targets.html index 8b8506292..ba460b2bd 100644 --- a/docs/reference/add_manual_targets.html +++ b/docs/reference/add_manual_targets.html @@ -1,61 +1,5 @@ - - - - - - - -Add manual targets — add_manual_targets • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add manual targets — add_manual_targets • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Set targets for a conservation planning problem() by manually +

    Set targets for a conservation planning problem() by manually specifying all the required information for each target. This function is useful because it can be used to customize all aspects of a target. For most cases, targets can be specified using the -add_absolute_targets() and add_relative_targets() +add_absolute_targets() and add_relative_targets() functions. However, this function can be used to (i) mix absolute and relative targets for different features and zones, (ii) set targets that pertain to the allocations of planning units in multiple zones, and (iii) -set targets that require different senses (e.g. targets which specify the +set targets that require different senses (e.g., targets which specify the solution should not exceed a certain quantity using "<=" values).

    -
    add_manual_targets(x, targets)
    -
    -# S4 method for ConservationProblem,data.frame
    -add_manual_targets(x, targets)
    -
    -# S4 method for ConservationProblem,tbl_df
    -add_manual_targets(x, targets)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    targets

    data.frame or tibble::tibble() object. -See the Target data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the targets added +

    Usage,
    add_manual_targets(x, targets)
    +
    +# S4 method for ConservationProblem,data.frame
    +add_manual_targets(x, targets)
    +
    +# S4 method for ConservationProblem,tbl_df
    +add_manual_targets(x, targets)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    targets
    +

    data.frame or tibble::tibble() object. +See the Target data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the targets added to it.

    -

    Details

    - +
    +
    +

    Details

    Targets are used to specify the minimum amount or proportion of a feature's distribution that needs to be protected. Most conservation planning problems require targets with the exception of the maximum cover -(see add_max_cover_objective()) and maximum utility -(see add_max_utility_objective()) problems. Attempting to solve +(see add_max_cover_objective()) and maximum utility +(see add_max_utility_objective()) problems. Attempting to solve problems with objectives that require targets without specifying targets will throw an error.

    For problems associated with multiple management zones, this function can @@ -251,211 +163,217 @@

    Details potentially, be met through allocating all planning units to any specific management zone, or through allocating the planning units to different combinations of management zones.

    -

    Targets format

    - +
    +
    +

    Targets format

    The targets argument should be a data.frame with the following fields (columns):

    -
    - -
    feature

    character name of features in argument +

    feature
    +

    character name of features in argument to x.

    -
    zone

    character name of zones in argument to + +

    zone
    +

    character name of zones in argument to x. This field (column) is optional for arguments to x that do not contain multiple zones.

    -
    type

    character describing the type of target. + +

    type
    +

    character describing the type of target. Acceptable values include "absolute" and "relative". -These values correspond to add_absolute_targets(), -and add_relative_targets() respectively.

    +These values correspond to add_absolute_targets(), +and add_relative_targets() respectively.

    -
    sense

    character sense of the target. Acceptable + +

    sense
    +

    character sense of the target. Acceptable values include: ">=", "<=", and "=". This field (column) is optional and if it is missing then target senses will default to ">=" values.

    -
    target

    numeric target threshold.

    +
    target
    +

    numeric target threshold.

    -
    -

    See also

    -

    See targets for an overview of all functions for adding targets.

    +
    +
    +

    See also

    +

    See targets for an overview of all functions for adding targets.

    Other targets: -add_absolute_targets(), -add_loglinear_targets(), -add_relative_targets()

    +add_absolute_targets(), +add_loglinear_targets(), +add_relative_targets()

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create problem with 10% relative targets
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -# }
    -# create equivalent problem using add_manual_targets
    -p2 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(data.frame(feature = names(sim_features),
    -                                    type = "relative", sense = ">=",
    -                                    target = 0.1)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(s2, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create problem with targets set for only a few features
    -p3 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(data.frame(
    -        feature = names(sim_features)[1:3], type = "relative",
    -        sense = ">=", target = 0.1)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(s3, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create problem that aims to secure at least 10% of the habitat for one
    -# feature whilst ensuring that the solution does not capture more than
    -# 20 units habitat for different feature
    -# create problem with targets set for only a few features
    -p4 <- problem(sim_pu_raster, sim_features[[1:2]]) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(data.frame(
    -        feature = names(sim_features)[1:2], type = "relative",
    -        sense = c(">=", "<="), target = c(0.1, 0.2))) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s4 <- solve(p4)
    -
    -# plot solution
    -plot(s4, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create a multi-zone problem that requires a specific amount of each
    -# feature in each zone
    -targets_matrix <- matrix(rpois(15, 1), nrow = 5, ncol = 3)
    -
    -p5 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(targets_matrix) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s5 <- solve(p5)
    -
    -# plot solution
    -plot(category_layer(s5), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create equivalent problem using add_manual_targets
    -targets_dataframe <- expand.grid(feature = feature_names(sim_features_zones),
    -                                 zone = zone_names(sim_features_zones),
    -                                 sense = ">=", type = "absolute")
    -targets_dataframe$target <- c(targets_matrix)
    -
    -p6 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(targets_dataframe) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s6 <- solve(p6)
    -
    -# plot solution
    -plot(category_layer(s6), main = "solution", axes = FALSE, box = FALSE)
    -# }
    -# create a problem that requires a total of 20 units of habitat to be
    -# captured for two species. This can be achieved through representing
    -# habitat in two zones. The first zone represents a full restoration of the
    -# habitat and a second zone represents a partial restoration of the habitat
    -# Thus only half of the benefit that would have been gained from the full
    -# restoration is obtained when planning units are allocated a partial
    -# restoration
    -
    -# create data
    -spp_zone1 <- as.list(sim_features_zones)[[1]][[1:2]]
    -spp_zone2 <- spp_zone1 * 0.5
    -costs <- sim_pu_zones_stack[[1:2]]
    -
    -# create targets
    -targets_dataframe2 <- tibble::tibble(
    -  feature = names(spp_zone1), zone = list(c("z1", "z2"), c("z1", "z2")),
    -  sense = c(">=", ">="), type = c("absolute", "absolute"),
    -  target = c(20, 20))
    -
    -# create problem
    -p7 <- problem(costs, zones(spp_zone1, spp_zone2,
    -                           feature_names = names(spp_zone1),
    -                           zone_names = c("z1", "z2"))) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(targets_dataframe2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s7 <- solve(p7)
    -
    -# plot solution
    -plot(category_layer(s7), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create problem with 10% relative targets
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +# }
    +# create equivalent problem using add_manual_targets
    +p2 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(data.frame(feature = names(sim_features),
    +                                    type = "relative", sense = ">=",
    +                                    target = 0.1)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(s2, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create problem with targets set for only a few features
    +p3 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(data.frame(
    +        feature = names(sim_features)[1:3], type = "relative",
    +        sense = ">=", target = 0.1)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(s3, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create problem that aims to secure at least 10% of the habitat for one
    +# feature whilst ensuring that the solution does not capture more than
    +# 20 units habitat for different feature
    +# create problem with targets set for only a few features
    +p4 <- problem(sim_pu_raster, sim_features[[1:2]]) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(data.frame(
    +        feature = names(sim_features)[1:2], type = "relative",
    +        sense = c(">=", "<="), target = c(0.1, 0.2))) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s4 <- solve(p4)
    +
    +# plot solution
    +plot(s4, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create a multi-zone problem that requires a specific amount of each
    +# feature in each zone
    +targets_matrix <- matrix(rpois(15, 1), nrow = 5, ncol = 3)
    +
    +p5 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(targets_matrix) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s5 <- solve(p5)
    +
    +# plot solution
    +plot(category_layer(s5), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create equivalent problem using add_manual_targets
    +targets_dataframe <- expand.grid(feature = feature_names(sim_features_zones),
    +                                 zone = zone_names(sim_features_zones),
    +                                 sense = ">=", type = "absolute")
    +targets_dataframe$target <- c(targets_matrix)
    +
    +p6 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(targets_dataframe) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s6 <- solve(p6)
    +
    +# plot solution
    +plot(category_layer(s6), main = "solution", axes = FALSE, box = FALSE)
    +# }
    +# create a problem that requires a total of 20 units of habitat to be
    +# captured for two species. This can be achieved through representing
    +# habitat in two zones. The first zone represents a full restoration of the
    +# habitat and a second zone represents a partial restoration of the habitat
    +# Thus only half of the benefit that would have been gained from the full
    +# restoration is obtained when planning units are allocated a partial
    +# restoration
    +
    +# create data
    +spp_zone1 <- as.list(sim_features_zones)[[1]][[1:2]]
    +spp_zone2 <- spp_zone1 * 0.5
    +costs <- sim_pu_zones_stack[[1:2]]
    +
    +# create targets
    +targets_dataframe2 <- tibble::tibble(
    +  feature = names(spp_zone1), zone = list(c("z1", "z2"), c("z1", "z2")),
    +  sense = c(">=", ">="), type = c("absolute", "absolute"),
    +  target = c(20, 20))
    +
    +# create problem
    +p7 <- problem(costs, zones(spp_zone1, spp_zone2,
    +                           feature_names = names(spp_zone1),
    +                           zone_names = c("z1", "z2"))) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(targets_dataframe2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s7 <- solve(p7)
    +
    +# plot solution
    +plot(category_layer(s7), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/add_max_cover_objective.html b/docs/reference/add_max_cover_objective.html index 7cef56f8f..6c9f84b01 100644 --- a/docs/reference/add_max_cover_objective.html +++ b/docs/reference/add_max_cover_objective.html @@ -1,105 +1,22 @@ - - - - - - - -Add maximum coverage objective — add_max_cover_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add maximum coverage objective — add_max_cover_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to +

    Set the objective of a conservation planning problem() to represent at least one instance of as many features as possible within a given budget. This objective does not use targets, and feature weights should be used instead to increase the representation of certain features by a solution.

    -
    add_max_cover_objective(x, budget)
    +
    Usage,
    add_max_cover_objective(x, budget)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    budget

    numeric value specifying the maximum expenditure of +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    budget
    +

    numeric value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument to budget can be a single numeric value to specify a budget for the entire solution or a numeric vector to specify -a budget for each each management zone.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +a budget for each each management zone.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The maximum coverage objective seeks to find the set of planning units that maximizes the number of represented features, while keeping cost within a fixed budget. Here, features are treated as being represented if the reserve system contains at least a single instance of a feature -(i.e. an amount greater than 1). This formulation has often been +(i.e., an amount greater than 1). This formulation has often been used in conservation planning problems dealing with binary biodiversity data that indicate the presence/absence of suitable habitat -(e.g. Church & Velle 1974). Additionally, weights can be used to favor the +(e.g., Church & Velle 1974). Additionally, weights can be used to favor the representation of certain features over other features (see -add_feature_weights()). Check out the -add_max_features_objective() for a more +add_feature_weights()). Check out the +add_max_features_objective() for a more generalized formulation which can accommodate user-specified representation targets.

    -

    Mathematical formulation

    - +
    +
    +

    Mathematical formulation

    This objective is based on the maximum coverage reserve @@ -244,137 +157,138 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(r_{ij}\) is the amount of feature \(j\) in planning unit \(i\), \(y_j\) indicates if the solution has meet the target \(t_j\) for feature \(j\), and \(w_j\) is the weight for feature \(j\) (defaults to 1 for all features; see -add_feature_weights() to specify weights). Additionally, +add_feature_weights() to specify weights). Additionally, \(B\) is the budget allocated for the solution, \(c_i\) is the cost of planning unit \(i\), and \(s\) is a scaling factor used to shrink the costs so that the problem will return a cheapest solution when there are multiple solutions that represent the same amount of all features within the budget.

    -

    Notes

    - +
    +
    +

    Notes

    In early versions (< 3.0.0.0), the mathematical formulation underpinning this function was very different. Specifically, as described above, the function now follows the formulations outlined in Church et al. (1996). The old formulation is now provided by the -add_max_utility_objective() function.

    -

    References

    - +add_max_utility_objective() function.

    +
    +
    +

    References

    Church RL and Velle CR (1974) The maximum covering location problem. Regional Science, 32: 101--118.

    Church RL, Stoms DM, and Davis FW (1996) Reserve selection as a maximum covering location problem. Biological Conservation, 76: 105--112.

    -

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also, see add_feature_weights() to specify weights for different features.

    +
    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# threshold the feature data to generate binary biodiversity data
    -sim_binary_features <- sim_features
    -thresholds <- raster::quantile(sim_features, probs = 0.95, names = FALSE,
    -                               na.rm = TRUE)
    -for (i in seq_len(raster::nlayers(sim_features)))
    -  sim_binary_features[[i]] <- as.numeric(raster::values(sim_features[[i]]) >
    -                                         thresholds[[i]])
    -
    -# create problem with maximum utility objective
    -p1 <- problem(sim_pu_raster, sim_binary_features) %>%
    -      add_max_cover_objective(500) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# threshold the multi-zone feature data to generate binary biodiversity data
    -sim_binary_features_zones <- sim_features_zones
    -for (z in number_of_zones(sim_features_zones)) {
    -  thresholds <- raster::quantile(sim_features_zones[[z]], probs = 0.95,
    -                                 names = FALSE, na.rm = TRUE)
    -  for (i in seq_len(number_of_features(sim_features_zones))) {
    -    sim_binary_features_zones[[z]][[i]] <- as.numeric(
    -      raster::values(sim_features_zones[[z]][[i]]) > thresholds[[i]])
    -  }
    -}
    -
    -# create multi-zone problem with maximum utility objective that
    -# has a single budget for all zones
    -p2 <- problem(sim_pu_zones_stack, sim_binary_features_zones) %>%
    -      add_max_cover_objective(800) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create multi-zone problem with maximum utility objective that
    -# has separate budgets for each zone
    -p3 <- problem(sim_pu_zones_stack, sim_binary_features_zones) %>%
    -      add_max_cover_objective(c(400, 400, 400)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# threshold the feature data to generate binary biodiversity data
    +sim_binary_features <- sim_features
    +thresholds <- raster::quantile(sim_features, probs = 0.95, names = FALSE,
    +                               na.rm = TRUE)
    +for (i in seq_len(raster::nlayers(sim_features)))
    +  sim_binary_features[[i]] <- as.numeric(raster::values(sim_features[[i]]) >
    +                                         thresholds[[i]])
    +
    +# create problem with maximum utility objective
    +p1 <- problem(sim_pu_raster, sim_binary_features) %>%
    +      add_max_cover_objective(500) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# threshold the multi-zone feature data to generate binary biodiversity data
    +sim_binary_features_zones <- sim_features_zones
    +for (z in number_of_zones(sim_features_zones)) {
    +  thresholds <- raster::quantile(sim_features_zones[[z]], probs = 0.95,
    +                                 names = FALSE, na.rm = TRUE)
    +  for (i in seq_len(number_of_features(sim_features_zones))) {
    +    sim_binary_features_zones[[z]][[i]] <- as.numeric(
    +      raster::values(sim_features_zones[[z]][[i]]) > thresholds[[i]])
    +  }
    +}
    +
    +# create multi-zone problem with maximum utility objective that
    +# has a single budget for all zones
    +p2 <- problem(sim_pu_zones_stack, sim_binary_features_zones) %>%
    +      add_max_cover_objective(800) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create multi-zone problem with maximum utility objective that
    +# has separate budgets for each zone
    +p3 <- problem(sim_pu_zones_stack, sim_binary_features_zones) %>%
    +      add_max_cover_objective(c(400, 400, 400)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_max_features_objective.html b/docs/reference/add_max_features_objective.html index a904a1a65..effa84e2a 100644 --- a/docs/reference/add_max_features_objective.html +++ b/docs/reference/add_max_features_objective.html @@ -1,103 +1,20 @@ - - - - - - - -Add maximum feature representation objective — add_max_features_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add maximum feature representation objective — add_max_features_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to +

    Set the objective of a conservation planning problem() to fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget.

    -
    add_max_features_objective(x, budget)
    +
    Usage,
    add_max_features_objective(x, budget)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    budget

    numeric value specifying the maximum expenditure of +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    budget
    +

    numeric value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument to budget can be (i) a single numeric value to specify a single budget for the entire solution or (ii) a numeric vector to specify -a separate budget for each management zone.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +a separate budget for each management zone.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The maximum feature representation objective is an enhanced version of the -maximum coverage objective add_max_cover_objective() because +maximum coverage objective add_max_cover_objective() because targets can be used to ensure that a certain amount of each feature is required in order for them to be adequately represented (similar to the -minimum set objective (see add_min_set_objective()). This +minimum set objective (see add_min_set_objective()). This objective finds the set of planning units that meets representation targets for as many features as possible while staying within a fixed budget (inspired by Cabeza and Moilanen 2001). Additionally, weights can be used -add_feature_weights()). If multiple solutions can meet the same +add_feature_weights()). If multiple solutions can meet the same number of weighted targets while staying within budget, the cheapest solution is returned.

    -

    Mathematical formulation

    - +
    +
    +

    Mathematical formulation

    This objective can be expressed mathematically for a set of planning units @@ -235,115 +148,115 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(r_{ij}\) is the amount of feature \(j\) in planning unit \(i\), \(t_j\) is the representation target for feature \(j\), \(y_j\) indicates if the solution has meet the target \(t_j\) for feature \(j\), and \(w_j\) is the weight for feature \(j\) (defaults to 1 for all features; see -add_feature_weights() to specify weights). Additionally, +add_feature_weights() to specify weights). Additionally, \(B\) is the budget allocated for the solution, \(c_i\) is the cost of planning unit \(i\), and \(s\) is a scaling factor used to shrink the costs so that the problem will return a cheapest solution when there are multiple solutions that represent the same amount of all features within the budget.

    -

    References

    - +
    +
    +

    References

    Cabeza M and Moilanen A (2001) Design of reserve networks and the persistence of biodiversity. Trends in Ecology & Evolution, 16: 242--248.

    -

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also, see targets for an overview of all functions for adding targets, and -add_feature_weights() to specify weights for different features.

    +
    +
    +

    See also

    +

    See objectives for an overview of all functions for adding objectives. +Also, see targets for an overview of all functions for adding targets, and +add_feature_weights() to specify weights for different features.

    Other objectives: -add_max_cover_objective(), -add_max_phylo_div_objective(), -add_max_phylo_end_objective(), -add_max_utility_objective(), -add_min_largest_shortfall_objective(), -add_min_set_objective(), -add_min_shortfall_objective()

    +add_max_cover_objective(), +add_max_phylo_div_objective(), +add_max_phylo_end_objective(), +add_max_utility_objective(), +add_min_largest_shortfall_objective(), +add_min_set_objective(), +add_min_shortfall_objective()

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# create problem with maximum features objective
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_features_objective(1800) %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create multi-zone problem with maximum features objective,
    -# with 10% representation targets for each feature, and set
    -# a budget such that the total maximum expenditure in all zones
    -# cannot exceed 3000
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_features_objective(3000) %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create multi-zone problem with maximum features objective,
    -# with 10% representation targets for each feature, and set
    -# separate budgets for each management zone
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_features_objective(c(3000, 3000, 3000)) %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# create problem with maximum features objective
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_features_objective(1800) %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create multi-zone problem with maximum features objective,
    +# with 10% representation targets for each feature, and set
    +# a budget such that the total maximum expenditure in all zones
    +# cannot exceed 3000
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_features_objective(3000) %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create multi-zone problem with maximum features objective,
    +# with 10% representation targets for each feature, and set
    +# separate budgets for each management zone
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_features_objective(c(3000, 3000, 3000)) %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_max_phylo_div_objective.html b/docs/reference/add_max_phylo_div_objective.html index cd6cb2848..d0ed7b206 100644 --- a/docs/reference/add_max_phylo_div_objective.html +++ b/docs/reference/add_max_phylo_div_objective.html @@ -1,108 +1,25 @@ - - - - - - - -Add maximum phylogenetic diversity objective — add_max_phylo_div_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add maximum phylogenetic diversity objective — add_max_phylo_div_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to +

    Set the objective of a conservation planning problem() to maximize the phylogenetic diversity of the features represented in the solution subject to a budget. This objective is similar to -add_max_features_objective() except +add_max_features_objective() except that emphasis is placed on representing a phylogenetically diverse set of species, rather than as many features as possible (subject to weights). This function was inspired by Faith (1992) and Rodrigues et al. (2002).

    -
    add_max_phylo_div_objective(x, budget, tree)
    +
    Usage,
    add_max_phylo_div_objective(x, budget, tree)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    budget

    numeric value specifying the maximum expenditure of +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    budget
    +

    numeric value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument to budget can be (i) a single numeric value to specify a single budget for the entire solution or (ii) a numeric vector to specify -a separate budget for each management zone.

    tree

    phylo() object specifying a phylogenetic tree -for the conservation features.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +a separate budget for each management zone.

    +
    tree
    +

    phylo() object specifying a phylogenetic tree +for the conservation features.

    + +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The maximum phylogenetic diversity objective finds the set of planning units that meets representation targets for a phylogenetic tree while staying within a fixed budget. If multiple solutions can meet all targets while staying within budget, the cheapest solution is chosen. Note that this objective is similar to the maximum -features objective (add_max_features_objective()) in that it +features objective (add_max_features_objective()) in that it allows for both a budget and targets to be set for each feature. However, unlike the maximum feature objective, the aim of this objective is to maximize the total phylogenetic diversity of the targets met in the @@ -253,8 +163,9 @@

    Details Here each target corresponds to a single feature and can be met through the total amount of habitat in planning units present to the three zones.

    -

    Mathematical formulation

    - +
    +
    +

    Mathematical formulation

    This objective can be expressed mathematically for a set of planning units @@ -266,7 +177,7 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(r_{ij}\) is the amount of feature \(j\) in planning unit \(i\), \(t_j\) is the representation target for feature @@ -283,209 +194,210 @@

    Notes

    - +
    +
    +

    Notes

    In early versions, this function was named as the add_max_phylo_div_objective function.

    -

    References

    - +
    +
    +

    References

    Faith DP (1992) Conservation evaluation and phylogenetic diversity. Biological Conservation, 61: 1--10.

    Rodrigues ASL and Gaston KJ (2002) Maximising phylogenetic diversity in the selection of networks of conservation areas. Biological Conservation, 105: 103--111.

    -

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also, see targets for an overview of all functions for adding targets, and -add_feature_weights() to specify weights for different features.

    +
    +
    +

    See also

    +

    See objectives for an overview of all functions for adding objectives. +Also, see targets for an overview of all functions for adding targets, and +add_feature_weights() to specify weights for different features.

    Other objectives: -add_max_cover_objective(), -add_max_features_objective(), -add_max_phylo_end_objective(), -add_max_utility_objective(), -add_min_largest_shortfall_objective(), -add_min_set_objective(), -add_min_shortfall_objective()

    +add_max_cover_objective(), +add_max_features_objective(), +add_max_phylo_end_objective(), +add_max_utility_objective(), +add_min_largest_shortfall_objective(), +add_min_set_objective(), +add_min_shortfall_objective()

    +
    -

    Examples

    -
    # load ape package
    -require(ape)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_phylogeny, sim_pu_zones_stack,
    -     sim_features_zones)
    -
    -# plot the simulated phylogeny
    -# \dontrun{
    -par(mfrow = c(1, 1))
    -plot(sim_phylogeny, main = "phylogeny")
    -
    -# }
    -# create problem with a maximum phylogenetic diversity objective,
    -# where each feature needs 10% of its distribution to be secured for
    -# it to be adequately conserved and a total budget of 1900
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_phylo_div_objective(1900, sim_phylogeny) %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find out which features have their targets met
    -r1 <- eval_target_coverage_summary(p1, s1)
    -print(r1, width = Inf)
    -#> # A tibble: 5 × 9
    -#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1 layer.1 FALSE         83.3            8.33          7.35              0.975
    -#> 2 layer.2 TRUE          31.2            3.12          3.13              0    
    -#> 3 layer.3 FALSE         72.0            7.20          5.78              1.42 
    -#> 4 layer.4 TRUE          42.7            4.27          4.50              0    
    -#> 5 layer.5 FALSE         56.7            5.67          5.24              0.431
    -#>   relative_target relative_held relative_shortfall
    -#>             <dbl>         <dbl>              <dbl>
    -#> 1             0.1        0.0883            0.0117 
    -#> 2             0.1        0.100             0      
    -#> 3             0.1        0.0803            0.0197 
    -#> 4             0.1        0.106             0      
    -#> 5             0.1        0.0924            0.00760
    -
    -# plot the phylogeny and color the adequately represented features in red
    -plot(sim_phylogeny, main = "adequately represented features",
    -     tip.color = replace(
    -       rep("black", nlayers(sim_features)),
    -       sim_phylogeny$tip.label %in% r1$feature[r1$met],
    -       "red"))
    -
    -# }
    -# rename the features in the example phylogeny for use with the
    -# multi-zone data
    -sim_phylogeny$tip.label <- feature_names(sim_features_zones)
    -
    -# create targets for a multi-zone problem. Here, each feature needs a total
    -# of 10 units of habitat to be conserved among the three zones to be
    -# considered adequately conserved
    -targets <- tibble::tibble(
    -  feature = feature_names(sim_features_zones),
    -  zone = list(zone_names(sim_features_zones))[rep(1,
    -          number_of_features(sim_features_zones))],
    -  type = rep("absolute", number_of_features(sim_features_zones)),
    -  target = rep(10, number_of_features(sim_features_zones)))
    -
    -# create a multi-zone problem with a maximum phylogenetic diversity
    -# objective, where the total expenditure in all zones is 5000.
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_phylo_div_objective(5000, sim_phylogeny) %>%
    -      add_manual_targets(targets) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find out which features have their targets met
    -r2 <- eval_target_coverage_summary(p2, s2)
    -print(r2, width = Inf)
    -#> # A tibble: 5 × 11
    -#>   feature   zone      sense met   total_amount absolute_target absolute_held
    -#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    -#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         17.6 
    -#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          6.66
    -#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         14.0 
    -#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.0 
    -#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         12.4 
    -#>   absolute_shortfall relative_target relative_held relative_shortfall
    -#>                <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1               0             0.0400        0.0706             0     
    -#> 2               3.34          0.107         0.0712             0.0356
    -#> 3               0             0.0463        0.0646             0     
    -#> 4               0             0.0781        0.0784             0     
    -#> 5               0             0.0588        0.0731             0     
    -
    -# plot the phylogeny and color the adequately represented features in red
    -plot(sim_phylogeny, main = "adequately represented features",
    -     tip.color = replace(rep("black", nlayers(sim_features)),
    -                         which(r2$met), "red"))
    -
    -# }
    -# create a multi-zone problem with a maximum phylogenetic diversity
    -# objective, where each zone has a separate budget.
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_phylo_div_objective(c(2500, 500, 2000), sim_phylogeny) %>%
    -      add_manual_targets(targets) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find out which features have their targets met
    -r3 <- eval_target_coverage_summary(p3, s3)
    -print(r3, width = Inf)
    -#> # A tibble: 5 × 11
    -#>   feature   zone      sense met   total_amount absolute_target absolute_held
    -#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    -#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         15.6 
    -#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          7.23
    -#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         12.9 
    -#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.1 
    -#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         10.8 
    -#>   absolute_shortfall relative_target relative_held relative_shortfall
    -#>                <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1               0             0.0400        0.0624             0     
    -#> 2               2.77          0.107         0.0772             0.0296
    -#> 3               0             0.0463        0.0598             0     
    -#> 4               0             0.0781        0.0788             0     
    -#> 5               0             0.0588        0.0636             0     
    -
    -# plot the phylogeny and color the adequately represented features in red
    -plot(sim_phylogeny, main = "adequately represented features",
    -     tip.color = replace(rep("black", nlayers(sim_features)),
    -                         which(r3$met), "red"))
    -
    -# }
    +    
    +

    Examples

    +
    # load ape package
    +require(ape)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_phylogeny, sim_pu_zones_stack,
    +     sim_features_zones)
    +
    +# plot the simulated phylogeny
    +# \dontrun{
    +par(mfrow = c(1, 1))
    +plot(sim_phylogeny, main = "phylogeny")
    +
    +# }
    +# create problem with a maximum phylogenetic diversity objective,
    +# where each feature needs 10% of its distribution to be secured for
    +# it to be adequately conserved and a total budget of 1900
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_phylo_div_objective(1900, sim_phylogeny) %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find out which features have their targets met
    +r1 <- eval_target_coverage_summary(p1, s1)
    +print(r1, width = Inf)
    +#> # A tibble: 5 × 9
    +#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1 layer.1 FALSE         83.3            8.33          7.35              0.975
    +#> 2 layer.2 TRUE          31.2            3.12          3.13              0    
    +#> 3 layer.3 FALSE         72.0            7.20          5.78              1.42 
    +#> 4 layer.4 TRUE          42.7            4.27          4.50              0    
    +#> 5 layer.5 FALSE         56.7            5.67          5.24              0.431
    +#>   relative_target relative_held relative_shortfall
    +#>             <dbl>         <dbl>              <dbl>
    +#> 1             0.1        0.0883            0.0117 
    +#> 2             0.1        0.100             0      
    +#> 3             0.1        0.0803            0.0197 
    +#> 4             0.1        0.106             0      
    +#> 5             0.1        0.0924            0.00760
    +
    +# plot the phylogeny and color the adequately represented features in red
    +plot(sim_phylogeny, main = "adequately represented features",
    +     tip.color = replace(
    +       rep("black", nlayers(sim_features)),
    +       sim_phylogeny$tip.label %in% r1$feature[r1$met],
    +       "red"))
    +
    +# }
    +# rename the features in the example phylogeny for use with the
    +# multi-zone data
    +sim_phylogeny$tip.label <- feature_names(sim_features_zones)
    +
    +# create targets for a multi-zone problem. Here, each feature needs a total
    +# of 10 units of habitat to be conserved among the three zones to be
    +# considered adequately conserved
    +targets <- tibble::tibble(
    +  feature = feature_names(sim_features_zones),
    +  zone = list(zone_names(sim_features_zones))[rep(1,
    +          number_of_features(sim_features_zones))],
    +  type = rep("absolute", number_of_features(sim_features_zones)),
    +  target = rep(10, number_of_features(sim_features_zones)))
    +
    +# create a multi-zone problem with a maximum phylogenetic diversity
    +# objective, where the total expenditure in all zones is 5000.
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_phylo_div_objective(5000, sim_phylogeny) %>%
    +      add_manual_targets(targets) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find out which features have their targets met
    +r2 <- eval_target_coverage_summary(p2, s2)
    +print(r2, width = Inf)
    +#> # A tibble: 5 × 11
    +#>   feature   zone      sense met   total_amount absolute_target absolute_held
    +#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    +#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         17.6 
    +#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          6.66
    +#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         14.0 
    +#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.0 
    +#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         12.4 
    +#>   absolute_shortfall relative_target relative_held relative_shortfall
    +#>                <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1               0             0.0400        0.0706             0     
    +#> 2               3.34          0.107         0.0712             0.0356
    +#> 3               0             0.0463        0.0646             0     
    +#> 4               0             0.0781        0.0784             0     
    +#> 5               0             0.0588        0.0731             0     
    +
    +# plot the phylogeny and color the adequately represented features in red
    +plot(sim_phylogeny, main = "adequately represented features",
    +     tip.color = replace(rep("black", nlayers(sim_features)),
    +                         which(r2$met), "red"))
    +
    +# }
    +# create a multi-zone problem with a maximum phylogenetic diversity
    +# objective, where each zone has a separate budget.
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_phylo_div_objective(c(2500, 500, 2000), sim_phylogeny) %>%
    +      add_manual_targets(targets) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find out which features have their targets met
    +r3 <- eval_target_coverage_summary(p3, s3)
    +print(r3, width = Inf)
    +#> # A tibble: 5 × 11
    +#>   feature   zone      sense met   total_amount absolute_target absolute_held
    +#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    +#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         15.6 
    +#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          7.23
    +#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         12.9 
    +#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.1 
    +#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         10.8 
    +#>   absolute_shortfall relative_target relative_held relative_shortfall
    +#>                <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1               0             0.0400        0.0624             0     
    +#> 2               2.77          0.107         0.0772             0.0296
    +#> 3               0             0.0463        0.0598             0     
    +#> 4               0             0.0781        0.0788             0     
    +#> 5               0             0.0588        0.0636             0     
    +
    +# plot the phylogeny and color the adequately represented features in red
    +plot(sim_phylogeny, main = "adequately represented features",
    +     tip.color = replace(rep("black", nlayers(sim_features)),
    +                         which(r3$met), "red"))
    +
    +# }
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/add_max_phylo_end_objective.html b/docs/reference/add_max_phylo_end_objective.html index 58c690d1f..489d47c4a 100644 --- a/docs/reference/add_max_phylo_end_objective.html +++ b/docs/reference/add_max_phylo_end_objective.html @@ -1,108 +1,25 @@ - - - - - - - -Add maximum phylogenetic endemism objective — add_max_phylo_end_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add maximum phylogenetic endemism objective — add_max_phylo_end_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to +

    Set the objective of a conservation planning problem() to maximize the phylogenetic endemism of the features represented in the solution subject to a budget. This objective is similar to add_max_phylo_end_objective() except @@ -198,42 +115,35 @@

    Add maximum phylogenetic endemism objective

    Rodrigues et al. (2002), and Rosauer et al. (2009).

    -
    add_max_phylo_end_objective(x, budget, tree)
    +
    Usage,
    add_max_phylo_end_objective(x, budget, tree)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    budget

    numeric value specifying the maximum expenditure of +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    budget
    +

    numeric value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument to budget can be (i) a single numeric value to specify a single budget for the entire solution or (ii) a numeric vector to specify -a separate budget for each management zone.

    tree

    phylo() object specifying a phylogenetic tree -for the conservation features.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +a separate budget for each management zone.

    +
    tree
    +

    phylo() object specifying a phylogenetic tree +for the conservation features.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The maximum phylogenetic endemism objective finds the set of planning units that meets representation targets for a phylogenetic tree while staying within a fixed budget. If multiple solutions can meet all targets while staying within budget, the cheapest solution is chosen. Note that this objective is similar to the maximum -features objective (add_max_features_objective()) in that it +features objective (add_max_features_objective()) in that it allows for both a budget and targets to be set for each feature. However, unlike the maximum feature objective, the aim of this objective is to maximize the total phylogenetic endemism of the targets met in the @@ -253,8 +163,9 @@

    Details Here each target corresponds to a single feature and can be met through the total amount of habitat in planning units present to the three zones.

    -

    Mathematical formulation

    - +
    +
    +

    Mathematical formulation

    This objective can be expressed mathematically for a set of planning units @@ -266,7 +177,7 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(r_{ij}\) is the amount of feature \(j\) in planning unit \(i\), \(t_j\) is the representation target for feature @@ -287,8 +198,9 @@

    References

    - +
    +
    +

    References

    Faith DP (1992) Conservation evaluation and phylogenetic diversity. Biological Conservation, 61: 1--10.

    Rodrigues ASL and Gaston KJ (2002) Maximising phylogenetic diversity in the @@ -298,196 +210,195 @@

    R Phylogenetic endemism: a new approach for identifying geographical concentrations of evolutionary history. Molecular Ecology, 18: 4061--4072.

    -

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also, see targets for an overview of all functions for adding targets, and -add_feature_weights() to specify weights for different features.

    +
    +
    +

    See also

    +

    See objectives for an overview of all functions for adding objectives. +Also, see targets for an overview of all functions for adding targets, and +add_feature_weights() to specify weights for different features.

    Other objectives: -add_max_cover_objective(), -add_max_features_objective(), -add_max_phylo_div_objective(), -add_max_utility_objective(), -add_min_largest_shortfall_objective(), -add_min_set_objective(), -add_min_shortfall_objective()

    +add_max_cover_objective(), +add_max_features_objective(), +add_max_phylo_div_objective(), +add_max_utility_objective(), +add_min_largest_shortfall_objective(), +add_min_set_objective(), +add_min_shortfall_objective()

    +
    -

    Examples

    -
    # load ape package
    -require(ape)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_phylogeny, sim_pu_zones_stack,
    -     sim_features_zones)
    -
    -# plot the simulated phylogeny
    -# \dontrun{
    -par(mfrow = c(1, 1))
    -plot(sim_phylogeny, main = "phylogeny")
    -
    -# }
    -# create problem with a maximum phylogenetic endemism objective,
    -# where each feature needs 10% of its distribution to be secured for
    -# it to be adequately conserved and a total budget of 1900
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_phylo_end_objective(1900, sim_phylogeny) %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find out which features have their targets met
    -r1 <- eval_target_coverage_summary(p1, s1)
    -print(r1, width = Inf)
    -#> # A tibble: 5 × 9
    -#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1 layer.1 FALSE         83.3            8.33          7.71              0.618
    -#> 2 layer.2 FALSE         31.2            3.12          2.35              0.775
    -#> 3 layer.3 FALSE         72.0            7.20          5.81              1.38 
    -#> 4 layer.4 TRUE          42.7            4.27          4.28              0    
    -#> 5 layer.5 TRUE          56.7            5.67          5.77              0    
    -#>   relative_target relative_held relative_shortfall
    -#>             <dbl>         <dbl>              <dbl>
    -#> 1             0.1        0.0926            0.00742
    -#> 2             0.1        0.0752            0.0248 
    -#> 3             0.1        0.0808            0.0192 
    -#> 4             0.1        0.100             0      
    -#> 5             0.1        0.102             0      
    -
    -# plot the phylogeny and color the adequately represented features in red
    -plot(sim_phylogeny, main = "adequately represented features",
    -     tip.color = replace(
    -       rep("black", nlayers(sim_features)),
    -       sim_phylogeny$tip.label %in% r1$feature[r1$met],
    -       "red"))
    -
    -# }
    -# rename the features in the example phylogeny for use with the
    -# multi-zone data
    -sim_phylogeny$tip.label <- feature_names(sim_features_zones)
    -
    -# create targets for a multi-zone problem. Here, each feature needs a total
    -# of 10 units of habitat to be conserved among the three zones to be
    -# considered adequately conserved
    -targets <- tibble::tibble(
    -  feature = feature_names(sim_features_zones),
    -  zone = list(zone_names(sim_features_zones))[rep(1,
    -          number_of_features(sim_features_zones))],
    -  type = rep("absolute", number_of_features(sim_features_zones)),
    -  target = rep(10, number_of_features(sim_features_zones)))
    -
    -# create a multi-zone problem with a maximum phylogenetic endemism
    -# objective, where the total expenditure in all zones is 5000.
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_phylo_end_objective(5000, sim_phylogeny) %>%
    -      add_manual_targets(targets) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find out which features have their targets met
    -r2 <- eval_target_coverage_summary(p2, s2)
    -print(r2, width = Inf)
    -#> # A tibble: 5 × 11
    -#>   feature   zone      sense met   total_amount absolute_target absolute_held
    -#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    -#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         19.6 
    -#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          6.72
    -#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         16.3 
    -#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.0 
    -#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         13.7 
    -#>   absolute_shortfall relative_target relative_held relative_shortfall
    -#>                <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1               0             0.0400        0.0786             0     
    -#> 2               3.28          0.107         0.0717             0.0351
    -#> 3               0             0.0463        0.0753             0     
    -#> 4               0             0.0781        0.0782             0     
    -#> 5               0             0.0588        0.0807             0     
    -
    -# plot the phylogeny and color the adequately represented features in red
    -plot(sim_phylogeny, main = "adequately represented features",
    -     tip.color = replace(rep("black", nlayers(sim_features)),
    -                         which(r2$met), "red"))
    -
    -# }
    -# create a multi-zone problem with a maximum phylogenetic endemism
    -# objective, where each zone has a separate budget.
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_phylo_end_objective(c(2500, 500, 2000), sim_phylogeny) %>%
    -      add_manual_targets(targets) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# find out which features have their targets met
    -r3 <- eval_target_coverage_summary(p3, s3)
    -print(r3, width = Inf)
    -#> # A tibble: 5 × 11
    -#>   feature   zone      sense met   total_amount absolute_target absolute_held
    -#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    -#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         15.7 
    -#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          5.73
    -#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         12.1 
    -#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.0 
    -#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         11.4 
    -#>   absolute_shortfall relative_target relative_held relative_shortfall
    -#>                <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1               0             0.0400        0.0628             0     
    -#> 2               4.27          0.107         0.0612             0.0456
    -#> 3               0             0.0463        0.0559             0     
    -#> 4               0             0.0781        0.0782             0     
    -#> 5               0             0.0588        0.0669             0     
    -
    -# plot the phylogeny and color the adequately represented features in red
    -plot(sim_phylogeny, main = "adequately represented features",
    -     tip.color = replace(rep("black", nlayers(sim_features)),
    -                         which(r3$met), "red"))
    -
    -# }
    +    
    +

    Examples

    +
    # load ape package
    +require(ape)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_phylogeny, sim_pu_zones_stack,
    +     sim_features_zones)
    +
    +# plot the simulated phylogeny
    +# \dontrun{
    +par(mfrow = c(1, 1))
    +plot(sim_phylogeny, main = "phylogeny")
    +
    +# }
    +# create problem with a maximum phylogenetic endemism objective,
    +# where each feature needs 10% of its distribution to be secured for
    +# it to be adequately conserved and a total budget of 1900
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_phylo_end_objective(1900, sim_phylogeny) %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find out which features have their targets met
    +r1 <- eval_target_coverage_summary(p1, s1)
    +print(r1, width = Inf)
    +#> # A tibble: 5 × 9
    +#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1 layer.1 FALSE         83.3            8.33          7.71              0.618
    +#> 2 layer.2 FALSE         31.2            3.12          2.35              0.775
    +#> 3 layer.3 FALSE         72.0            7.20          5.81              1.38 
    +#> 4 layer.4 TRUE          42.7            4.27          4.28              0    
    +#> 5 layer.5 TRUE          56.7            5.67          5.77              0    
    +#>   relative_target relative_held relative_shortfall
    +#>             <dbl>         <dbl>              <dbl>
    +#> 1             0.1        0.0926            0.00742
    +#> 2             0.1        0.0752            0.0248 
    +#> 3             0.1        0.0808            0.0192 
    +#> 4             0.1        0.100             0      
    +#> 5             0.1        0.102             0      
    +
    +# plot the phylogeny and color the adequately represented features in red
    +plot(sim_phylogeny, main = "adequately represented features",
    +     tip.color = replace(
    +       rep("black", nlayers(sim_features)),
    +       sim_phylogeny$tip.label %in% r1$feature[r1$met],
    +       "red"))
    +
    +# }
    +# rename the features in the example phylogeny for use with the
    +# multi-zone data
    +sim_phylogeny$tip.label <- feature_names(sim_features_zones)
    +
    +# create targets for a multi-zone problem. Here, each feature needs a total
    +# of 10 units of habitat to be conserved among the three zones to be
    +# considered adequately conserved
    +targets <- tibble::tibble(
    +  feature = feature_names(sim_features_zones),
    +  zone = list(zone_names(sim_features_zones))[rep(1,
    +          number_of_features(sim_features_zones))],
    +  type = rep("absolute", number_of_features(sim_features_zones)),
    +  target = rep(10, number_of_features(sim_features_zones)))
    +
    +# create a multi-zone problem with a maximum phylogenetic endemism
    +# objective, where the total expenditure in all zones is 5000.
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_phylo_end_objective(5000, sim_phylogeny) %>%
    +      add_manual_targets(targets) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find out which features have their targets met
    +r2 <- eval_target_coverage_summary(p2, s2)
    +print(r2, width = Inf)
    +#> # A tibble: 5 × 11
    +#>   feature   zone      sense met   total_amount absolute_target absolute_held
    +#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    +#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         19.6 
    +#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          6.72
    +#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         16.3 
    +#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.0 
    +#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         13.7 
    +#>   absolute_shortfall relative_target relative_held relative_shortfall
    +#>                <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1               0             0.0400        0.0786             0     
    +#> 2               3.28          0.107         0.0717             0.0351
    +#> 3               0             0.0463        0.0753             0     
    +#> 4               0             0.0781        0.0782             0     
    +#> 5               0             0.0588        0.0807             0     
    +
    +# plot the phylogeny and color the adequately represented features in red
    +plot(sim_phylogeny, main = "adequately represented features",
    +     tip.color = replace(rep("black", nlayers(sim_features)),
    +                         which(r2$met), "red"))
    +
    +# }
    +# create a multi-zone problem with a maximum phylogenetic endemism
    +# objective, where each zone has a separate budget.
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_phylo_end_objective(c(2500, 500, 2000), sim_phylogeny) %>%
    +      add_manual_targets(targets) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# find out which features have their targets met
    +r3 <- eval_target_coverage_summary(p3, s3)
    +print(r3, width = Inf)
    +#> # A tibble: 5 × 11
    +#>   feature   zone      sense met   total_amount absolute_target absolute_held
    +#>   <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    +#> 1 feature_1 <chr [3]> >=    TRUE         250.               10         15.7 
    +#> 2 feature_2 <chr [3]> >=    FALSE         93.6              10          5.73
    +#> 3 feature_3 <chr [3]> >=    TRUE         216.               10         12.1 
    +#> 4 feature_4 <chr [3]> >=    TRUE         128.               10         10.0 
    +#> 5 feature_5 <chr [3]> >=    TRUE         170.               10         11.4 
    +#>   absolute_shortfall relative_target relative_held relative_shortfall
    +#>                <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1               0             0.0400        0.0628             0     
    +#> 2               4.27          0.107         0.0612             0.0456
    +#> 3               0             0.0463        0.0559             0     
    +#> 4               0             0.0781        0.0782             0     
    +#> 5               0             0.0588        0.0669             0     
    +
    +# plot the phylogeny and color the adequately represented features in red
    +plot(sim_phylogeny, main = "adequately represented features",
    +     tip.color = replace(rep("black", nlayers(sim_features)),
    +                         which(r3$met), "red"))
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_max_utility_objective.html b/docs/reference/add_max_utility_objective.html index 073f5c7c9..258e31805 100644 --- a/docs/reference/add_max_utility_objective.html +++ b/docs/reference/add_max_utility_objective.html @@ -1,108 +1,25 @@ - - - - - - - -Add maximum utility objective — add_max_utility_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add maximum utility objective — add_max_utility_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to +

    Set the objective of a conservation planning problem() to secure as much of the features as possible without exceeding a budget. This objective does not use targets, and feature weights should be used instead to increase the representation of certain @@ -198,39 +115,35 @@

    Add maximum utility objective

    just a few features.

    -
    add_max_utility_objective(x, budget)
    +
    Usage,
    add_max_utility_objective(x, budget)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    budget

    numeric value specifying the maximum expenditure of +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    budget
    +

    numeric value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument to budget can be (i) a single numeric value to specify a single budget for the entire solution or (ii) a numeric vector to specify -a separate budget for each management zone.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +a separate budget for each management zone.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The maximum utility objective seeks to maximize the overall level of representation across a suite of conservation features, while keeping cost within a fixed budget. Additionally, weights can be used to favor the representation of certain features over other features (see -add_feature_weights()).

    -

    Mathematical formulation

    - +add_feature_weights()).

    +
    +
    +

    Mathematical formulation

    This objective can be expressed mathematically for a set of planning units @@ -240,110 +153,110 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(r_{ij}\) is the amount of feature \(j\) in planning unit \(i\), \(A_j\) is the amount of feature \(j\) represented in in the solution, and \(w_j\) is the weight for feature \(j\) (defaults to 1 for all features; see -add_feature_weights() +add_feature_weights() to specify weights). Additionally, \(B\) is the budget allocated for the solution, \(c_i\) is the cost of planning unit \(i\), and \(s\) is a scaling factor used to shrink the costs so that the problem will return a cheapest solution when there are multiple solutions that represent the same amount of all features within the budget.

    -

    Notes

    - +
    +
    +

    Notes

    In early versions (< 3.0.0.0), this function was named as the add_max_cover_objective function. It was renamed to avoid confusion with existing terminology.

    -

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also, see add_feature_weights() to specify weights for different features.

    +
    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# create problem with maximum utility objective
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_utility_objective(5000) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create multi-zone problem with maximum utility objective that
    -# has a single budget for all zones
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_utility_objective(5000) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create multi-zone problem with maximum utility objective that
    -# has separate budgets for each zone
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_max_utility_objective(c(1000, 2000, 3000)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# create problem with maximum utility objective
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_utility_objective(5000) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create multi-zone problem with maximum utility objective that
    +# has a single budget for all zones
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_utility_objective(5000) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create multi-zone problem with maximum utility objective that
    +# has separate budgets for each zone
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_max_utility_objective(c(1000, 2000, 3000)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_min_largest_shortfall_objective.html b/docs/reference/add_min_largest_shortfall_objective.html index 76965913b..f44cd4d2f 100644 --- a/docs/reference/add_min_largest_shortfall_objective.html +++ b/docs/reference/add_min_largest_shortfall_objective.html @@ -1,107 +1,24 @@ - - - - - - - -Add minimum largest shortfall objective — add_min_largest_shortfall_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add minimum largest shortfall objective — add_min_largest_shortfall_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to +

    Set the objective of a conservation planning problem() to minimize the largest target shortfall while ensuring that the cost of the solution does not exceed a budget. Note that if the target shortfall for a single feature cannot be decreased beyond a certain -point (e.g. because all remaining planning units occupied by that feature +point (e.g., because all remaining planning units occupied by that feature are too costly or are locked out), then solutions may only use a small proportion of the specified budget.

    -
    add_min_largest_shortfall_objective(x, budget)
    +
    Usage,
    add_min_largest_shortfall_objective(x, budget)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    budget

    numeric value specifying the maximum expenditure of +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    budget
    +

    numeric value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument to budget can be (i) a single numeric value to specify a single budget for the entire solution or (ii) a numeric vector to specify -a separate budget for each management zone.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +a separate budget for each management zone.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The minimum largest shortfall objective aims to find the set of planning units that minimize the largest shortfall for any of the representation targets---that is, the fraction of each target that remains unmet---for as many features as possible while staying within a fixed budget. This objective is different from the -minimum shortfall objective (add_min_shortfall_objective()) because this +minimum shortfall objective (add_min_shortfall_objective()) because this objective minimizes the largest (maximum) target shortfall, whereas the minimum shortfall objective minimizes the total (weighted sum) of the target shortfalls. Note that this objective function is not compatible with feature weights -(add_feature_weights()).

    -

    Mathematical formulation

    - +(add_feature_weights()).

    +
    +
    +

    Mathematical formulation

    This objective can be expressed mathematically for a set of planning units @@ -244,7 +157,7 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(r_{ij}\) is the amount of feature \(j\) in planning unit \(i\), and \(t_j\) is the representation target for feature @@ -256,97 +169,96 @@

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also, see targets for an overview of all functions for adding targets, and -add_feature_weights() to specify weights for different features.

    +
    +
    +

    See also

    +

    See objectives for an overview of all functions for adding objectives. +Also, see targets for an overview of all functions for adding targets, and +add_feature_weights() to specify weights for different features.

    Other objectives: -add_max_cover_objective(), -add_max_features_objective(), -add_max_phylo_div_objective(), -add_max_phylo_end_objective(), -add_max_utility_objective(), -add_min_set_objective(), -add_min_shortfall_objective()

    +add_max_cover_objective(), +add_max_features_objective(), +add_max_phylo_div_objective(), +add_max_phylo_end_objective(), +add_max_utility_objective(), +add_min_set_objective(), +add_min_shortfall_objective()

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# create problem with minimum largest shortfall objective
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_largest_shortfall_objective(1800) %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create multi-zone problem with minimum largest shortfall objective,
    -# with 10% representation targets for each feature, and set
    -# a budget such that the total maximum expenditure in all zones
    -# cannot exceed 3000
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_largest_shortfall_objective(3000) %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create multi-zone problem with minimum largest shortfall objective,
    -# with 10% representation targets for each feature, and set
    -# separate budgets for each management zone
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_largest_shortfall_objective(c(3000, 3000, 3000)) %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# create problem with minimum largest shortfall objective
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_largest_shortfall_objective(1800) %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create multi-zone problem with minimum largest shortfall objective,
    +# with 10% representation targets for each feature, and set
    +# a budget such that the total maximum expenditure in all zones
    +# cannot exceed 3000
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_largest_shortfall_objective(3000) %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create multi-zone problem with minimum largest shortfall objective,
    +# with 10% representation targets for each feature, and set
    +# separate budgets for each management zone
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_largest_shortfall_objective(c(3000, 3000, 3000)) %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_min_set_objective.html b/docs/reference/add_min_set_objective.html index b90a8d506..abd8e557a 100644 --- a/docs/reference/add_min_set_objective.html +++ b/docs/reference/add_min_set_objective.html @@ -1,104 +1,21 @@ - - - - - - - -Add minimum set objective — add_min_set_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add minimum set objective — add_min_set_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to -minimize the cost of the solution whilst ensuring that all targets are met. +

    Set the objective of a conservation planning problem() to +minimize the cost of the solution whilst ensuring that all targets are met. This objective is similar to that used in Marxan and is detailed in Rodrigues et al. (2000).

    -
    add_min_set_objective(x)
    - -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    +
    Usage,
    add_min_set_objective(x)
    -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The minimum set objective -- in the the context of systematic reserve design -- seeks to find the set of planning units that minimizes the overall cost of a reserve network, while meeting a set of representation @@ -215,8 +129,9 @@

    Details (BLM) set to zero. The difference between this objective and the Marxan software is that the targets for the features will always be met (and as such it does not use Species Penalty Factors).

    -

    Mathematical formulation

    - +
    +
    +

    Mathematical formulation

    This objective can be expressed @@ -225,7 +140,7 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(c_i\) is the cost of planning unit \(i\), \(r_{ij}\) is the amount of feature \(j\) in planning unit @@ -233,87 +148,87 @@

    References

    - +
    +
    +

    References

    Rodrigues AS, Cerdeira OJ, and Gaston KJ (2000) Flexibility, efficiency, and accountability: adapting reserve selection algorithms to more complex conservation problems. Ecography, 23: 565--574.

    -

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also see targets for an overview of all functions for adding targets.

    +
    + +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem with minimum set objective
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create multi-zone problem with minimum set objective
    -targets_matrix <- matrix(rpois(15, 1), nrow = 5, ncol = 3)
    -
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(targets_matrix) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem with minimum set objective
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create multi-zone problem with minimum set objective
    +targets_matrix <- matrix(rpois(15, 1), nrow = 5, ncol = 3)
    +
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(targets_matrix) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/add_min_shortfall_objective.html b/docs/reference/add_min_shortfall_objective.html index efd570c99..c37a87560 100644 --- a/docs/reference/add_min_shortfall_objective.html +++ b/docs/reference/add_min_shortfall_objective.html @@ -1,103 +1,20 @@ - - - - - - - -Add minimum shortfall objective — add_min_shortfall_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add minimum shortfall objective — add_min_shortfall_objective • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Set the objective of a conservation planning problem() to -minimize the overall shortfall for as many targets as possible while +

    Set the objective of a conservation planning problem() to +minimize the overall shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget.

    -
    add_min_shortfall_objective(x, budget)
    +
    Usage,
    add_min_shortfall_objective(x, budget)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    budget

    numeric value specifying the maximum expenditure of +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    budget
    +

    numeric value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument to budget can be (i) a single numeric value to specify a single budget for the entire solution or (ii) a numeric vector to specify -a separate budget for each management zone.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the objective +a separate budget for each management zone.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the objective added to it.

    -

    Details

    - +
    +
    +

    Details

    The minimum shortfall objective aims to find the set of planning units that minimize the overall (weighted sum) shortfall for the @@ -221,9 +133,10 @@

    Details fixed budget (inspired by Table 1, equation IV, Arponen et al. 2005). Additionally, weights can be used to favor the representation of certain features over other features (see -add_feature_weights().

    -

    Mathematical formulation

    - +add_feature_weights().

    +
    +
    +

    Mathematical formulation

    This objective can be expressed mathematically for a set of planning units @@ -233,114 +146,114 @@

    decisions variable (e.g., specifying whether planning unit \(i\) has been selected (1) or not (0)), \(r_{ij}\) is the amount of feature \(j\) in planning unit \(i\), \(t_j\) is the representation target for feature \(j\), \(y_j\) denotes the representation shortfall for the target \(t_j\) for feature \(j\), and \(w_j\) is the weight for feature \(j\) (defaults to 1 for all features; see -add_feature_weights() to specify weights). Additionally, +add_feature_weights() to specify weights). Additionally, \(B\) is the budget allocated for the solution, \(c_i\) is the cost of planning unit \(i\). Note that \(y_j\) is a continuous variable bounded between zero and infinity, and denotes the shortfall for target \(j\).

    -

    References

    - +
    +
    +

    References

    Arponen A, Heikkinen RK, Thomas CD, and Moilanen A (2005) The value of biodiversity in reserve selection: representation, species weighting, and benefit functions. Conservation Biology, 19: 2009--2014.

    -

    See also

    - -

    See objectives for an overview of all functions for adding objectives. -Also, see targets for an overview of all functions for adding targets, and -add_feature_weights() to specify weights for different features.

    +
    +
    +

    See also

    +

    See objectives for an overview of all functions for adding objectives. +Also, see targets for an overview of all functions for adding targets, and +add_feature_weights() to specify weights for different features.

    Other objectives: -add_max_cover_objective(), -add_max_features_objective(), -add_max_phylo_div_objective(), -add_max_phylo_end_objective(), -add_max_utility_objective(), -add_min_largest_shortfall_objective(), -add_min_set_objective()

    +add_max_cover_objective(), +add_max_features_objective(), +add_max_phylo_div_objective(), +add_max_phylo_end_objective(), +add_max_utility_objective(), +add_min_largest_shortfall_objective(), +add_min_set_objective()

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# create problem with minimum shortfall objective
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_shortfall_objective(1800) %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create multi-zone problem with minimum shortfall objective,
    -# with 10% representation targets for each feature, and set
    -# a budget such that the total maximum expenditure in all zones
    -# cannot exceed 3000
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_shortfall_objective(3000) %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create multi-zone problem with minimum shortfall objective,
    -# with 10% representation targets for each feature, and set
    -# separate budgets for each management zone
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_shortfall_objective(c(3000, 3000, 3000)) %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# create problem with minimum shortfall objective
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_shortfall_objective(1800) %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create multi-zone problem with minimum shortfall objective,
    +# with 10% representation targets for each feature, and set
    +# a budget such that the total maximum expenditure in all zones
    +# cannot exceed 3000
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_shortfall_objective(3000) %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create multi-zone problem with minimum shortfall objective,
    +# with 10% representation targets for each feature, and set
    +# separate budgets for each management zone
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_shortfall_objective(c(3000, 3000, 3000)) %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_neighbor_constraints.html b/docs/reference/add_neighbor_constraints.html index fbc2353c5..ba32413ad 100644 --- a/docs/reference/add_neighbor_constraints.html +++ b/docs/reference/add_neighbor_constraints.html @@ -1,103 +1,20 @@ - - - - - - - -Add neighbor constraints — add_neighbor_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add neighbor constraints — add_neighbor_constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Add constraints to a conservation planning problem() to ensure +

    Add constraints to a conservation planning problem() to ensure that all selected planning units in the solution have at least a certain number of neighbors that are also selected in the solution.

    -
    # S4 method for ConservationProblem,ANY,ANY,ANY
    -add_neighbor_constraints(x, k, zones, data)
    +    
    Usage,
    # S4 method for ConservationProblem,ANY,ANY,ANY
    +add_neighbor_constraints(x, k, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,data.frame
    -add_neighbor_constraints(x, k, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY,data.frame
    +add_neighbor_constraints(x, k, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,matrix
    -add_neighbor_constraints(x, k, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY,matrix
    +add_neighbor_constraints(x, k, zones, data)
     
    -# S4 method for ConservationProblem,ANY,ANY,array
    -add_neighbor_constraints(x, k, zones, data)
    +# S4 method for ConservationProblem,ANY,ANY,array +add_neighbor_constraints(x, k, zones, data)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    k

    integer minimum number of neighbors for selected +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    k
    +

    integer minimum number of neighbors for selected planning units in the solution. For problems with multiple zones, -the argument to k must have an element for each zone.

    zones

    matrix or Matrix object describing the +the argument to k must have an element for each zone.

    +
    zones
    +

    matrix or Matrix object describing the neighborhood scheme for different zones. Each row and column corresponds to a different zone in the argument to x, and cell values must -contain binary numeric values (i.e. one or zero) that indicate +contain binary numeric values (i.e., one or zero) that indicate if neighboring planning units (as specified in the argument to data) should be considered neighbors if they are allocated to different zones. The cell values along the diagonal of the matrix indicate if planning units that are allocated to the same zone should be considered neighbors or not. The default argument to -zones is an identity matrix (i.e. a matrix with ones along the +zones is an identity matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are -only considered neighbors if they are both allocated to the same zone.

    data

    NULL, matrix, Matrix, data.frame, or +only considered neighbors if they are both allocated to the same zone.

    +
    data
    +

    NULL, matrix, Matrix, data.frame, or array object showing which planning units are neighbors with each other. The argument defaults to NULL which means that the neighborhood data is calculated automatically using the -adjacency_matrix() function. -See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the constraints +adjacency_matrix() function. +See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Details

    - +
    +
    +

    Details

    This function uses neighborhood data identify solutions that surround planning units with a minimum number of neighbors. It was inspired by the mathematical formulations detailed in Billionnet (2013) and Beyer et al. (2016).

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data can be specified using the following formats:

    -
    - -
    data as a NULL value

    neighborhood data should be calculated +

    data as a NULL value
    +

    neighborhood data should be calculated automatically -using the adjacency_matrix() function. This is the default +using the adjacency_matrix() function. This is the default argument. Note that the neighborhood data must be manually defined using one of the other formats below when the planning unit data -in the argument to x is not spatially referenced (e.g. +in the argument to x is not spatially referenced (e.g., in data.frame or numeric format).

    -
    data as a matrix/Matrix object

    where rows and columns represent + +

    data as a matrix/Matrix object
    +

    where rows and columns represent different planning units and the value of each cell indicates if the two planning units are neighbors or not. Cell values should be binary -numeric values (i.e. one or zero). Cells that occur along the +numeric values (i.e., one or zero). Cells that occur along the matrix diagonal have no effect on the solution at all because each planning unit cannot be a neighbor with itself.

    -
    data as a data.frame object

    containing the fields (columns) + +

    data as a data.frame object
    +

    containing the fields (columns) "id1", "id2", and "boundary". Here, each row denotes the connectivity between two planning units following the Marxan format. The field boundary should contain @@ -280,7 +192,7 @@

    x contains multiple zones, then the columns @@ -290,11 +202,13 @@

    "zone1" and "zone2" are present, then the argument to zones must be NULL.

    -
    data as an array object

    containing four-dimensions where binary + +

    data as an array object
    +

    containing four-dimensions where binary numeric values indicate if planning unit should be treated as being neighbors with every other planning unit when they are allocated to every combination of management zone. The first two -dimensions (i.e. rows and columns) correspond to the planning units, +dimensions (i.e., rows and columns) correspond to the planning units, and second two dimensions correspond to the management zones. For example, if the argument to data had a value of 1 at the index data[1, 2, 3, 4] this would indicate that planning unit 1 and @@ -302,134 +216,133 @@

    -
    - -

    References

    +
    +
    +

    References

    Beyer HL, Dujardin Y, Watts ME, and Possingham HP (2016) Solving conservation planning problems with integer linear programming. Ecological Modelling, 228: 14--22.

    Billionnet A (2013) Mathematical optimization ideas for biodiversity conservation. European Journal of Operational Research, 231: 514--534.

    -

    See also

    - -

    See constraints for an overview of all functions for adding constraints.

    +
    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with constraints that require 1 neighbor
    -# and neighbors are defined using a rook-style neighborhood
    -p2 <- p1 %>% add_neighbor_constraints(1)
    -
    -# create problem with constraints that require 2 neighbor
    -# and neighbors are defined using a rook-style neighborhood
    -p3 <- p1 %>% add_neighbor_constraints(2)
    -
    -# create problem with constraints that require 3 neighbor
    -# and neighbors are defined using a queen-style neighborhood
    -p4 <- p1 %>% add_neighbor_constraints(3,
    -               data = adjacency_matrix(sim_pu_raster, directions = 8))
    -
    -# \dontrun{
    -# solve problems
    -s1 <- stack(list(solve(p1), solve(p2), solve(p3), solve(p4)))
    -
    -# plot solutions
    -plot(s1, box = FALSE, axes = FALSE,
    -     main = c("basic solution", "1 neighbor", "2 neighbors", "3 neighbors"))
    -
    -# }
    -# create minimal problem with multiple zones
    -p5 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem where selected planning units require at least 2 neighbors
    -# for each zone and planning units are only considered neighbors if they
    -# are allocated to the same zone
    -z6 <- diag(3)
    -print(z6)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    0    0
    -#> [2,]    0    1    0
    -#> [3,]    0    0    1
    -p6 <- p5 %>% add_neighbor_constraints(rep(2, 3), z6)
    -
    -# create problem where the planning units in zone 1 don't explicitly require
    -# any neighbors, planning units in zone 2 require at least 1 neighbors, and
    -# planning units in zone 3 require at least 2 neighbors. As before, planning
    -# units are still only considered neighbors if they are allocated to the
    -# same zone
    -p7 <- p5 %>% add_neighbor_constraints(c(0, 1, 2), z6)
    -
    -# create problem given the same constraints as outlined above, except
    -# that when determining which selected planning units are neighbors,
    -# planning units that are allocated to zone 1 and zone 2 can also treated
    -# as being neighbors with each other
    -z8 <- diag(3)
    -z8[1, 2] <- 1
    -z8[2, 1] <- 1
    -print(z8)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    1    0
    -#> [2,]    1    1    0
    -#> [3,]    0    0    1
    -p8 <- p5 %>% add_neighbor_constraints(c(0, 1, 2), z8)
    -# \dontrun{
    -# solve problems
    -s2 <- list(p5, p6, p7, p8)
    -s2 <- lapply(s2, solve)
    -s2 <- lapply(s2, category_layer)
    -s2 <- stack(s2)
    -names(s2) <- c("basic problem", "p6", "p7", "p8")
    -
    -# plot solutions
    -plot(s2, main = names(s2), box = FALSE, axes = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with constraints that require 1 neighbor
    +# and neighbors are defined using a rook-style neighborhood
    +p2 <- p1 %>% add_neighbor_constraints(1)
    +
    +# create problem with constraints that require 2 neighbor
    +# and neighbors are defined using a rook-style neighborhood
    +p3 <- p1 %>% add_neighbor_constraints(2)
    +
    +# create problem with constraints that require 3 neighbor
    +# and neighbors are defined using a queen-style neighborhood
    +p4 <- p1 %>% add_neighbor_constraints(3,
    +               data = adjacency_matrix(sim_pu_raster, directions = 8))
    +
    +# \dontrun{
    +# solve problems
    +s1 <- stack(list(solve(p1), solve(p2), solve(p3), solve(p4)))
    +
    +# plot solutions
    +plot(s1, box = FALSE, axes = FALSE,
    +     main = c("basic solution", "1 neighbor", "2 neighbors", "3 neighbors"))
    +
    +# }
    +# create minimal problem with multiple zones
    +p5 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(0.1, ncol = 3, nrow = 5)) %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem where selected planning units require at least 2 neighbors
    +# for each zone and planning units are only considered neighbors if they
    +# are allocated to the same zone
    +z6 <- diag(3)
    +print(z6)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    0    0
    +#> [2,]    0    1    0
    +#> [3,]    0    0    1
    +p6 <- p5 %>% add_neighbor_constraints(rep(2, 3), z6)
    +
    +# create problem where the planning units in zone 1 don't explicitly require
    +# any neighbors, planning units in zone 2 require at least 1 neighbors, and
    +# planning units in zone 3 require at least 2 neighbors. As before, planning
    +# units are still only considered neighbors if they are allocated to the
    +# same zone
    +p7 <- p5 %>% add_neighbor_constraints(c(0, 1, 2), z6)
    +
    +# create problem given the same constraints as outlined above, except
    +# that when determining which selected planning units are neighbors,
    +# planning units that are allocated to zone 1 and zone 2 can also treated
    +# as being neighbors with each other
    +z8 <- diag(3)
    +z8[1, 2] <- 1
    +z8[2, 1] <- 1
    +print(z8)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    1    0
    +#> [2,]    1    1    0
    +#> [3,]    0    0    1
    +p8 <- p5 %>% add_neighbor_constraints(c(0, 1, 2), z8)
    +# \dontrun{
    +# solve problems
    +s2 <- list(p5, p6, p7, p8)
    +s2 <- lapply(s2, solve)
    +s2 <- lapply(s2, category_layer)
    +s2 <- stack(s2)
    +names(s2) <- c("basic problem", "p6", "p7", "p8")
    +
    +# plot solutions
    +plot(s2, main = names(s2), box = FALSE, axes = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_proportion_decisions.html b/docs/reference/add_proportion_decisions.html index ee7557c4e..08b8bdb1f 100644 --- a/docs/reference/add_proportion_decisions.html +++ b/docs/reference/add_proportion_decisions.html @@ -1,106 +1,23 @@ - - - - - - - -Add proportion decisions — add_proportion_decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add proportion decisions — add_proportion_decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Add a proportion decision to a conservation planning problem(). +

    Add a proportion decision to a conservation planning problem(). This is a relaxed decision where a part of a planning unit can be prioritized as opposed to the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in @@ -194,111 +111,107 @@

    Add proportion decisions

    decisions

    -
    add_proportion_decisions(x)
    - -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    +
    Usage,
    add_proportion_decisions(x)
    -

    Value

    - -

    Object (i.e. ConservationProblem) with the decisions added +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the decisions added to it.

    -

    Details

    - +
    +
    +

    Details

    Conservation planning problems involve making decisions on planning -units. These decisions are then associated with actions (e.g. turning a +units. These decisions are then associated with actions (e.g., turning a planning unit into a protected area). Only a single decision should be added to a ConservationProblem object. Note that if multiple decisions are added to a problem object, then the last one to be added will be used.

    -

    See also

    - -

    See decisions for an overview of all functions for adding decisions.

    +
    +
    +

    See also

    +

    See decisions for an overview of all functions for adding decisions.

    Other decisions: -add_binary_decisions(), -add_default_decisions(), -add_semicontinuous_decisions()

    +add_binary_decisions(), +add_default_decisions(), +add_semicontinuous_decisions()

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem with proportion decisions
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_proportion_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solutions
    -plot(s1, main = "solution")
    -
    -# }
    -# build multi-zone conservation problem with proportion decisions
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_proportion_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print solution
    -print(s2)
    -#> class      : RasterStack 
    -#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> names      : zone_1, zone_2, zone_3 
    -#> min values :      0,      0,      0 
    -#> max values :      1,      1,      1 
    -#> 
    -
    -# plot solution
    -# panels show the proportion of each planning unit allocated to each zone
    -plot(s2, axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem with proportion decisions
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_proportion_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solutions
    +plot(s1, main = "solution")
    +
    +# }
    +# build multi-zone conservation problem with proportion decisions
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_proportion_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print solution
    +print(s2)
    +#> class      : RasterStack 
    +#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> names      : zone_1, zone_2, zone_3 
    +#> min values :      0,      0,      0 
    +#> max values :      1,      1,      1 
    +#> 
    +
    +# plot solution
    +# panels show the proportion of each planning unit allocated to each zone
    +plot(s2, axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/add_relative_targets.html b/docs/reference/add_relative_targets.html index 715696564..aab1f4751 100644 --- a/docs/reference/add_relative_targets.html +++ b/docs/reference/add_relative_targets.html @@ -1,105 +1,22 @@ - - - - - - - -Add relative targets — add_relative_targets • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add relative targets — add_relative_targets • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -189,51 +106,46 @@

    Add relative targets

    representation of features in the study area. Please note that proportions are scaled according to the features' total abundances in the study area (including any locked out planning units, or planning units with NA -cost data) using the feature_abundances() function.

    +cost data) using the feature_abundances() function.

    -
    add_relative_targets(x, targets)
    -
    -# S4 method for ConservationProblem,numeric
    -add_relative_targets(x, targets)
    +    
    Usage,
    add_relative_targets(x, targets)
     
    -# S4 method for ConservationProblem,matrix
    -add_relative_targets(x, targets)
    +# S4 method for ConservationProblem,numeric
    +add_relative_targets(x, targets)
     
    -# S4 method for ConservationProblem,character
    -add_relative_targets(x, targets)
    +# S4 method for ConservationProblem,matrix +add_relative_targets(x, targets) -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    targets

    Object that specifies the targets for each feature. -See the Targets format section for more information.

    +# S4 method for ConservationProblem,character +add_relative_targets(x, targets)
    -

    Value

    - -

    Object (i.e. ConservationProblem) with the targets added +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    targets
    +

    Object that specifies the targets for each feature. +See the Targets format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the targets added to it.

    -

    Details

    - +
    +
    +

    Details

    Targets are used to specify the minimum amount or proportion of a feature's distribution that needs to be protected. Most conservation planning problems require targets with the exception of the maximum cover -(see add_max_cover_objective()) and maximum utility -(see add_max_utility_objective()) problems. Attempting to solve +(see add_max_cover_objective()) and maximum utility +(see add_max_utility_objective()) problems. Attempting to solve problems with objectives that require targets without specifying targets will throw an error.

    For problems associated with multiple management zones, this function can be used to set targets that each pertain to a single feature and a single zone. To set targets which can be met through allocating different -planning units to multiple zones, see the add_manual_targets() +planning units to multiple zones, see the add_manual_targets() function. An example of a target that could be met through allocations to multiple zones might be where each management zone is expected to result in a different amount of a feature and the target requires that @@ -244,150 +156,152 @@

    Details potentially, be met through allocating all planning units to any specific management zone, or through allocating the planning units to different combinations of management zones.

    -

    Targets format

    - +
    +
    +

    Targets format

    The targets for a problem can be specified using the following formats.

    -
    - -
    targets as a numeric vector

    containing target values for each +

    targets as a numeric vector
    +

    containing target values for each feature. Additionally, for convenience, this format can be a single value to assign the same target to each feature. Note that this format cannot be used to specify targets for problems with multiple zones.

    -
    targets as a matrix object

    containing a target for each feature + +

    targets as a matrix object
    +

    containing a target for each feature in each zone. Here, each row corresponds to a different feature in argument to x, each column corresponds to a different zone in argument to x, and each cell contains the target value for a given feature that the solution needs to secure in a given zone.

    -
    targets as a character vector

    containing the names of fields + +

    targets as a character vector
    +

    containing the names of fields (columns) in the feature data associated with the argument to x that contain targets. This format can only be used when the feature data associated with x is a data.frame. This argument must contain a field (column) name for each zone.

    -
    - -

    See also

    -

    See targets for an overview of all functions for adding targets.

    +
    +
    +

    See also

    +

    See targets for an overview of all functions for adding targets.

    Other targets: -add_absolute_targets(), -add_loglinear_targets(), -add_manual_targets()

    +add_absolute_targets(), +add_loglinear_targets(), +add_manual_targets()

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create base problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(verbose = FALSE)
    -
    -# create problem with 10% targets
    -p1 <- p %>% add_relative_targets(0.1)
    -
    -# create problem with varying targets for each feature
    -targets <- c(0.1, 0.2, 0.3, 0.4, 0.5)
    -p2 <- p %>% add_relative_targets(targets)
    -# \dontrun{
    -# solve problem
    -s <- stack(solve(p1), solve(p2))
    -
    -# plot solution
    -plot(s, main = c("10% targets", "varying targets"), axes = FALSE,
    -     box = FALSE)
    -
    -# }
    -# create a problem with multiple management zones
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create a problem with targets that specify an equal amount of each feature
    -# to be represented in each zone
    -p4_targets <- matrix(0.1, nrow = 5, ncol = 3,
    -                     dimnames = list(feature_names(sim_features_zones),
    -                                     zone_names(sim_features_zones)))
    -print(p4_targets)
    -#>           zone_1 zone_2 zone_3
    -#> feature_1    0.1    0.1    0.1
    -#> feature_2    0.1    0.1    0.1
    -#> feature_3    0.1    0.1    0.1
    -#> feature_4    0.1    0.1    0.1
    -#> feature_5    0.1    0.1    0.1
    -
    -p4 <- p3 %>% add_relative_targets(p4_targets)
    -
    -# solve problem
    -# \dontrun{
    -# solve problem
    -s4 <- solve(p4)
    -
    -# plot solution (pixel values correspond to zone identifiers)
    -plot(category_layer(s4), main = c("equal targets"))
    -
    -# }
    -# create a problem with targets that require a varying amount of each
    -# feature to be represented in each zone
    -p5_targets <- matrix(runif(15, 0.01, 0.2), nrow = 5, ncol = 3,
    -                     dimnames = list(feature_names(sim_features_zones),
    -                                     zone_names(sim_features_zones)))
    -print(p5_targets)
    -#>               zone_1     zone_2     zone_3
    -#> feature_1 0.16838399 0.04908221 0.06359158
    -#> feature_2 0.14775224 0.10731456 0.17964011
    -#> feature_3 0.19530969 0.18583855 0.15529418
    -#> feature_4 0.09884473 0.16747797 0.04122594
    -#> feature_5 0.16433284 0.14519964 0.14909414
    -
    -p5 <- p3 %>% add_relative_targets(p4_targets)
    -# solve problem
    -# \dontrun{
    -# solve problem
    -s5 <- solve(p5)
    -
    -# plot solution (pixel values correspond to zone identifiers)
    -plot(category_layer(s5), main = c("varying targets"))
    -
    -# }
    -
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create base problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(verbose = FALSE)
    +
    +# create problem with 10% targets
    +p1 <- p %>% add_relative_targets(0.1)
    +
    +# create problem with varying targets for each feature
    +targets <- c(0.1, 0.2, 0.3, 0.4, 0.5)
    +p2 <- p %>% add_relative_targets(targets)
    +# \dontrun{
    +# solve problem
    +s <- stack(solve(p1), solve(p2))
    +
    +# plot solution
    +plot(s, main = c("10% targets", "varying targets"), axes = FALSE,
    +     box = FALSE)
    +
    +# }
    +# create a problem with multiple management zones
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create a problem with targets that specify an equal amount of each feature
    +# to be represented in each zone
    +p4_targets <- matrix(0.1, nrow = 5, ncol = 3,
    +                     dimnames = list(feature_names(sim_features_zones),
    +                                     zone_names(sim_features_zones)))
    +print(p4_targets)
    +#>           zone_1 zone_2 zone_3
    +#> feature_1    0.1    0.1    0.1
    +#> feature_2    0.1    0.1    0.1
    +#> feature_3    0.1    0.1    0.1
    +#> feature_4    0.1    0.1    0.1
    +#> feature_5    0.1    0.1    0.1
    +
    +p4 <- p3 %>% add_relative_targets(p4_targets)
    +
    +# solve problem
    +# \dontrun{
    +# solve problem
    +s4 <- solve(p4)
    +
    +# plot solution (pixel values correspond to zone identifiers)
    +plot(category_layer(s4), main = c("equal targets"))
    +
    +# }
    +# create a problem with targets that require a varying amount of each
    +# feature to be represented in each zone
    +p5_targets <- matrix(runif(15, 0.01, 0.2), nrow = 5, ncol = 3,
    +                     dimnames = list(feature_names(sim_features_zones),
    +                                     zone_names(sim_features_zones)))
    +print(p5_targets)
    +#>               zone_1     zone_2     zone_3
    +#> feature_1 0.16838399 0.04908221 0.06359158
    +#> feature_2 0.14775224 0.10731456 0.17964011
    +#> feature_3 0.19530969 0.18583855 0.15529418
    +#> feature_4 0.09884473 0.16747797 0.04122594
    +#> feature_5 0.16433284 0.14519964 0.14909414
    +
    +p5 <- p3 %>% add_relative_targets(p4_targets)
    +# solve problem
    +# \dontrun{
    +# solve problem
    +s5 <- solve(p5)
    +
    +# plot solution (pixel values correspond to zone identifiers)
    +plot(category_layer(s5), main = c("varying targets"))
    +
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/add_rsymphony_solver.html b/docs/reference/add_rsymphony_solver.html index c9d51a34c..e287c954d 100644 --- a/docs/reference/add_rsymphony_solver.html +++ b/docs/reference/add_rsymphony_solver.html @@ -1,105 +1,22 @@ - - - - - - - -Add a SYMPHONY solver with Rsymphony — add_rsymphony_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a SYMPHONY solver with Rsymphony — add_rsymphony_solver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Specify that the SYMPHONY +

    Specify that the SYMPHONY software (Ralphs & Güzelsoy 2005) -- using the Rsymphony package -- -should be used to solve a conservation planning problem(). +should be used to solve a conservation planning problem(). This function can also be used to customize the behavior of the solver. It requires the Rsymphony package to be installed.

    -
    add_rsymphony_solver(
    -  x,
    -  gap = 0.1,
    -  time_limit = .Machine$integer.max,
    -  first_feasible = FALSE,
    -  verbose = TRUE
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    gap

    numeric gap to optimality. This gap is relative +

    Usage,
    add_rsymphony_solver(
    +  x,
    +  gap = 0.1,
    +  time_limit = .Machine$integer.max,
    +  first_feasible = FALSE,
    +  verbose = TRUE
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    gap
    +

    numeric gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10% from optimality).

    time_limit

    numeric time limit (seconds) for generating solutions. +The default value is 0.1 (i.e., 10% from optimality).

    +
    time_limit
    +

    numeric time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. .Machine$integer.max), effectively meaning that solver -will keep running until a solution within the optimality gap is found.

    first_feasible

    logical should the first feasible solution be +(i.e., .Machine$integer.max), effectively meaning that solver +will keep running until a solution within the optimality gap is found.

    +
    first_feasible
    +

    logical should the first feasible solution be be returned? If first_feasible is set to TRUE, the solver will return the first solution it encounters that meets all the constraints, regardless of solution quality. Note that the first feasible solution is not an arbitrary solution, rather it is derived from the relaxed solution, and is therefore often reasonably close to optimality. -Defaults to FALSE.

    verbose

    logical should information be printed while solving -optimization problems? Defaults to TRUE.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the solver +Defaults to FALSE.

    +
    verbose
    +

    logical should information be printed while solving +optimization problems? Defaults to TRUE.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the solver added to it.

    -

    Details

    - -

    SYMPHONY is an +

    +
    +

    Details

    +

    SYMPHONY is an open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. @@ -259,68 +165,68 @@

    Details please see Schuster et al. (2020) for benchmarks comparing the run time and solution quality of different solvers when applied to different sized datasets.

    -

    References

    - +
    +
    +

    References

    Ralphs TK and Güzelsoy M (2005) The SYMPHONY callable library for mixed integer programming. In The Next Wave in Computing, Optimization, and Decision Technologies (pp. 61--76). Springer, Boston, MA.

    Schuster R, Hanson JO, Strimas-Mackey M, and Bennett JR (2020). Exact integer linear programming solvers outperform simulated annealing for solving conservation planning problems. PeerJ, 8: e9258.

    -

    See also

    - -

    See solvers for an overview of all functions for adding a solver.

    +
    +
    +

    See also

    +

    See solvers for an overview of all functions for adding a solver.

    Other solvers: -add_cbc_solver(), -add_cplex_solver(), -add_default_solver(), -add_gurobi_solver(), -add_lsymphony_solver

    +add_cbc_solver(), +add_cplex_solver(), +add_default_solver(), +add_gurobi_solver(), +add_lsymphony_solver

    +
    -

    Examples

    -
    # \dontrun{
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.1) %>%
    -     add_binary_decisions() %>%
    -     add_rsymphony_solver(time_limit = 10, verbose = FALSE)
    -
    -# generate solution
    -s <- solve(p)
    -
    -# plot solution
    -plot(s, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.1) %>%
    +     add_binary_decisions() %>%
    +     add_rsymphony_solver(time_limit = 10, verbose = FALSE)
    +
    +# generate solution
    +s <- solve(p)
    +
    +# plot solution
    +plot(s, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/add_semicontinuous_decisions.html b/docs/reference/add_semicontinuous_decisions.html index f4fe97a4b..aa57e929b 100644 --- a/docs/reference/add_semicontinuous_decisions.html +++ b/docs/reference/add_semicontinuous_decisions.html @@ -1,61 +1,5 @@ - - - - - - - -Add semi-continuous decisions — add_semicontinuous_decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add semi-continuous decisions — add_semicontinuous_decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Add a semi-continuous decision to a conservation planning -problem(). This is a relaxed decision where a part of a planning +problem(). This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the entire planning unit, which is -the default function (see add_binary_decisions()). -This decision is similar to the add_proportion_decisions() +the default function (see add_binary_decisions()). +This decision is similar to the add_proportion_decisions() function except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, an upper bound can be specified to ensure that at -most only a fraction (e.g. 80%) of a planning unit can be preserved. This +most only a fraction (e.g., 80%) of a planning unit can be preserved. This type of decision may be useful when it is not practical to conserve entire planning units.

    -
    add_semicontinuous_decisions(x, upper_limit)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    upper_limit

    numeric value specifying the maximum proportion -of a planning unit that can be reserved (e.g. set to 0.8 for 80%).

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the decisions added +

    Usage,
    add_semicontinuous_decisions(x, upper_limit)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    upper_limit
    +

    numeric value specifying the maximum proportion +of a planning unit that can be reserved (e.g., set to 0.8 for 80%).

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the decisions added to it.

    -

    Details

    - +
    +
    +

    Details

    Conservation planning problems involve making decisions on planning -units. These decisions are then associated with actions (e.g. turning a +units. These decisions are then associated with actions (e.g., turning a planning unit into a protected area). Only a single decision should be added to a ConservationProblem object. Note that if multiple decisions are added to a problem object, then the last one to be added will be used.

    -

    See also

    - -

    See decisions for an overview of all functions for adding decisions.

    +
    +
    +

    See also

    +

    See decisions for an overview of all functions for adding decisions.

    Other decisions: -add_binary_decisions(), -add_default_decisions(), -add_proportion_decisions()

    +add_binary_decisions(), +add_default_decisions(), +add_proportion_decisions()

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem with semi-continuous decisions
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_semicontinuous_decisions(0.5) %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# plot solutions
    -plot(s1, main = "solution")
    -
    -# }
    -# build multi-zone conservation problem with semi-continuous decisions
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_semicontinuous_decisions(0.5) %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print solution
    -print(s2)
    -#> class      : RasterStack 
    -#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> names      : zone_1, zone_2, zone_3 
    -#> min values :      0,      0,      0 
    -#> max values :    0.5,    0.5,    0.5 
    -#> 
    -
    -# plot solution
    -# panels show the proportion of each planning unit allocated to each zone
    -plot(s2, axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem with semi-continuous decisions
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_semicontinuous_decisions(0.5) %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# plot solutions
    +plot(s1, main = "solution")
    +
    +# }
    +# build multi-zone conservation problem with semi-continuous decisions
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_semicontinuous_decisions(0.5) %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print solution
    +print(s2)
    +#> class      : RasterStack 
    +#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> names      : zone_1, zone_2, zone_3 
    +#> min values :      0,      0,      0 
    +#> max values :    0.5,    0.5,    0.5 
    +#> 
    +
    +# plot solution
    +# panels show the proportion of each planning unit allocated to each zone
    +plot(s2, axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/add_shuffle_portfolio.html b/docs/reference/add_shuffle_portfolio.html index 133df4c02..533a04abc 100644 --- a/docs/reference/add_shuffle_portfolio.html +++ b/docs/reference/add_shuffle_portfolio.html @@ -1,105 +1,22 @@ - - - - - - - -Add a shuffle portfolio — add_shuffle_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a shuffle portfolio — add_shuffle_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Generate a portfolio of solutions for a conservation planning -problem() by randomly reordering the data prior to +problem() by randomly reordering the data prior to solving the problem. This is recommended as a replacement for -add_top_portfolio() when the Gurobi software is not +add_top_portfolio() when the Gurobi software is not available.

    -
    add_shuffle_portfolio(
    -  x,
    -  number_solutions = 10L,
    -  threads = 1L,
    -  remove_duplicates = TRUE
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    number_solutions

    integer number of attempts to generate -different solutions. Defaults to 10.

    threads

    integer number of threads to use for the generating -the solution portfolio. Defaults to 1.

    remove_duplicates

    logical should duplicate solutions -be removed? Defaults to TRUE.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the portfolio +

    Usage,
    add_shuffle_portfolio(
    +  x,
    +  number_solutions = 10L,
    +  threads = 1L,
    +  remove_duplicates = TRUE
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    number_solutions
    +

    integer number of attempts to generate +different solutions. Defaults to 10.

    +
    threads
    +

    integer number of threads to use for the generating +the solution portfolio. Defaults to 1.

    +
    remove_duplicates
    +

    logical should duplicate solutions +be removed? Defaults to TRUE.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the portfolio added to it.

    -

    Details

    - +
    +
    +

    Details

    This strategy for generating a portfolio of solutions often results in different solutions, depending on optimality gap, but may return duplicate solutions. In general, this strategy is most effective when problems are quick to solve and multiple threads are available for solving each problem separately.

    -

    See also

    - -

    See portfolios for an overview of all functions for adding a portfolio.

    +
    +
    +

    See also

    +

    See portfolios for an overview of all functions for adding a portfolio.

    Other portfolios: -add_cuts_portfolio(), -add_extra_portfolio(), -add_gap_portfolio(), -add_top_portfolio()

    +add_cuts_portfolio(), +add_extra_portfolio(), +add_gap_portfolio(), +add_top_portfolio()

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem with shuffle portfolio
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_shuffle_portfolio(10, remove_duplicates = FALSE) %>%
    -      add_default_solver(gap = 0.2, verbose = FALSE)
    -
    -# \dontrun{
    -# solve problem and generate 10 solutions within 20% of optimality
    -s1 <- solve(p1)
    -
    -# plot solutions in portfolio
    -plot(stack(s1), axes = FALSE, box = FALSE)
    -
    -# }
    -# build multi-zone conservation problem with shuffle portfolio
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_shuffle_portfolio(10, remove_duplicates = FALSE) %>%
    -      add_default_solver(gap = 0.2, verbose = FALSE)
    -
    -# \dontrun{
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print solution
    -str(s2, max.level = 1)
    -#> List of 10
    -#>  $ solution_1 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_2 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_3 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_4 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_5 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_6 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_7 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_8 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_9 :Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  $ solution_10:Formal class 'RasterStack' [package "raster"] with 11 slots
    -#>  - attr(*, "objective")= Named num [1:10] 11667 11408 11268 11080 11752 ...
    -#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    -#>  - attr(*, "status")= Named chr [1:10] "OPTIMAL" "OPTIMAL" "OPTIMAL" "OPTIMAL" ...
    -#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    -#>  - attr(*, "runtime")= Named num [1:10] 0.027 0.006 0.005 0.005 0.005 ...
    -#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    -
    -# plot solutions in portfolio
    -plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem with shuffle portfolio
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_shuffle_portfolio(10, remove_duplicates = FALSE) %>%
    +      add_default_solver(gap = 0.2, verbose = FALSE)
    +
    +# \dontrun{
    +# solve problem and generate 10 solutions within 20% of optimality
    +s1 <- solve(p1)
    +
    +# plot solutions in portfolio
    +plot(stack(s1), axes = FALSE, box = FALSE)
    +
    +# }
    +# build multi-zone conservation problem with shuffle portfolio
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_shuffle_portfolio(10, remove_duplicates = FALSE) %>%
    +      add_default_solver(gap = 0.2, verbose = FALSE)
    +
    +# \dontrun{
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print solution
    +str(s2, max.level = 1)
    +#> List of 10
    +#>  $ solution_1 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_2 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_3 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_4 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_5 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_6 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_7 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_8 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_9 :Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  $ solution_10:Formal class 'RasterStack' [package "raster"] with 11 slots
    +#>  - attr(*, "objective")= Named num [1:10] 11667 11408 11268 11080 11752 ...
    +#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    +#>  - attr(*, "status")= Named chr [1:10] "OPTIMAL" "OPTIMAL" "OPTIMAL" "OPTIMAL" ...
    +#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    +#>  - attr(*, "runtime")= Named num [1:10] 0.006 0.024 0.006 0.023 0.007 ...
    +#>   ..- attr(*, "names")= chr [1:10] "solution_1" "solution_2" "solution_3" "solution_4" ...
    +
    +# plot solutions in portfolio
    +plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/add_top_portfolio.html b/docs/reference/add_top_portfolio.html index 63f7f9c2c..bd382d78b 100644 --- a/docs/reference/add_top_portfolio.html +++ b/docs/reference/add_top_portfolio.html @@ -1,103 +1,20 @@ - - - - - - - -Add a top portfolio — add_top_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a top portfolio — add_top_portfolio • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Generate a portfolio of solutions for a conservation planning -problem() by finding a pre-specified number of solutions that +problem() by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions).

    -
    add_top_portfolio(x, number_solutions)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    number_solutions

    integer number of solutions required.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the portfolio +

    Usage,
    add_top_portfolio(x, number_solutions)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    number_solutions
    +

    integer number of solutions required.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the portfolio added to it.

    -

    Details

    - +
    +
    +

    Details

    This strategy for generating a portfolio requires problems to -be solved using the Gurobi software suite (i.e. using -add_gurobi_solver(). Specifically, version 9.0.0 (or greater) +be solved using the Gurobi software suite (i.e., using +add_gurobi_solver(). Specifically, version 9.0.0 (or greater) of the gurobi package must be installed. Note that the number of solutions returned may be less than the argument to number_solutions, if the total number of feasible solutions is less than the number of solutions requested.

    -

    See also

    - -

    See portfolios for an overview of all functions for adding a portfolio.

    +
    +
    +

    See also

    +

    See portfolios for an overview of all functions for adding a portfolio.

    Other portfolios: -add_cuts_portfolio(), -add_extra_portfolio(), -add_gap_portfolio(), -add_shuffle_portfolio()

    +add_cuts_portfolio(), +add_extra_portfolio(), +add_gap_portfolio(), +add_shuffle_portfolio()

    +
    -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(600)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create minimal problem with a portfolio for the top 5 solutions
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.05) %>%
    -      add_top_portfolio(number_solutions = 5) %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem and generate portfolio
    -s1 <- solve(p1)
    -
    -# print number of solutions found
    -print(length(s1))
    -#> [1] 5
    -
    -# plot solutions
    -plot(stack(s1), axes = FALSE, box = FALSE)
    -
    -
    -# create multi-zone problem with a portfolio for the top 5 solutions
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_top_portfolio(number_solutions = 5) %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem and generate portfolio
    -s2 <- solve(p2)
    -
    -# print number of solutions found
    -print(length(s2))
    -#> [1] 5
    -
    -# plot solutions in portfolio
    -plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(600)
    +
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create minimal problem with a portfolio for the top 5 solutions
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.05) %>%
    +      add_top_portfolio(number_solutions = 5) %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem and generate portfolio
    +s1 <- solve(p1)
    +
    +# print number of solutions found
    +print(length(s1))
    +#> [1] 5
    +
    +# plot solutions
    +plot(stack(s1), axes = FALSE, box = FALSE)
    +
    +
    +# create multi-zone problem with a portfolio for the top 5 solutions
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_top_portfolio(number_solutions = 5) %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem and generate portfolio
    +s2 <- solve(p2)
    +
    +# print number of solutions found
    +print(length(s2))
    +#> [1] 5
    +
    +# plot solutions in portfolio
    +plot(stack(lapply(s2, category_layer)), main = "solution", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/adjacency_matrix.html b/docs/reference/adjacency_matrix.html index 6314fc812..4391ce6e8 100644 --- a/docs/reference/adjacency_matrix.html +++ b/docs/reference/adjacency_matrix.html @@ -1,103 +1,20 @@ - - - - - - - -Adjacency matrix — adjacency_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Adjacency matrix — adjacency_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -188,146 +105,137 @@

    Adjacency matrix

    with each other too.

    -
    adjacency_matrix(x, ...)
    +    
    Usage,
    adjacency_matrix(x, ...)
     
    -# S3 method for Raster
    -adjacency_matrix(x, directions = 4L, ...)
    +# S3 method for Raster
    +adjacency_matrix(x, directions = 4L, ...)
     
    -# S3 method for SpatialPolygons
    -adjacency_matrix(x, ...)
    +# S3 method for SpatialPolygons
    +adjacency_matrix(x, ...)
     
    -# S3 method for SpatialLines
    -adjacency_matrix(x, ...)
    +# S3 method for SpatialLines
    +adjacency_matrix(x, ...)
     
    -# S3 method for SpatialPoints
    -adjacency_matrix(x, ...)
    +# S3 method for SpatialPoints
    +adjacency_matrix(x, ...)
     
    -# S3 method for sf
    -adjacency_matrix(x, ...)
    +# S3 method for sf
    +adjacency_matrix(x, ...)
     
    -# S3 method for default
    -adjacency_matrix(x, ...)
    +# S3 method for default +adjacency_matrix(x, ...)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    Raster, -SpatialPolygons, -SpatialLines, -or sf::sf() object -representing planning units.

    ...

    not used.

    directions

    integer If x is a -Raster object, the number of directions +

    +

    Arguments

    +
    x
    +

    Raster, +SpatialPolygons, +SpatialLines, +or sf::sf() object +representing planning units.

    +
    ...
    +

    not used.

    +
    directions
    +

    integer If x is a +Raster object, the number of directions in which cells should be considered adjacent: 4 (rook's case), 8 (queen's case), 16 (knight and one-cell queen moves), or "bishop" to for cells -with one-cell diagonal moves.

    - -

    Value

    - -

    dsCMatrix sparse symmetric matrix. +with one-cell diagonal moves.

    +
    +
    +

    Value

    +

    dsCMatrix sparse symmetric matrix. Each row and column represents a planning unit. Cells values indicate if different planning units are adjacent to each other or not (using ones and zeros). To reduce computational burden, cells among the matrix diagonal are set to zero. Furthermore, if the argument to x is a -Raster object, then cells with NA -values are set to zero too.

    -

    Details

    - +Raster object, then cells with NAvalues are set to zero too.

    +
    +
    +

    Details

    Spatial processing is completed using -sf::st_intersects() for Spatial and -sf::sf() objects, -and raster::adjacent() for Raster +sf::st_intersects() for Spatial and +sf::sf() objects, +and raster::adjacent() for Raster objects.

    -

    Notes

    - +
    +
    +

    Notes

    In earlier versions (< 5.0.0), this function was named as the connected_matrix function. It has been renamed to be consistent with other spatial association matrix functions.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_sf, sim_pu_lines)
    -
    -# create adjacency matrix using raster data
    -## crop raster to 9 cells
    -r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    -
    -## make adjacency matrix
    -am_raster <- adjacency_matrix(r)
    -
    -# create adjacency matrix using polygons (sf) data
    -## subset 9 polygons
    -ply <- sim_pu_sf[c(1:2, 10:12, 20:22), ]
    -
    -## make adjacency matrix
    -am_ply <- adjacency_matrix(ply)
    -
    -# create adjacency matrix using lines (Spatial) data
    -## subset 9 lines
    -lns <- sim_pu_lines[c(1:2, 10:12, 20:22), ]
    -
    -## make adjacency matrix
    -am_lns <- adjacency_matrix(lns)
    -
    -# plot data and the adjacency matrices
    -# \dontrun{
    -par(mfrow = c(4,2))
    -
    -## plot raster and adjacency matrix
    -plot(r, main = "raster", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(am_raster)), main = "adjacency matrix", axes = FALSE,
    -     box = FALSE)
    -
    -## plot polygons (sf) and adjacency matrix
    -plot(r, main = "polygons (sf)", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(am_ply)), main = "adjacency matrix", axes = FALSE,
    -    box = FALSE)
    -
    -## plot lines (Spatial) and adjacency matrix
    -plot(r, main = "lines (Spatial)", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(am_lns)), main = "adjacency matrix", axes = FALSE,
    -     box = FALSE)
    -# }
    -
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_sf, sim_pu_lines)
    +
    +# create adjacency matrix using raster data
    +## crop raster to 9 cells
    +r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    +
    +## make adjacency matrix
    +am_raster <- adjacency_matrix(r)
    +
    +# create adjacency matrix using polygons (sf) data
    +## subset 9 polygons
    +ply <- sim_pu_sf[c(1:2, 10:12, 20:22), ]
    +
    +## make adjacency matrix
    +am_ply <- adjacency_matrix(ply)
    +
    +# create adjacency matrix using lines (Spatial) data
    +## subset 9 lines
    +lns <- sim_pu_lines[c(1:2, 10:12, 20:22), ]
    +
    +## make adjacency matrix
    +am_lns <- adjacency_matrix(lns)
    +
    +# plot data and the adjacency matrices
    +# \dontrun{
    +par(mfrow = c(4,2))
    +
    +## plot raster and adjacency matrix
    +plot(r, main = "raster", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(am_raster)), main = "adjacency matrix", axes = FALSE,
    +     box = FALSE)
    +
    +## plot polygons (sf) and adjacency matrix
    +plot(r, main = "polygons (sf)", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(am_ply)), main = "adjacency matrix", axes = FALSE,
    +    box = FALSE)
    +
    +## plot lines (Spatial) and adjacency matrix
    +plot(r, main = "lines (Spatial)", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(am_lns)), main = "adjacency matrix", axes = FALSE,
    +     box = FALSE)
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/array_parameters.html b/docs/reference/array_parameters.html index 18bfc5fe5..472060104 100644 --- a/docs/reference/array_parameters.html +++ b/docs/reference/array_parameters.html @@ -1,102 +1,19 @@ - - - - - - - -Array parameters — array_parameters • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Array parameters — array_parameters • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,202 +103,193 @@

    Array parameters

    to create a parameter with conflicting settings then an error will be thrown.

    -
    proportion_parameter_array(name, value, label)
    -
    -binary_parameter_array(name, value, label)
    -
    -integer_parameter_array(
    -  name,
    -  value,
    -  label,
    -  lower_limit = rep(as.integer(-.Machine$integer.max), length(value)),
    -  upper_limit = rep(as.integer(.Machine$integer.max), length(value))
    -)
    -
    -numeric_parameter_array(
    -  name,
    -  value,
    -  label,
    -  lower_limit = rep(.Machine$double.xmin, length(value)),
    -  upper_limit = rep(.Machine$double.xmax, length(value))
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    name

    character name of parameter.

    value

    numeric vector of values.

    label

    character vector of labels for each value.

    lower_limit

    numeric vector of values denoting the minimum acceptable +

    Usage,
    proportion_parameter_array(name, value, label)
    +
    +binary_parameter_array(name, value, label)
    +
    +integer_parameter_array(
    +  name,
    +  value,
    +  label,
    +  lower_limit = rep(as.integer(-.Machine$integer.max), length(value)),
    +  upper_limit = rep(as.integer(.Machine$integer.max), length(value))
    +)
    +
    +numeric_parameter_array(
    +  name,
    +  value,
    +  label,
    +  lower_limit = rep(.Machine$double.xmin, length(value)),
    +  upper_limit = rep(.Machine$double.xmax, length(value))
    +)
    + +
    +

    Arguments

    +
    name
    +

    character name of parameter.

    +
    value
    +

    numeric vector of values.

    +
    label
    +

    character vector of labels for each value.

    +
    lower_limit
    +

    numeric vector of values denoting the minimum acceptable value for each element in value. Defaults to the -smallest possible number on the system.

    upper_limit

    numeric vector of values denoting the maximum acceptable +smallest possible number on the system.

    +
    upper_limit
    +

    numeric vector of values denoting the maximum acceptable value for each element in value. Defaults to the -largest possible number on the system.

    - -

    Value

    - -

    ArrayParameter object.

    -

    Details

    - +largest possible number on the system.

    +
    +
    +

    Value

    +

    ArrayParameter object.

    +
    +
    +

    Details

    Below is a list of parameter generating functions and a brief -description of each.

    - -
    proportion_parameter_array

    a parameter that consists of multiple +description of each.

    proportion_parameter_array
    +

    a parameter that consists of multiple numeric values that are between zero and one.

    -
    binary_parameter_array

    a parameter that consists of multiple + +

    binary_parameter_array
    +

    a parameter that consists of multiple integer values that are either zero or one.

    -
    integer_parameter_array

    a parameter that consists of multiple + +

    integer_parameter_array
    +

    a parameter that consists of multiple integer values.

    -
    numeric_parameter_array

    a parameter that consists of multiple -numeric values.

    +
    numeric_parameter_array
    +

    a parameter that consists of multiple +numeric values.

    -
    -

    Examples

    -
    # proportion parameter array
    -p1 <- proportion_parameter_array('prop_array', c(0.1, 0.2, 0.3),
    -                                 letters[1:3])
    -print(p1) # print it
    -#> prop_array (min: 0.1, max: 0.3)
    -p1$get() # get value
    -#>   value
    -#> a   0.1
    -#> b   0.2
    -#> c   0.3
    -p1$id # get id
    -#> id: 79916be9-839c-4864-be8d-bc16e9226395
    -invalid <- data.frame(value = 1:3, row.names=letters[1:3]) # invalid values
    -p1$validate(invalid) # check invalid input is invalid
    -valid <- data.frame(value = c(0.4, 0.5, 0.6), row.names=letters[1:3]) # valid
    -p1$validate(valid) # check valid input is valid
    -p1$set(valid) # change value to valid input
    -print(p1)
    -#> prop_array (min: 0.4, max: 0.6)
    -
    -# binary parameter array
    -p2 <- binary_parameter_array('bin_array', c(0L, 1L, 0L), letters[1:3])
    -print(p2) # print it
    -#> bin_array (min: 0, max: 1)
    -p2$get() # get value
    -#>   value
    -#> a     0
    -#> b     1
    -#> c     0
    -p2$id # get id
    -#> id: 955a2518-aa58-46c9-8dd0-ca5054f436e3
    -invalid <- data.frame(value = 1:3, row.names=letters[1:3]) # invalid values
    -p2$validate(invalid) # check invalid input is invalid
    -valid <- data.frame(value = c(0L, 0L, 0L), row.names=letters[1:3]) # valid
    -p2$validate(valid) # check valid input is valid
    -p2$set(valid) # change value to valid input
    -print(p2)
    -#> bin_array (min: 0, max: 0)
    -
    -# integer parameter array
    -p3 <- integer_parameter_array('int_array', c(1:3), letters[1:3])
    -print(p3) # print it
    -#> int_array (min: 1, max: 3)
    -p3$get() # get value
    -#>   value
    -#> a     1
    -#> b     2
    -#> c     3
    -p3$id # get id
    -#> id: db018677-fca9-46df-b1fd-33a3d023bc9e
    -invalid <- data.frame(value = rnorm(3), row.names=letters[1:3]) # invalid
    -p3$validate(invalid) # check invalid input is invalid
    -valid <- data.frame(value = 5:7, row.names=letters[1:3]) # valid
    -p3$validate(valid) # check valid input is valid
    -p3$set(valid) # change value to valid input
    -print(p3)
    -#> int_array (min: 5, max: 7)
    -
    -# numeric parameter array
    -p4 <- numeric_parameter_array('dbl_array', c(0.1, 4, -5), letters[1:3])
    -print(p4) # print it
    -#> dbl_array (min: -5, max: 4)
    -p4$get() # get value
    -#>   value
    -#> a   0.1
    -#> b   4.0
    -#> c  -5.0
    -p4$id # get id
    -#> id: d199e63a-36cf-47d9-8a05-b254f498e7d0
    -invalid <- data.frame(value = c(NA, 1, 2), row.names=letters[1:3]) # invalid
    -p4$validate(invalid) # check invalid input is invalid
    -valid <- data.frame(value = c(1, 2, 3), row.names=letters[1:3]) # valid
    -p4$validate(valid) # check valid input is valid
    -p4$set(valid) # change value to valid input
    -print(p4)
    -#> dbl_array (min: 1, max: 3)
    -
    -# numeric parameter array with lower bounds
    -p5 <- numeric_parameter_array('b_dbl_array', c(0.1, 4, -5), letters[1:3],
    -                              lower_limit=c(0, 1, 2))
    -print(p5) # print it
    -#> b_dbl_array (min: -5, max: 4)
    -p5$get() # get value
    -#>   value
    -#> a   0.1
    -#> b   4.0
    -#> c  -5.0
    -p5$id# get id
    -#> id: d2529a02-5134-4b9c-bc60-c29c27cf4f3f
    -invalid <- data.frame(value = c(-1, 5, 5), row.names=letters[1:3]) # invalid
    -p5$validate(invalid) # check invalid input is invalid
    -valid <- data.frame(value = c(0, 1, 2), row.names=letters[1:3]) # valid
    -p5$validate(valid) # check valid input is valid
    -p5$set(valid) # change value to valid input
    -print(p5)
    -#> b_dbl_array (min: 0, max: 2)
    -
    +
    + +
    +

    Examples

    +
    # proportion parameter array
    +p1 <- proportion_parameter_array('prop_array', c(0.1, 0.2, 0.3),
    +                                 letters[1:3])
    +print(p1) # print it
    +#> prop_array (min: 0.1, max: 0.3)
    +p1$get() # get value
    +#>   value
    +#> a   0.1
    +#> b   0.2
    +#> c   0.3
    +p1$id # get id
    +#> id: d2bca11d-4367-4c50-93a1-46041bff7f53
    +invalid <- data.frame(value = 1:3, row.names=letters[1:3]) # invalid values
    +p1$validate(invalid) # check invalid input is invalid
    +valid <- data.frame(value = c(0.4, 0.5, 0.6), row.names=letters[1:3]) # valid
    +p1$validate(valid) # check valid input is valid
    +p1$set(valid) # change value to valid input
    +print(p1)
    +#> prop_array (min: 0.4, max: 0.6)
    +
    +# binary parameter array
    +p2 <- binary_parameter_array('bin_array', c(0L, 1L, 0L), letters[1:3])
    +print(p2) # print it
    +#> bin_array (min: 0, max: 1)
    +p2$get() # get value
    +#>   value
    +#> a     0
    +#> b     1
    +#> c     0
    +p2$id # get id
    +#> id: 206b0969-b11c-4f49-b06e-760674882b5e
    +invalid <- data.frame(value = 1:3, row.names=letters[1:3]) # invalid values
    +p2$validate(invalid) # check invalid input is invalid
    +valid <- data.frame(value = c(0L, 0L, 0L), row.names=letters[1:3]) # valid
    +p2$validate(valid) # check valid input is valid
    +p2$set(valid) # change value to valid input
    +print(p2)
    +#> bin_array (min: 0, max: 0)
    +
    +# integer parameter array
    +p3 <- integer_parameter_array('int_array', c(1:3), letters[1:3])
    +print(p3) # print it
    +#> int_array (min: 1, max: 3)
    +p3$get() # get value
    +#>   value
    +#> a     1
    +#> b     2
    +#> c     3
    +p3$id # get id
    +#> id: fb5f4dd4-0caa-46a6-a5d3-3994cd2718b7
    +invalid <- data.frame(value = rnorm(3), row.names=letters[1:3]) # invalid
    +p3$validate(invalid) # check invalid input is invalid
    +valid <- data.frame(value = 5:7, row.names=letters[1:3]) # valid
    +p3$validate(valid) # check valid input is valid
    +p3$set(valid) # change value to valid input
    +print(p3)
    +#> int_array (min: 5, max: 7)
    +
    +# numeric parameter array
    +p4 <- numeric_parameter_array('dbl_array', c(0.1, 4, -5), letters[1:3])
    +print(p4) # print it
    +#> dbl_array (min: -5, max: 4)
    +p4$get() # get value
    +#>   value
    +#> a   0.1
    +#> b   4.0
    +#> c  -5.0
    +p4$id # get id
    +#> id: d0009519-648d-4aad-89e5-4b0f86aaffee
    +invalid <- data.frame(value = c(NA, 1, 2), row.names=letters[1:3]) # invalid
    +p4$validate(invalid) # check invalid input is invalid
    +valid <- data.frame(value = c(1, 2, 3), row.names=letters[1:3]) # valid
    +p4$validate(valid) # check valid input is valid
    +p4$set(valid) # change value to valid input
    +print(p4)
    +#> dbl_array (min: 1, max: 3)
    +
    +# numeric parameter array with lower bounds
    +p5 <- numeric_parameter_array('b_dbl_array', c(0.1, 4, -5), letters[1:3],
    +                              lower_limit=c(0, 1, 2))
    +print(p5) # print it
    +#> b_dbl_array (min: -5, max: 4)
    +p5$get() # get value
    +#>   value
    +#> a   0.1
    +#> b   4.0
    +#> c  -5.0
    +p5$id# get id
    +#> id: 274883ce-0d40-485f-89b8-f2fc2f433e5e
    +invalid <- data.frame(value = c(-1, 5, 5), row.names=letters[1:3]) # invalid
    +p5$validate(invalid) # check invalid input is invalid
    +valid <- data.frame(value = c(0, 1, 2), row.names=letters[1:3]) # valid
    +p5$validate(valid) # check valid input is valid
    +p5$set(valid) # change value to valid input
    +print(p5)
    +#> b_dbl_array (min: 0, max: 2)
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/as.html b/docs/reference/as.html index 88fc43b03..b66e788d2 100644 --- a/docs/reference/as.html +++ b/docs/reference/as.html @@ -1,101 +1,18 @@ - - - - - - - -Coerce object to another object — as.Id • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Coerce object to another object — as.Id • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,58 +101,48 @@

    Coerce object to another object

    Coerce an object.

    -
    as.Id(x, ...)
    -
    -# S3 method for character
    -as.Id(x, ...)
    +    
    Usage,
    as.Id(x, ...)
     
    -# S3 method for Parameters
    -as.list(x, ...)
    +# S3 method for character
    +as.Id(x, ...)
     
    -# S3 method for Zones
    -as.list(x, ...)
    +# S3 method for Parameters +as.list(x, ...) -

    Arguments

    - - - - - - - - - - -
    x

    Object.

    ...

    unused arguments.

    - -

    Value

    +# S3 method for Zones +as.list(x, ...)
    +
    +

    Arguments

    +
    x
    +

    Object.

    +
    ...
    +

    unused arguments.

    +
    +
    +

    Value

    An object.

    +
    + - - - - - - - - - - - + diff --git a/docs/reference/as.list.html b/docs/reference/as.list.html index f750d9c6d..282890a0a 100644 --- a/docs/reference/as.list.html +++ b/docs/reference/as.list.html @@ -1,101 +1,18 @@ - - - - - - - -Convert OptimizationProblem to list — as.list.OptimizationProblem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Convert OptimizationProblem to list — as.list.OptimizationProblem • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,50 +101,40 @@

    Convert OptimizationProblem to list

    Convert OptimizationProblem to list

    -
    # S3 method for OptimizationProblem
    -as.list(x, ...)
    - -

    Arguments

    - - - - - - - - - - -
    x

    OptimizationProblem object.

    ...

    not used.

    - -

    Value

    - -

    list() object.

    +
    Usage,
    # S3 method for OptimizationProblem
    +as.list(x, ...)
    + +
    +

    Arguments

    +
    x
    +

    OptimizationProblem object.

    +
    ...
    +

    not used.

    +
    +
    +

    Value

    +

    list() object.

    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/binary_stack.html b/docs/reference/binary_stack.html index 6dd03f2bd..d92cf65c8 100644 --- a/docs/reference/binary_stack.html +++ b/docs/reference/binary_stack.html @@ -1,104 +1,21 @@ - - - - - - - -Binary stack — binary_stack • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Binary stack — binary_stack • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Convert a RasterLayer object containing -categorical identifiers into a RasterStack +

    Convert a RasterLayer object containing +categorical identifiers into a RasterStack object where each layer corresponds to a different identifier and values indicate the presence/absence of that category in the input object.

    -
    binary_stack(x)
    - -

    Arguments

    - - - - - - -
    x

    Raster object containing a single layer.

    - -

    Value

    - -

    RasterStack object.

    -

    Details

    +
    Usage,
    binary_stack(x)
    +
    +

    Arguments

    +
    x
    +

    Raster object containing a single layer.

    +
    +
    +

    Value

    +

    RasterStack object.

    +
    +
    +

    Details

    This function is provided to help manage data that encompass multiple management zones. For instance, this function may be helpful -for preparing raster data for add_locked_in_constraints() and -add_locked_out_constraints() since they require binary -RasterStack objects as input arguments.

    -

    See also

    - - +for preparing raster data for add_locked_in_constraints() and +add_locked_out_constraints() since they require binary +RasterStack objects as input arguments.

    +
    +
    +

    See also

    + +
    -

    Examples

    -
    # create raster with categorical identifers
    -x <- raster(matrix(c(1, 2, 3, 1, NA, 1), nrow = 3))
    -
    -# convert to binary stack
    -y <- binary_stack(x)
    -
    -# plot categorical raster and binary stack representation
    -# \dontrun{
    -plot(stack(x, y), main = c("x", "y[[1]]", "y[[2]]", "y[[3]]"), nr = 1)
    -
    -# }
    +    
    +

    Examples

    +
    # create raster with categorical identifers
    +x <- raster(matrix(c(1, 2, 3, 1, NA, 1), nrow = 3))
    +
    +# convert to binary stack
    +y <- binary_stack(x)
    +
    +# plot categorical raster and binary stack representation
    +# \dontrun{
    +plot(stack(x, y), main = c("x", "y[[1]]", "y[[2]]", "y[[3]]"), nr = 1)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/boundary_matrix.html b/docs/reference/boundary_matrix.html index d514677cb..9a8c06c99 100644 --- a/docs/reference/boundary_matrix.html +++ b/docs/reference/boundary_matrix.html @@ -1,103 +1,20 @@ - - - - - - - -Boundary matrix — boundary_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Boundary matrix — boundary_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -188,144 +105,137 @@

    Boundary matrix

    planning unit exhibits.

    -
    boundary_matrix(x, str_tree)
    +    
    Usage,
    boundary_matrix(x, str_tree)
     
    -# S3 method for Raster
    -boundary_matrix(x, str_tree = FALSE)
    +# S3 method for Raster
    +boundary_matrix(x, str_tree = FALSE)
     
    -# S3 method for SpatialPolygons
    -boundary_matrix(x, str_tree = FALSE)
    +# S3 method for SpatialPolygons
    +boundary_matrix(x, str_tree = FALSE)
     
    -# S3 method for SpatialLines
    -boundary_matrix(x, str_tree = FALSE)
    +# S3 method for SpatialLines
    +boundary_matrix(x, str_tree = FALSE)
     
    -# S3 method for SpatialPoints
    -boundary_matrix(x, str_tree = FALSE)
    +# S3 method for SpatialPoints
    +boundary_matrix(x, str_tree = FALSE)
     
    -# S3 method for sf
    -boundary_matrix(x, str_tree = FALSE)
    +# S3 method for sf
    +boundary_matrix(x, str_tree = FALSE)
     
    -# S3 method for default
    -boundary_matrix(x, str_tree = FALSE)
    +# S3 method for default +boundary_matrix(x, str_tree = FALSE)
    -

    Arguments

    - - - - - - - - - - -
    x

    Raster, -SpatialLines, -SpatialPolygons, -sf::sf() object representing planning units. If x is a -Raster object then it must have only one -layer.

    str_tree

    logical should a GEOS STRtree structure be used to +

    +

    Arguments

    +
    x
    +

    Raster, +SpatialLines, +SpatialPolygons, +sf::sf() object representing planning units. If x is a +Raster object then it must have only one +layer.

    +
    str_tree
    +

    logical should a GEOS STRtree structure be used to to pre-process data? If TRUE, then the experimental -rgeos::gUnarySTRtreeQuery() function +rgeos::gUnarySTRtreeQuery() function will be used to pre-compute which planning units are adjacent to each other and potentially reduce the processing time required to generate the boundary matrices. This argument is only used when -the planning unit data are vector-based polygons (i.e. -sp::SpatialPolygonsDataFrame() objects). Note that +the planning unit data are vector-based polygons (i.e., +sp::SpatialPolygonsDataFrame() objects). Note that using TRUE may crash Mac OSX systems. The default argument -is FALSE.

    - -

    Value

    - -

    dsCMatrix symmetric sparse matrix object. +is FALSE.

    +
    +
    +

    Value

    +

    dsCMatrix symmetric sparse matrix object. Each row and column represents a planning unit. Cells values indicate the shared boundary length between different pairs of planning units.

    -

    Details

    - -

    This function returns a dsCMatrix +

    +
    +

    Details

    +

    This function returns a dsCMatrix symmetric sparse matrix. Cells on the off-diagonal indicate the length of the shared boundary between two different planning units. Cells on the diagonal indicate length of a given planning unit's edges that have no -neighbors (e.g. for edges of planning units found along the +neighbors (e.g., for edges of planning units found along the coastline). This function assumes the data are in a coordinate system where Euclidean distances accurately describe the proximity between two points on the earth. Thus spatial data in a longitude/latitude -coordinate system (i.e. -WGS84) +coordinate system (i.e., +WGS84) should be reprojected to another coordinate system before using this -function. Note that for Raster objects +function. Note that for Raster objects boundaries are missing for cells that have NA values in all cells.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_polygons)
    -
    -# subset data to reduce processing time
    -r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    -ply <- sim_pu_polygons[c(1:2, 10:12, 20:22), ]
    -ply2 <- st_as_sf(ply)
    -
    -# create boundary matrix using raster data
    -bm_raster <- boundary_matrix(r)
    -
    -# create boundary matrix using polygon (Spatial) data
    -bm_ply1 <- boundary_matrix(ply)
    -
    -# create boundary matrix using polygon (sf) data
    -bm_ply2 <- boundary_matrix(ply2)
    -
    -# create boundary matrix with polygon (Spatial) data and GEOS STR query trees
    -# to speed up processing
    -bm_ply3 <- boundary_matrix(ply, TRUE)
    -
    -# plot raster and boundary matrix
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(r, main = "raster", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(bm_raster)), main = "boundary matrix",
    -     axes = FALSE, box = FALSE)
    -
    -# }
    -# plot polygons and boundary matrices
    -# \dontrun{
    -par(mfrow = c(1, 3))
    -plot(r, main = "polygons (Spatial)", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(bm_ply1)), main = "boundary matrix", axes = FALSE,
    -     box = FALSE)
    -plot(r, main = "polygons (sf)", axes = FALSE, box = FALSE)
    -
    -plot(raster(as.matrix(bm_ply2)), main = "boundary matrix", axes = FALSE,
    -     box = FALSE)
    -plot(raster(as.matrix(bm_ply3)), main = "boundary matrix (Spatial, STR)",
    -            axes = FALSE, box = FALSE)
    -# }
    -
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_polygons)
    +
    +# subset data to reduce processing time
    +r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    +ply <- sim_pu_polygons[c(1:2, 10:12, 20:22), ]
    +ply2 <- st_as_sf(ply)
    +
    +# create boundary matrix using raster data
    +bm_raster <- boundary_matrix(r)
    +
    +# create boundary matrix using polygon (Spatial) data
    +bm_ply1 <- boundary_matrix(ply)
    +
    +# create boundary matrix using polygon (sf) data
    +bm_ply2 <- boundary_matrix(ply2)
    +
    +# create boundary matrix with polygon (Spatial) data and GEOS STR query trees
    +# to speed up processing
    +bm_ply3 <- boundary_matrix(ply, TRUE)
    +
    +# plot raster and boundary matrix
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(r, main = "raster", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(bm_raster)), main = "boundary matrix",
    +     axes = FALSE, box = FALSE)
    +
    +# }
    +# plot polygons and boundary matrices
    +# \dontrun{
    +par(mfrow = c(1, 3))
    +plot(r, main = "polygons (Spatial)", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(bm_ply1)), main = "boundary matrix", axes = FALSE,
    +     box = FALSE)
    +plot(r, main = "polygons (sf)", axes = FALSE, box = FALSE)
    +
    +plot(raster(as.matrix(bm_ply2)), main = "boundary matrix", axes = FALSE,
    +     box = FALSE)
    +plot(raster(as.matrix(bm_ply3)), main = "boundary matrix (Spatial, STR)",
    +            axes = FALSE, box = FALSE)
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/branch_matrix.html b/docs/reference/branch_matrix.html index 309686cd4..eb3214782 100644 --- a/docs/reference/branch_matrix.html +++ b/docs/reference/branch_matrix.html @@ -1,107 +1,24 @@ - - - - - - - -Branch matrix — branch_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Branch matrix — branch_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -196,69 +113,63 @@

    Branch matrix

    each species have experienced.

    -
    branch_matrix(x)
    -
    -# S3 method for default
    -branch_matrix(x)
    -
    -# S3 method for phylo
    -branch_matrix(x)
    +
    Usage,
    branch_matrix(x)
     
    -    

    Arguments

    - - - - - - -
    x

    ape::phylo() tree object.

    +# S3 method for default +branch_matrix(x) -

    Value

    +# S3 method for phylo +branch_matrix(x)
    -

    dgCMatrix sparse matrix object. Each row +

    +

    Arguments

    +
    x
    +

    ape::phylo() tree object.

    +
    +
    +

    Value

    +

    dgCMatrix sparse matrix object. Each row corresponds to a different species. Each column corresponds to a different branch. Species that inherit from a given branch are denoted with a one.

    +
    -

    Examples

    -
    # load data
    -data(sim_phylogeny)
    -
    -# generate species by branch matrix
    -m <- branch_matrix(sim_phylogeny)
    -
    -# plot data
    -# \dontrun{
    -par(mfrow = c(1,2))
    -plot(sim_phylogeny, main = "phylogeny")
    -plot(raster(as.matrix(m)), main = "branch matrix", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_phylogeny)
    +
    +# generate species by branch matrix
    +m <- branch_matrix(sim_phylogeny)
    +
    +# plot data
    +# \dontrun{
    +par(mfrow = c(1,2))
    +plot(sim_phylogeny, main = "phylogeny")
    +plot(raster(as.matrix(m)), main = "branch matrix", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/category_layer.html b/docs/reference/category_layer.html index 90a40e98e..effd7e9f0 100644 --- a/docs/reference/category_layer.html +++ b/docs/reference/category_layer.html @@ -1,105 +1,22 @@ - - - - - - - -Category layer — category_layer • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Category layer — category_layer • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Convert a RasterStack +

    Convert a RasterStack object where each layer corresponds to a different identifier and values indicate the presence/absence of that category into a -RasterLayer object containing categorical +RasterLayer object containing categorical identifiers.

    -
    category_layer(x)
    - -

    Arguments

    - - - - - - -
    x

    Raster object containing a multiple -layers. Note that pixels must be 0, 1 or NA values.

    - -

    Value

    - -

    RasterLayer object.

    -

    Details

    - +
    Usage,
    category_layer(x)
    + +
    +

    Arguments

    +
    x
    +

    Raster object containing a multiple +layers. Note that pixels must be 0, 1 or NA values.

    +
    +
    +

    Value

    +

    RasterLayer object.

    +
    +
    +

    Details

    This function is provided to help manage data that encompass multiple management zones. For instance, this function may be helpful for interpreting solutions for problems associated with multiple zones that have binary decisions.

    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # create a binary raster stack
    -x <- stack(raster(matrix(c(1, 0, 0, 1, NA, 0), nrow = 3)),
    -           raster(matrix(c(0, 1, 0, 0, NA, 0), nrow = 3)),
    -           raster(matrix(c(0, 0, 1, 0, NA, 1), nrow = 3)))
    -
    -# convert to binary stack
    -y <- category_layer(x)
    -
    -# plot categorical raster and binary stack representation
    -# \dontrun{
    -plot(stack(x, y), main = c("x[[1]]", "x[[2]]", "x[[3]]", "y"), nr = 1)
    -
    -# }
    +    
    +

    Examples

    +
    # create a binary raster stack
    +x <- stack(raster(matrix(c(1, 0, 0, 1, NA, 0), nrow = 3)),
    +           raster(matrix(c(0, 1, 0, 0, NA, 0), nrow = 3)),
    +           raster(matrix(c(0, 0, 1, 0, NA, 1), nrow = 3)))
    +
    +# convert to binary stack
    +y <- category_layer(x)
    +
    +# plot categorical raster and binary stack representation
    +# \dontrun{
    +plot(stack(x, y), main = c("x[[1]]", "x[[2]]", "x[[3]]", "y"), nr = 1)
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/category_vector.html b/docs/reference/category_vector.html index 8406ea588..a6281900f 100644 --- a/docs/reference/category_vector.html +++ b/docs/reference/category_vector.html @@ -1,103 +1,20 @@ - - - - - - - -Category vector — category_vector • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Category vector — category_vector • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -188,86 +105,82 @@

    Category vector

    1.

    -
    category_vector(x)
    -
    -# S3 method for data.frame
    -category_vector(x)
    +    
    Usage,
    category_vector(x)
     
    -# S3 method for sf
    -category_vector(x)
    +# S3 method for data.frame
    +category_vector(x)
     
    -# S3 method for Spatial
    -category_vector(x)
    +# S3 method for sf
    +category_vector(x)
     
    -# S3 method for matrix
    -category_vector(x)
    +# S3 method for Spatial +category_vector(x) -

    Arguments

    - - - - - - -
    x

    matrix, data.frame, Spatial, -or sf::sf() object.

    - -

    Value

    +# S3 method for matrix +category_vector(x)
    +
    +

    Arguments

    +
    x
    +

    matrix, data.frame, Spatial, +or sf::sf() object.

    +
    +
    +

    Value

    integer vector.

    -

    Details

    - -

    This function is conceptually similar to base::max.col() +

    +
    +

    Details

    +

    This function is conceptually similar to base::max.col() except that rows with no values equal to 1 values are assigned a value of zero. Also, note that in the argument to x, each row must contain only a single value equal to 1.

    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # create matrix with logical fields
    -x <- matrix(c(1, 0, 0, NA, 0, 1, 0, NA, 0, 0, 0, NA), ncol = 3)
    -
    -# print matrix
    -print(x)
    -#>      [,1] [,2] [,3]
    -#> [1,]    1    0    0
    -#> [2,]    0    1    0
    -#> [3,]    0    0    0
    -#> [4,]   NA   NA   NA
    -
    -# convert to category vector
    -y <- category_vector(x)
    -
    -# print category vector
    -print(y)
    -#> [1]  1  2  0 NA
    +    
    +

    Examples

    +
    # create matrix with logical fields
    +x <- matrix(c(1, 0, 0, NA, 0, 1, 0, NA, 0, 0, 0, NA), ncol = 3)
    +
    +# print matrix
    +print(x)
    +#>      [,1] [,2] [,3]
    +#> [1,]    1    0    0
    +#> [2,]    0    1    0
    +#> [3,]    0    0    0
    +#> [4,]   NA   NA   NA
    +
    +# convert to category vector
    +y <- category_vector(x)
    +
    +# print category vector
    +print(y)
    +#> [1]  1  2  0 NA
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/compile.html b/docs/reference/compile.html index 2eca18519..835f4f7bb 100644 --- a/docs/reference/compile.html +++ b/docs/reference/compile.html @@ -1,102 +1,19 @@ - - - - - - - -Compile a problem — compile • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Compile a problem — compile • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Compile a conservation planning problem() into an +

    Compile a conservation planning problem() into an (potentially mixed) integer linear programming problem.

    -
    compile(x, ...)
    +    
    Usage,
    compile(x, ...)
     
    -# S3 method for ConservationProblem
    -compile(x, compressed_formulation = NA, ...)
    +# S3 method for ConservationProblem +compile(x, compressed_formulation = NA, ...)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    ...

    not used.

    compressed_formulation

    logical should the conservation problem +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    ...
    +

    not used.

    +
    compressed_formulation
    +

    logical should the conservation problem compiled into a compressed version of a planning problem? If TRUE then the problem is expressed using the compressed formulation. If FALSE then the problem is expressed using the expanded formulation. If NA, then the compressed is used unless one of the constraints requires the expanded formulation. This argument -defaults to NA.

    - -

    Value

    - -

    OptimizationProblem object.

    -

    Details

    - +defaults to NA.

    +
    +
    +

    Value

    +

    OptimizationProblem object.

    +
    +
    +

    Details

    This function might be useful for those interested in understanding -how their conservation planning problem() is expressed +how their conservation planning problem() is expressed as a mathematical problem. However, if the problem just needs to -be solved, then the solve() function should just be used.

    +be solved, then the solve() function should just be used.

    Please note that in nearly all cases, the default argument to formulation should be used. The only situation where manually setting the argument to formulation is desirable is during testing. @@ -230,48 +140,46 @@

    Details have no effect on the problem. At worst, it may result in an error, a misspecified problem, or unnecessarily long solve times.

    +

    -

    Examples

    -
    # build minimal conservation problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -  add_min_set_objective() %>%
    -  add_relative_targets(0.1)
    -
    -# compile the conservation problem into an optimization problem
    -o <- compile(p)
    -
    -# print the optimization problem
    -print(o)
    -#> optimization problem
    -#>   model sense: min
    -#>   dimensions:  5, 90, 450 (nrow, ncol, ncell)
    -#>   variables:   90 (B)
    -
    +    
    +

    Examples

    +
    # build minimal conservation problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +  add_min_set_objective() %>%
    +  add_relative_targets(0.1)
    +
    +# compile the conservation problem into an optimization problem
    +o <- compile(p)
    +
    +# print the optimization problem
    +print(o)
    +#> optimization problem
    +#>   model sense: min
    +#>   dimensions:  5, 90, 450 (nrow, ncol, ncell)
    +#>   variables:   90 (B)
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/connectivity_matrix.html b/docs/reference/connectivity_matrix.html index b01fa0d8c..7230dd897 100644 --- a/docs/reference/connectivity_matrix.html +++ b/docs/reference/connectivity_matrix.html @@ -1,105 +1,22 @@ - - - - - - - -Connectivity matrix — connectivity_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Connectivity matrix — connectivity_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -192,248 +109,238 @@

    Connectivity matrix

    boundary are associated with greater connectivity.

    -
    connectivity_matrix(x, y, ...)
    +    
    Usage,
    connectivity_matrix(x, y, ...)
     
    -# S4 method for Spatial,Raster
    -connectivity_matrix(x, y, ...)
    +# S4 method for Spatial,Raster
    +connectivity_matrix(x, y, ...)
     
    -# S4 method for Spatial,character
    -connectivity_matrix(x, y, ...)
    +# S4 method for Spatial,character
    +connectivity_matrix(x, y, ...)
     
    -# S4 method for sf,character
    -connectivity_matrix(x, y, ...)
    +# S4 method for sf,character
    +connectivity_matrix(x, y, ...)
     
    -# S4 method for sf,Raster
    -connectivity_matrix(x, y, ...)
    +# S4 method for sf,Raster
    +connectivity_matrix(x, y, ...)
     
    -# S4 method for Raster,Raster
    -connectivity_matrix(x, y, ...)
    +# S4 method for Raster,Raster +connectivity_matrix(x, y, ...)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    Raster, -SpatialPolygonsDataFrame, -SpatialLinesDataFrame, -or sf::sf() object +

    +

    Arguments

    +
    x
    +

    Raster, +SpatialPolygonsDataFrame, +SpatialLinesDataFrame, +or sf::sf() object representing planning units. -If x is a Raster object then it must -contain a single layer.

    y

    Raster object showing the conductance +If x is a Raster object then it must +contain a single layer.

    +
    y
    +

    Raster object showing the conductance of different areas across the study area, or a character object denoting a column name in the attribute table of x that contains the conductance values. Note that argument to y can only be a character object if the argument to x is a -Spatial or sf::sf() object. +Spatial or sf::sf() object. Also, note that if the argument to x is a -Raster object then +Raster object then argument to y must have the same spatial properties as it -(i.e. coordinate system, extent, resolution).

    ...

    additional arguments passed to fast_extract() for +(i.e., coordinate system, extent, resolution).

    +
    ...
    +

    additional arguments passed to fast_extract() for extracting and calculating the conductance values for each planning unit. These arguments are only used if argument to x is a -link[sp]{Spatial-class} or sf::sf() object and argument -to y is a Raster object.

    - -

    Value

    - -

    dsCMatrix symmetric sparse matrix object. +link[sp]{Spatial-class} or sf::sf() object and argument +to y is a Raster object.

    +
    +
    +

    Value

    +

    dsCMatrix symmetric sparse matrix object. Each row and column represents a planning unit. Cells values indicate the connectivity between different pairs of planning units. To reduce computational burden, cells among the matrix diagonal are set to zero. Furthermore, if the argument to x is a -Raster object, then cells with NA -values are set to zero too.

    -

    Details

    - +Raster object, then cells with NAvalues are set to zero too.

    +
    +
    +

    Details

    Shared boundary calculations are performed using -boundary_matrix().

    +boundary_matrix().

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_sf, sim_features)
    -
    -# create connectivity matrix using raster planning unit data using
    -# the raster cost values to represent conductance
    -## extract 9 planning units
    -r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    -
    -## extract conductance data for the 9 planning units
    -cd <- crop(sim_features, r)
    -
    -## make connectivity matrix using the habitat suitability data for the
    -## second feature to represent the planning unit conductance data
    -cm_raster <- connectivity_matrix(r, cd[[2]])
    -
    -## plot data and matrix
    -# \dontrun{
    -par(mfrow = c(1,3))
    -plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    -plot(cd[[2]], main = "conductivity", axes = FALSE, box = FALSE)
    -plot(clamp(raster(as.matrix(cm_raster)), lower = 1e-5, useValues = FALSE),
    -     main = "connectivity", axes = FALSE, box = FALSE)
    -
    -# }
    -# create connectivity matrix using polygon planning unit data using
    -# the habitat suitability data for the second feature to represent
    -# planning unit conductances
    -## subset data to 9 polygons
    -ply <- sim_pu_sf[c(1:2, 10:12, 20:22), ]
    -
    -## make connectivity matrix
    -cm_ply <- connectivity_matrix(ply, sim_features[[2]])
    -
    -## plot data and matrix
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(st_geometry(ply), main = "planning units (sf)")
    -plot(clamp(raster(as.matrix(cm_ply)), lower = 1e-5, useValues = FALSE),
    -     main = "connectivity", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# create connectivity matrix using habitat suitability data for each feature,
    -# this could be useful if prioritisations should spatially clump
    -# together adjacent planning units that have suitable habitat
    -# for the same species (e.g. to maintain functional connectivity)
    -
    -## let's use the raster data for this example, and we can generate the
    -## connectivity matrix that we would use in the prioritization by
    -## (1) generating a connectivity matrix for each feature separately, and
    -## and then (2) then summing the values together
    -cm_sum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    -cm_sum <- Reduce("+", cm_sum) # sum matrices together
    -
    -## plot data and matrix
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    -plot(clamp(raster(as.matrix(cm_sum)), lower = 1e-5, useValues = FALSE),
    -     main = "connectivity", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -## we could take this example one step further, and use weights to indicate
    -## relative importance of maintaining functional connectivity
    -## for each feature (i.e. use the weighted sum instead of the sum)
    -
    -## let's pretend that the first feature is 20 times more important
    -## than all the other species
    -weights <- c(20, 1, 1, 1, 1)
    -
    -## calculate connectivity matrix using weighted sum
    -cm_wsum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    -cm_wsum <- Map("*", cm_wsum, weights) # multiply by weights
    -cm_wsum <- Reduce("+", cm_wsum) # sum matrices together
    -
    -## plot data and matrix
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    -plot(clamp(raster(as.matrix(cm_wsum)), lower = 1e-5, useValues = FALSE),
    -     main = "connectivity", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -## since the statistical distribution of the connectivity values
    -## for each feature (e.g. the mean and standard deviation of the
    -## connectivity values) are different, it might make sense -- depending
    -## on the goal of the conservation planning exercise and the underlying
    -## data -- to first normalize the conductance values before applying the
    -## weights and summing the data for feature together
    -
    -## one approach would be to linearly rescale the values between 0.01 and 1
    -## note that we wouldn't want to rescale them between 0 and 1 since
    -## a value of zero means that there is no connectivity at all (and
    -## and not a relatively small amount of connectivity)
    -# \dontrun{
    -### define helper function
    -library(scales) # load scales library for rescale
    -rescale_matrix <- function(x) {x@x <- rescale(x@x, c(0.01, 1)); x}
    -
    -### calculate functional connectivity matrix using the weighted sum of
    -### connectivity values that have been normalized by linearly re-scaling
    -### values
    -cm_lwsum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    -cm_lwsum <- lapply(cm_lwsum, rescale_matrix) # rescale matrices to [0.01, 1]
    -cm_lwsum <- Map("*", cm_lwsum, weights) # multiply by weights
    -cm_lwsum <- Reduce("+", cm_lwsum) # sum matrices together
    -# }
    -
    -## plot data and matrix
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    -plot(clamp(raster(as.matrix(cm_lwsum)), lower = 1e-5, useValues = FALSE),
    -     main = "connectivity", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -## another approach for normalizing the data could be using z-scores
    -## note that after normalizing the data we would need to add a constant
    -## value so that none of the connectivity values are negative
    -
    -### define helper functions
    -zscore <- function(x) {x@x <- (x@x - mean(x@x)) / sd(x@x); x}
    -min_non_zero_value <- function(x) min(x@x)
    -add_non_zero_value <- function(x, y) {x@x <- x@x + y; x}
    -
    -### calculate functional connectivity matrix using the weighted sum of
    -### connectivity values that have been normalized using z-scores,
    -### and transformed to account for negative values
    -cm_zwsum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    -cm_zwsum <- lapply(cm_zwsum, zscore) # normalize using z-scores
    -min_value <- min(sapply(cm_zwsum, min_non_zero_value)) # find min value
    -min_value <- abs(min_value) + 0.01 # prepare constant for adding to matrices
    -cm_zwsum <- lapply(cm_zwsum, add_non_zero_value, min_value) # add constant
    -cm_zwsum <- Map("*", cm_zwsum, weights) # multiply by weights
    -cm_zwsum <- Reduce("+", cm_zwsum) # sum matrices together
    -
    -## plot data and matrix
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    -plot(clamp(raster(as.matrix(cm_zwsum)), lower = 1e-5, useValues = FALSE),
    -     main = "connectivity", axes = FALSE, box = FALSE)
    -
    -# }
    -
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_sf, sim_features)
    +
    +# create connectivity matrix using raster planning unit data using
    +# the raster cost values to represent conductance
    +## extract 9 planning units
    +r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    +
    +## extract conductance data for the 9 planning units
    +cd <- crop(sim_features, r)
    +
    +## make connectivity matrix using the habitat suitability data for the
    +## second feature to represent the planning unit conductance data
    +cm_raster <- connectivity_matrix(r, cd[[2]])
    +
    +## plot data and matrix
    +# \dontrun{
    +par(mfrow = c(1,3))
    +plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    +plot(cd[[2]], main = "conductivity", axes = FALSE, box = FALSE)
    +plot(clamp(raster(as.matrix(cm_raster)), lower = 1e-5, useValues = FALSE),
    +     main = "connectivity", axes = FALSE, box = FALSE)
    +
    +# }
    +# create connectivity matrix using polygon planning unit data using
    +# the habitat suitability data for the second feature to represent
    +# planning unit conductances
    +## subset data to 9 polygons
    +ply <- sim_pu_sf[c(1:2, 10:12, 20:22), ]
    +
    +## make connectivity matrix
    +cm_ply <- connectivity_matrix(ply, sim_features[[2]])
    +
    +## plot data and matrix
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(st_geometry(ply), main = "planning units (sf)")
    +plot(clamp(raster(as.matrix(cm_ply)), lower = 1e-5, useValues = FALSE),
    +     main = "connectivity", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# create connectivity matrix using habitat suitability data for each feature,
    +# this could be useful if prioritisations should spatially clump
    +# together adjacent planning units that have suitable habitat
    +# for the same species (e.g., to maintain functional connectivity)
    +
    +## let's use the raster data for this example, and we can generate the
    +## connectivity matrix that we would use in the prioritization by
    +## (1) generating a connectivity matrix for each feature separately, and
    +## and then (2) then summing the values together
    +cm_sum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    +cm_sum <- Reduce("+", cm_sum) # sum matrices together
    +
    +## plot data and matrix
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    +plot(clamp(raster(as.matrix(cm_sum)), lower = 1e-5, useValues = FALSE),
    +     main = "connectivity", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +## we could take this example one step further, and use weights to indicate
    +## relative importance of maintaining functional connectivity
    +## for each feature (i.e., use the weighted sum instead of the sum)
    +
    +## let's pretend that the first feature is 20 times more important
    +## than all the other species
    +weights <- c(20, 1, 1, 1, 1)
    +
    +## calculate connectivity matrix using weighted sum
    +cm_wsum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    +cm_wsum <- Map("*", cm_wsum, weights) # multiply by weights
    +cm_wsum <- Reduce("+", cm_wsum) # sum matrices together
    +
    +## plot data and matrix
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    +plot(clamp(raster(as.matrix(cm_wsum)), lower = 1e-5, useValues = FALSE),
    +     main = "connectivity", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +## since the statistical distribution of the connectivity values
    +## for each feature (e.g., the mean and standard deviation of the
    +## connectivity values) are different, it might make sense -- depending
    +## on the goal of the conservation planning exercise and the underlying
    +## data -- to first normalize the conductance values before applying the
    +## weights and summing the data for feature together
    +
    +## one approach would be to linearly rescale the values between 0.01 and 1
    +## note that we wouldn't want to rescale them between 0 and 1 since
    +## a value of zero means that there is no connectivity at all (and
    +## and not a relatively small amount of connectivity)
    +# \dontrun{
    +### define helper function
    +library(scales) # load scales library for rescale
    +rescale_matrix <- function(x) {x@x <- rescale(x@x, c(0.01, 1)); x}
    +
    +### calculate functional connectivity matrix using the weighted sum of
    +### connectivity values that have been normalized by linearly re-scaling
    +### values
    +cm_lwsum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    +cm_lwsum <- lapply(cm_lwsum, rescale_matrix) # rescale matrices to [0.01, 1]
    +cm_lwsum <- Map("*", cm_lwsum, weights) # multiply by weights
    +cm_lwsum <- Reduce("+", cm_lwsum) # sum matrices together
    +# }
    +
    +## plot data and matrix
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    +plot(clamp(raster(as.matrix(cm_lwsum)), lower = 1e-5, useValues = FALSE),
    +     main = "connectivity", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +## another approach for normalizing the data could be using z-scores
    +## note that after normalizing the data we would need to add a constant
    +## value so that none of the connectivity values are negative
    +
    +### define helper functions
    +zscore <- function(x) {x@x <- (x@x - mean(x@x)) / sd(x@x); x}
    +min_non_zero_value <- function(x) min(x@x)
    +add_non_zero_value <- function(x, y) {x@x <- x@x + y; x}
    +
    +### calculate functional connectivity matrix using the weighted sum of
    +### connectivity values that have been normalized using z-scores,
    +### and transformed to account for negative values
    +cm_zwsum <- lapply(as.list(cd), connectivity_matrix, x = r) # make matrices
    +cm_zwsum <- lapply(cm_zwsum, zscore) # normalize using z-scores
    +min_value <- min(sapply(cm_zwsum, min_non_zero_value)) # find min value
    +min_value <- abs(min_value) + 0.01 # prepare constant for adding to matrices
    +cm_zwsum <- lapply(cm_zwsum, add_non_zero_value, min_value) # add constant
    +cm_zwsum <- Map("*", cm_zwsum, weights) # multiply by weights
    +cm_zwsum <- Reduce("+", cm_zwsum) # sum matrices together
    +
    +## plot data and matrix
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(r, main = "planning units (raster)", axes = FALSE, box = FALSE)
    +plot(clamp(raster(as.matrix(cm_zwsum)), lower = 1e-5, useValues = FALSE),
    +     main = "connectivity", axes = FALSE, box = FALSE)
    +
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/constraints.html b/docs/reference/constraints.html index 92eafcb4e..e88080bff 100644 --- a/docs/reference/constraints.html +++ b/docs/reference/constraints.html @@ -1,102 +1,19 @@ - - - - - - - -Conservation problem constraints — constraints • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Conservation problem constraints — constraints • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    A constraint can be added to a conservation planning problem() +

    A constraint can be added to a conservation planning problem() to ensure that solutions exhibit a specific characteristic.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    Constraints can be used to ensure that solutions exhibit a range of different characteristics. For instance, they can be used to lock in or lock out certain planning units from the solution, such as protected areas or degraded land (respectively). -Additionally, similar to the penalties functions, +Additionally, similar to the penalties functions, some of the constraint functions can be used to increase connectivity in a solution. The key difference between a penalty and a constraint, however, is that constraints work by invalidating solutions that do not exhibit @@ -203,117 +120,126 @@

    Details constraints do not affect the objective function. The following constraints are available.

    The following constraints can be added to a conservation planning -problem():

    -
    - -
    add_locked_in_constraints()

    Add constraints to ensure +problem():

    +
    add_locked_in_constraints()
    +

    Add constraints to ensure that certain planning units are selected in the solution.

    -
    add_locked_out_constraints()

    Add constraints to ensure + +

    add_locked_out_constraints()
    +

    Add constraints to ensure that certain planning units are not selected in the solution.

    -
    add_neighbor_constraints()

    Add constraints to + +

    add_neighbor_constraints()
    +

    Add constraints to ensure that all selected planning units have at least a certain number of neighbors.

    -
    add_contiguity_constraints()

    Add constraints to a + +

    add_contiguity_constraints()
    +

    Add constraints to a ensure that all selected planning units are spatially connected to each other and form a single contiguous unit.

    -
    add_feature_contiguity_constraints()

    Add constraints to + +

    add_feature_contiguity_constraints()
    +

    Add constraints to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of -those implemented in the add_contiguity_constraints() +those implemented in the add_contiguity_constraints() function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit.

    -
    add_linear_constraints()

    Add constraints to ensure that all selected planning units meet certain + +

    add_linear_constraints()
    +

    Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study -region (e.g. different countries).

    +region (e.g., different countries).

    -
    add_mandatory_allocation_constraints()

    Add constraints to ensure that every planning unit is allocated to a + +

    add_mandatory_allocation_constraints()
    +

    Add constraints to ensure that every planning unit is allocated to a management zone in the solution. This function can only be used with problems that contain multiple zones.

    -
    - -

    See also

    - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features, sim_locked_in_raster,
    -     sim_locked_out_raster)
    -
    -# create minimal problem with only targets and no additional constraints
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with locked in constraints
    -p2 <- p1 %>% add_locked_in_constraints(sim_locked_in_raster)
    -
    -# create problem with locked in constraints
    -p3 <- p1 %>% add_locked_out_constraints(sim_locked_out_raster)
    -
    -# create problem with neighbor constraints
    -p4 <- p1 %>% add_neighbor_constraints(2)
    -
    -# create problem with contiguity constraints
    -p5 <- p1 %>% add_contiguity_constraints()
    -
    -# create problem with feature contiguity constraints
    -p6 <- p1 %>% add_feature_contiguity_constraints()
    -# \dontrun{
    -# solve problems
    -s <- stack(lapply(list(p1, p2, p3, p4, p5, p6), solve))
    -
    -# plot solutions
    -plot(s, box = FALSE, axes = FALSE, nr = 2,
    -     main = c("minimal problem", "locked in", "locked out",
    -              "neighbor", "contiguity", "feature contiguity"))
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features, sim_locked_in_raster,
    +     sim_locked_out_raster)
    +
    +# create minimal problem with only targets and no additional constraints
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with locked in constraints
    +p2 <- p1 %>% add_locked_in_constraints(sim_locked_in_raster)
    +
    +# create problem with locked in constraints
    +p3 <- p1 %>% add_locked_out_constraints(sim_locked_out_raster)
    +
    +# create problem with neighbor constraints
    +p4 <- p1 %>% add_neighbor_constraints(2)
    +
    +# create problem with contiguity constraints
    +p5 <- p1 %>% add_contiguity_constraints()
    +
    +# create problem with feature contiguity constraints
    +p6 <- p1 %>% add_feature_contiguity_constraints()
    +# \dontrun{
    +# solve problems
    +s <- stack(lapply(list(p1, p2, p3, p4, p5, p6), solve))
    +
    +# plot solutions
    +plot(s, box = FALSE, axes = FALSE, nr = 2,
    +     main = c("minimal problem", "locked in", "locked out",
    +              "neighbor", "contiguity", "feature contiguity"))
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/decisions.html b/docs/reference/decisions.html index a0f3a4cc3..4807838fe 100644 --- a/docs/reference/decisions.html +++ b/docs/reference/decisions.html @@ -1,106 +1,23 @@ - - - - - - - -Add decision types — decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add decision types — decisions • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -191,113 +108,114 @@

    Add decision types

    turning an entire planning unit into a protected area, turning part of a planning unit into a protected area, or allocating a planning unit to a specific management zone. If no decision is explicitly added to a -problem(), then binary decisions will be used by default.

    +problem(), then binary decisions will be used by default.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    Only a single type of decision can be added to a conservation -planning problem(). Note that if multiple decisions are added +planning problem(). Note that if multiple decisions are added to a problem, then the last one added will be used.

    The following decisions can be added to a conservation planning -problem():

    -
    - -
    add_binary_decisions()

    Add a binary decision to a +problem():

    +
    add_binary_decisions()
    +

    Add a binary decision to a conservation planning problem. This is the classic decision of either prioritizing or not prioritizing a planning unit. Typically, this decision has the assumed action of buying the planning unit to include in a protected area network. If no decision is added to a problem object then this decision class will be used by default.

    -
    add_proportion_decisions()

    Add a proportion decision to + +

    add_proportion_decisions()
    +

    Add a proportion decision to a conservation planning problem. This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the default of the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in a protected area network.

    -
    add_semicontinuous_decisions()

    Add a semi-continuous + +

    add_semicontinuous_decisions()
    +

    Add a semi-continuous decision to a conservation planning problem. This decision is similar to add_proportion_decision except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction -(e.g. 80%) of a planning unit can be preserved. This type of +(e.g., 80%) of a planning unit can be preserved. This type of decision may be useful when it is not practical to conserve the entire area encompassed by any single planning unit.

    -
    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create basic problem and using the default decision types (binary)
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with manually specified binary decisions
    -p2 <- p1 %>% add_binary_decisions()
    -
    -# create problem with proportion decisions
    -p3 <- p1 %>% add_proportion_decisions()
    -
    -# create problem with semicontinuous decisions
    -p4 <- p1 %>% add_semicontinuous_decisions(upper_limit = 0.5)
    -
    -# \dontrun{
    -# solve problem
    -s <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    -
    -# plot solutions
    -plot(s, main = c("default (binary)", "binary", "proportion",
    -                 "semicontinuous (upper = 0.5)"))
    -
    -# }
    -
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create basic problem and using the default decision types (binary)
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with manually specified binary decisions
    +p2 <- p1 %>% add_binary_decisions()
    +
    +# create problem with proportion decisions
    +p3 <- p1 %>% add_proportion_decisions()
    +
    +# create problem with semicontinuous decisions
    +p4 <- p1 %>% add_semicontinuous_decisions(upper_limit = 0.5)
    +
    +# \dontrun{
    +# solve problem
    +s <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    +
    +# plot solutions
    +plot(s, main = c("default (binary)", "binary", "proportion",
    +                 "semicontinuous (upper = 0.5)"))
    +
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/distribute_load.html b/docs/reference/distribute_load.html index 64feeb949..d1c62e13b 100644 --- a/docs/reference/distribute_load.html +++ b/docs/reference/distribute_load.html @@ -1,102 +1,19 @@ - - - - - - - -Distribute load — distribute_load • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Distribute load — distribute_load • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,199 +103,192 @@

    Distribute load

    for parallel processing.

    -
    distribute_load(x, n = 1)
    - -

    Arguments

    - - - - - - - - - - -
    x

    integer number of item to process.

    n

    integer number of threads.

    - -

    Value

    - +
    Usage,
    distribute_load(x, n = 1)
    + +
    +

    Arguments

    +
    x
    +

    integer number of item to process.

    +
    n
    +

    integer number of threads.

    +
    +
    +

    Value

    list object.

    -

    Details

    - +
    +
    +

    Details

    This function returns a list containing an element for each worker. Each element contains a integer vector specifying the indices that the worker should process.

    +
    -

    Examples

    -
    # \dontrun{
    -
    -# imagine that we have 10 jobs that need processing. For simplicity,
    -# our jobs will involve adding 1 to each element in 1:10.
    -values <- 1:10
    -
    -# we could complete this processing using the following vectorized code
    -result <- 1 + 1:10
    -print(result)
    -#>  [1]  2  3  4  5  6  7  8  9 10 11
    -
    -# however, if our jobs were complex then we would be better off using
    -# functionals
    -result <- lapply(1:10, function(x) x + 1)
    -print(result)
    -#> [[1]]
    -#> [1] 2
    -#> 
    -#> [[2]]
    -#> [1] 3
    -#> 
    -#> [[3]]
    -#> [1] 4
    -#> 
    -#> [[4]]
    -#> [1] 5
    -#> 
    -#> [[5]]
    -#> [1] 6
    -#> 
    -#> [[6]]
    -#> [1] 7
    -#> 
    -#> [[7]]
    -#> [1] 8
    -#> 
    -#> [[8]]
    -#> [1] 9
    -#> 
    -#> [[9]]
    -#> [1] 10
    -#> 
    -#> [[10]]
    -#> [1] 11
    -#> 
    -
    -# we could do one better, and use the "plyr" package to handle the
    -# processing
    -result <- plyr::llply(1:10, function(x) x + 1)
    -print(result)
    -#> [[1]]
    -#> [1] 2
    -#> 
    -#> [[2]]
    -#> [1] 3
    -#> 
    -#> [[3]]
    -#> [1] 4
    -#> 
    -#> [[4]]
    -#> [1] 5
    -#> 
    -#> [[5]]
    -#> [1] 6
    -#> 
    -#> [[6]]
    -#> [1] 7
    -#> 
    -#> [[7]]
    -#> [1] 8
    -#> 
    -#> [[8]]
    -#> [1] 9
    -#> 
    -#> [[9]]
    -#> [1] 10
    -#> 
    -#> [[10]]
    -#> [1] 11
    -#> 
    -
    -# we could also use the parallel processing options available through "plyr"
    -# to use more computation resources to complete the jobs (note that since
    -# these jobs are very quick to process this is actually slower).
    -cl <- parallel::makeCluster(2, "PSOCK")
    -doParallel::registerDoParallel(cl)
    -result <- plyr::llply(1:10, function(x) x + 1, .parallel = TRUE)
    -#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    -#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    -cl <- parallel::stopCluster(cl)
    -print(result)
    -#> [[1]]
    -#> [1] 2
    -#> 
    -#> [[2]]
    -#> [1] 3
    -#> 
    -#> [[3]]
    -#> [1] 4
    -#> 
    -#> [[4]]
    -#> [1] 5
    -#> 
    -#> [[5]]
    -#> [1] 6
    -#> 
    -#> [[6]]
    -#> [1] 7
    -#> 
    -#> [[7]]
    -#> [1] 8
    -#> 
    -#> [[8]]
    -#> [1] 9
    -#> 
    -#> [[9]]
    -#> [1] 10
    -#> 
    -#> [[10]]
    -#> [1] 11
    -#> 
    -
    -# however this approach iterates over each element individually, we could
    -# use the distribute_load function to split the N jobs up into K super
    -# jobs, and evaluate each super job using vectorized code.
    -x <- 1:10
    -cl <- parallel::makeCluster(2, "PSOCK")
    -parallel::clusterExport(cl, 'x', envir = environment())
    -doParallel::registerDoParallel(cl)
    -l <- distribute_load(length(x), n = 2)
    -result <- plyr::llply(l, function(i) x[i] + 1, .parallel = TRUE)
    -#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    -#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    -cl <- parallel::stopCluster(cl)
    -print(result)
    -#> [[1]]
    -#> [1] 2 3 4 5 6
    -#> 
    -#> [[2]]
    -#> [1]  7  8  9 10 11
    -#> 
    -# }
    -
    +    
    +

    Examples

    +
    # \dontrun{
    +
    +# imagine that we have 10 jobs that need processing. For simplicity,
    +# our jobs will involve adding 1 to each element in 1:10.
    +values <- 1:10
    +
    +# we could complete this processing using the following vectorized code
    +result <- 1 + 1:10
    +print(result)
    +#>  [1]  2  3  4  5  6  7  8  9 10 11
    +
    +# however, if our jobs were complex then we would be better off using
    +# functionals
    +result <- lapply(1:10, function(x) x + 1)
    +print(result)
    +#> [[1]]
    +#> [1] 2
    +#> 
    +#> [[2]]
    +#> [1] 3
    +#> 
    +#> [[3]]
    +#> [1] 4
    +#> 
    +#> [[4]]
    +#> [1] 5
    +#> 
    +#> [[5]]
    +#> [1] 6
    +#> 
    +#> [[6]]
    +#> [1] 7
    +#> 
    +#> [[7]]
    +#> [1] 8
    +#> 
    +#> [[8]]
    +#> [1] 9
    +#> 
    +#> [[9]]
    +#> [1] 10
    +#> 
    +#> [[10]]
    +#> [1] 11
    +#> 
    +
    +# we could do one better, and use the "plyr" package to handle the
    +# processing
    +result <- plyr::llply(1:10, function(x) x + 1)
    +print(result)
    +#> [[1]]
    +#> [1] 2
    +#> 
    +#> [[2]]
    +#> [1] 3
    +#> 
    +#> [[3]]
    +#> [1] 4
    +#> 
    +#> [[4]]
    +#> [1] 5
    +#> 
    +#> [[5]]
    +#> [1] 6
    +#> 
    +#> [[6]]
    +#> [1] 7
    +#> 
    +#> [[7]]
    +#> [1] 8
    +#> 
    +#> [[8]]
    +#> [1] 9
    +#> 
    +#> [[9]]
    +#> [1] 10
    +#> 
    +#> [[10]]
    +#> [1] 11
    +#> 
    +
    +# we could also use the parallel processing options available through "plyr"
    +# to use more computation resources to complete the jobs (note that since
    +# these jobs are very quick to process this is actually slower).
    +cl <- parallel::makeCluster(2, "PSOCK")
    +doParallel::registerDoParallel(cl)
    +result <- plyr::llply(1:10, function(x) x + 1, .parallel = TRUE)
    +#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    +#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    +cl <- parallel::stopCluster(cl)
    +print(result)
    +#> [[1]]
    +#> [1] 2
    +#> 
    +#> [[2]]
    +#> [1] 3
    +#> 
    +#> [[3]]
    +#> [1] 4
    +#> 
    +#> [[4]]
    +#> [1] 5
    +#> 
    +#> [[5]]
    +#> [1] 6
    +#> 
    +#> [[6]]
    +#> [1] 7
    +#> 
    +#> [[7]]
    +#> [1] 8
    +#> 
    +#> [[8]]
    +#> [1] 9
    +#> 
    +#> [[9]]
    +#> [1] 10
    +#> 
    +#> [[10]]
    +#> [1] 11
    +#> 
    +
    +# however this approach iterates over each element individually, we could
    +# use the distribute_load function to split the N jobs up into K super
    +# jobs, and evaluate each super job using vectorized code.
    +x <- 1:10
    +cl <- parallel::makeCluster(2, "PSOCK")
    +parallel::clusterExport(cl, 'x', envir = environment())
    +doParallel::registerDoParallel(cl)
    +l <- distribute_load(length(x), n = 2)
    +result <- plyr::llply(l, function(i) x[i] + 1, .parallel = TRUE)
    +#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    +#> Warning: <anonymous>: ... may be used in an incorrect context: ‘.fun(piece, ...)’
    +cl <- parallel::stopCluster(cl)
    +print(result)
    +#> [[1]]
    +#> [1] 2 3 4 5 6
    +#> 
    +#> [[2]]
    +#> [1]  7  8  9 10 11
    +#> 
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/eval_boundary_summary.html b/docs/reference/eval_boundary_summary.html index f22163a1b..3f86ee603 100644 --- a/docs/reference/eval_boundary_summary.html +++ b/docs/reference/eval_boundary_summary.html @@ -1,104 +1,21 @@ - - - - - - - -Evaluate solution boundary length — eval_boundary_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solution boundary length — eval_boundary_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Calculate the exposed boundary length (perimeter) associated with a -solution to a conservation planning problem(). +solution to a conservation planning problem(). This summary statistic is useful for evaluating the spatial fragmentation of planning units selected within a solution.

    -
    eval_boundary_summary(x, ...)
    -
    -# S3 method for default
    -eval_boundary_summary(x, ...)
    -
    -# S3 method for ConservationProblem
    -eval_boundary_summary(
    -  x,
    -  solution,
    -  edge_factor = rep(0.5, number_of_zones(x)),
    -  zones = diag(number_of_zones(x)),
    -  data = NULL,
    -  ...
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    ...

    not used.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. +

    Usage,
    eval_boundary_summary(x, ...)
    +
    +# S3 method for default
    +eval_boundary_summary(x, ...)
    +
    +# S3 method for ConservationProblem
    +eval_boundary_summary(
    +  x,
    +  solution,
    +  edge_factor = rep(0.5, number_of_zones(x)),
    +  zones = diag(number_of_zones(x)),
    +  data = NULL,
    +  ...
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    ...
    +

    not used.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. The argument should be in the same format as the planning unit cost data in the argument to x. -See the Solution format section for more information.

    edge_factor

    numeric proportion to scale +See the Solution format section for more information.

    +
    edge_factor
    +

    numeric proportion to scale planning unit edges (borders) that do not have any neighboring planning units. For example, an edge factor of 0.5 is commonly used to avoid overly penalizing planning units along a coastline. Note that this argument must have an element for each zone in the argument -to x.

    zones

    matrix or Matrix object describing the +to x.

    +
    zones
    +

    matrix or Matrix object describing the clumping scheme for different zones. Each row and column corresponds to a different zone in the argument to x, and cell values indicate the relative importance of clumping planning units that are allocated to @@ -245,33 +152,27 @@

    Arg allocated to the same zone. Cell values must range between 1 and -1, where negative values favor solutions that spread out planning units. The default argument to zones is an identity -matrix (i.e. a matrix with ones along the matrix diagonal and zeros +matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that penalties are incurred when neighboring planning units are not assigned to the same zone. If the cells along the matrix diagonal contain markedly smaller values than those found elsewhere in the matrix, then solutions are preferred that surround planning units with those allocated to different zones -(i.e. greater spatial fragmentation).

    data

    NULL, data.frame, matrix, or Matrix +(i.e., greater spatial fragmentation).

    +
    data
    +

    NULL, data.frame, matrix, or Matrix object containing the boundary data. These data describe the total amount of boundary (perimeter) length for each planning unit, and the amount of boundary (perimeter) length shared between different -planning units (i.e. planning units that are adjacent to each other). -See the Data format section for more information.

    - -

    Value

    - -

    tibble::tibble() object containing the boundary length of the +planning units (i.e., planning units that are adjacent to each other). +See the Data format section for more information.

    +
    +
    +

    Value

    +

    tibble::tibble() object containing the boundary length of the solution. -It contains the following columns:

    -
    - -
    summary

    character description of the summary statistic. +It contains the following columns:

    summary
    +

    character description of the summary statistic. The statistic associated with the "overall" value in this column is calculated using the entire solution (including all management zones if there are multiple zones). @@ -279,78 +180,85 @@

    Value

    are also provided for each zone separately (indicated using zone names).

    -
    boundary

    numeric exposed boundary length value. + +

    boundary
    +

    numeric exposed boundary length value. Greater values correspond to solutions with greater boundary length and, in turn, greater spatial fragmentation. Thus conservation planning exercises typically prefer solutions with smaller values.

    -
    - -

    Details

    +
    +
    +

    Details

    This summary statistic is equivalent to the Connectivity_Edge metric -reported by the Marxan software +reported by the Marxan software (Ball et al. 2009). It is calculated using the same equations used to penalize solutions -according to their total exposed boundary (i.e. add_boundary_penalties()). +according to their total exposed boundary (i.e., add_boundary_penalties()). See the Examples section for examples on how differences zone arguments can be used to calculate boundaries for different combinations of zones.

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data can be specified using the following formats. Note that boundary data must always describe symmetric relationships between planning units.

    -
    - -
    data as a NULL value

    indicating that the data should be -automatically calculated using the boundary_matrix() function. +

    data as a NULL value
    +

    indicating that the data should be +automatically calculated using the boundary_matrix() function. This argument is the default. Note that the boundary data must be supplied using one of the other formats below if the planning unit data in the argument to x do not explicitly contain spatial information -(e.g. planning unit data are a data.frame or numeric class).

    +(e.g., planning unit data are a data.frame or numeric class).

    -
    data as a matrix/Matrix object

    where rows and columns represent + +

    data as a matrix/Matrix object
    +

    where rows and columns represent different planning units and the value of each cell represents the amount of shared boundary length between two different planning units. Cells that occur along the matrix diagonal represent the amount of exposed boundary associated with each planning unit that has -no neighbor (e.g. these value might pertain to boundaries along a +no neighbor (e.g., these value might pertain to boundaries along a coastline).

    -
    data as a data.frame object

    with the columns "id1", + +

    data as a data.frame object
    +

    with the columns "id1", "id2", and "boundary". The "id1" and "id2" columns contain identifiers (indices) for a pair of planning units, and the "boundary" column contains the amount of shared boundary length between these two planning units. This format follows the the standard Marxan format for boundary -data (i.e. per the "bound.dat" file).

    +data (i.e., per the "bound.dat" file).

    -
    - -

    Solution format

    +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -358,8 +266,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -367,35 +277,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -404,215 +320,214 @@

    References

    +

    +
    +

    References

    Ball IR, Possingham HP, and Watts M (2009) Marxan and relatives: Software for spatial conservation prioritisation in Spatial conservation prioritisation: Quantitative methods and computational tools. Eds Moilanen A, Wilson KA, and Possingham HP. Oxford University Press, Oxford, UK.

    -

    See also

    - -

    See summaries for an overview of all functions for summarizing solutions. -Also, see add_boundary_penalties() to penalize solutions with high +

    +
    +

    See also

    +

    See summaries for an overview of all functions for summarizing solutions. +Also, see add_boundary_penalties() to penalize solutions with high boundary length.

    Other summaries: -eval_connectivity_summary(), -eval_cost_summary(), -eval_feature_representation_summary(), -eval_n_summary(), -eval_target_coverage_summary()

    - -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_sf, sim_features,
    -     sim_pu_zones_sf, sim_features_zones)
    -
    -# build minimal conservation problem with raster data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate boundary associated with the solution
    -r1 <- eval_boundary_summary(p1, s1)
    -print(r1)
    -#> # A tibble: 1 × 2
    -#>   summary boundary
    -#>   <chr>      <dbl>
    -#> 1 overall     2.25
    -
    -# build minimal conservation problem with polygon (sf) data
    -p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print first six rows of the attribute table
    -print(head(s2))
    -#> Simple feature collection with 6 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>       cost locked_in locked_out solution_1                       geometry
    -#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# plot solution
    -plot(s2[, "solution_1"])
    -
    -
    -# calculate boundary associated with the solution
    -r2 <- eval_boundary_summary(p2, s2[, "solution_1"])
    -print(r2)
    -#> # A tibble: 1 × 2
    -#>   summary boundary
    -#>   <chr>      <dbl>
    -#> 1 overall     2.05
    -
    -# build multi-zone conservation problem with polygon (sf) data
    -p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s3 <- solve(p3)
    -
    -# print first six rows of the attribute table
    -print(head(s3))
    -#> Simple feature collection with 6 features and 9 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    -#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    -#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    -#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    -#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    -#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    -#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    -#>   solution_1_zone_2 solution_1_zone_3                       geometry
    -#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s3$solution <- category_vector(
    -  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -s3$solution <- factor(s3$solution)
    -
    -# plot solution
    -plot(s3[, "solution"])
    -
    -
    -# calculate boundary associated with the solution
    -# here we will use the default argument for zones which treats each
    -# zone as completely separate, meaning that the "overall"
    -# boundary is just the sum of the boundaries for each zone
    -r3 <- eval_boundary_summary(
    -  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -print(r3)
    -#> # A tibble: 4 × 2
    -#>   summary boundary
    -#>   <chr>      <dbl>
    -#> 1 overall     14.2
    -#> 2 zone_1       4.7
    -#> 3 zone_2       4.9
    -#> 4 zone_3       4.6
    -
    -# let's calculate the overall exposed boundary across the entire
    -# solution, assuming that the shared boundaries between planning
    -# units allocated to different zones "count" just as much
    -# as those for planning units allocated to the same zone
    -
    -# in other words, let's calculate the overall exposed boundary
    -# across the entire solution by "combining" all selected planning units
    -# together (regardless of which zone they are allocated to in the solution)
    -r3_combined <- eval_boundary_summary(
    -  p3, zones = matrix(1, ncol = 3, nrow = 3),
    -  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -print(r3_combined)
    -#> # A tibble: 4 × 2
    -#>   summary boundary
    -#>   <chr>      <dbl>
    -#> 1 overall     8.80
    -#> 2 zone_1      4.7 
    -#> 3 zone_2      4.9 
    -#> 4 zone_3      4.6 
    -
    -# we can see that the "overall" boundary is now less than the
    -# sum of the individual zone boundaries, because it does not
    -# consider the shared boundary between two planning units allocated to
    -# different zones as "exposed" when performing the calculations
    -# }
    +eval_connectivity_summary(),
    +eval_cost_summary(),
    +eval_feature_representation_summary(),
    +eval_n_summary(),
    +eval_target_coverage_summary()

    +
    + +
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_sf, sim_features,
    +     sim_pu_zones_sf, sim_features_zones)
    +
    +# build minimal conservation problem with raster data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate boundary associated with the solution
    +r1 <- eval_boundary_summary(p1, s1)
    +print(r1)
    +#> # A tibble: 1 × 2
    +#>   summary boundary
    +#>   <chr>      <dbl>
    +#> 1 overall     2.25
    +
    +# build minimal conservation problem with polygon (sf) data
    +p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print first six rows of the attribute table
    +print(head(s2))
    +#> Simple feature collection with 6 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>       cost locked_in locked_out solution_1                       geometry
    +#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# plot solution
    +plot(s2[, "solution_1"])
    +
    +
    +# calculate boundary associated with the solution
    +r2 <- eval_boundary_summary(p2, s2[, "solution_1"])
    +print(r2)
    +#> # A tibble: 1 × 2
    +#>   summary boundary
    +#>   <chr>      <dbl>
    +#> 1 overall     2.05
    +
    +# build multi-zone conservation problem with polygon (sf) data
    +p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s3 <- solve(p3)
    +
    +# print first six rows of the attribute table
    +print(head(s3))
    +#> Simple feature collection with 6 features and 9 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    +#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    +#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    +#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    +#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    +#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    +#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    +#>   solution_1_zone_2 solution_1_zone_3                       geometry
    +#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s3$solution <- category_vector(
    +  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +s3$solution <- factor(s3$solution)
    +
    +# plot solution
    +plot(s3[, "solution"])
    +
    +
    +# calculate boundary associated with the solution
    +# here we will use the default argument for zones which treats each
    +# zone as completely separate, meaning that the "overall"
    +# boundary is just the sum of the boundaries for each zone
    +r3 <- eval_boundary_summary(
    +  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +print(r3)
    +#> # A tibble: 4 × 2
    +#>   summary boundary
    +#>   <chr>      <dbl>
    +#> 1 overall     14.2
    +#> 2 zone_1       4.7
    +#> 3 zone_2       4.9
    +#> 4 zone_3       4.6
    +
    +# let's calculate the overall exposed boundary across the entire
    +# solution, assuming that the shared boundaries between planning
    +# units allocated to different zones "count" just as much
    +# as those for planning units allocated to the same zone
    +
    +# in other words, let's calculate the overall exposed boundary
    +# across the entire solution by "combining" all selected planning units
    +# together (regardless of which zone they are allocated to in the solution)
    +r3_combined <- eval_boundary_summary(
    +  p3, zones = matrix(1, ncol = 3, nrow = 3),
    +  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +print(r3_combined)
    +#> # A tibble: 4 × 2
    +#>   summary boundary
    +#>   <chr>      <dbl>
    +#> 1 overall     8.80
    +#> 2 zone_1      4.7 
    +#> 3 zone_2      4.9 
    +#> 4 zone_3      4.6 
    +
    +# we can see that the "overall" boundary is now less than the
    +# sum of the individual zone boundaries, because it does not
    +# consider the shared boundary between two planning units allocated to
    +# different zones as "exposed" when performing the calculations
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/eval_connectivity_summary.html b/docs/reference/eval_connectivity_summary.html index a71f18236..5cee66282 100644 --- a/docs/reference/eval_connectivity_summary.html +++ b/docs/reference/eval_connectivity_summary.html @@ -1,104 +1,21 @@ - - - - - - - -Evaluate solution connectivity — eval_connectivity_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solution connectivity — eval_connectivity_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Calculate the connectivity held within a solution to a conservation -planning problem(). +planning problem(). This summary statistic evaluates the connectivity of a solution using pair-wise connectivity values between combinations of planning units.

    -
    # S4 method for ConservationProblem,ANY,ANY,matrix
    -eval_connectivity_summary(x, solution, zones, data)
    -
    -# S4 method for ConservationProblem,ANY,ANY,Matrix
    -eval_connectivity_summary(x, solution, zones, data)
    -
    -# S4 method for ConservationProblem,ANY,ANY,data.frame
    -eval_connectivity_summary(x, solution, zones, data)
    -
    -# S4 method for ConservationProblem,ANY,ANY,dgCMatrix
    -eval_connectivity_summary(x, solution, zones, data)
    -
    -# S4 method for ConservationProblem,ANY,ANY,array
    -eval_connectivity_summary(x, solution, zones, data)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. +

    Usage,
    # S4 method for ConservationProblem,ANY,ANY,matrix
    +eval_connectivity_summary(x, solution, zones, data)
    +
    +# S4 method for ConservationProblem,ANY,ANY,Matrix
    +eval_connectivity_summary(x, solution, zones, data)
    +
    +# S4 method for ConservationProblem,ANY,ANY,data.frame
    +eval_connectivity_summary(x, solution, zones, data)
    +
    +# S4 method for ConservationProblem,ANY,ANY,dgCMatrix
    +eval_connectivity_summary(x, solution, zones, data)
    +
    +# S4 method for ConservationProblem,ANY,ANY,array
    +eval_connectivity_summary(x, solution, zones, data)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. The argument should be in the same format as the planning unit cost data in the argument to x. -See the Solution format section for more information.

    zones

    matrix or Matrix object describing the +See the Solution format section for more information.

    +
    zones
    +

    matrix or Matrix object describing the level of connectivity between different zones. Each row and column corresponds to a different zone in the argument to x, and cell values indicate the level of connectivity between each combination @@ -231,33 +142,27 @@

    Arg the level of connectivity between planning units allocated to the same zone. Cell values must lay between 1 and -1, where negative values favor solutions with weak connectivity. The default argument to -zones is an identity matrix (i.e. a matrix with ones along the +zones is an identity matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered to be connected when they are allocated to the same zone. This argument is required when the argument to data is a matrix or Matrix object. If the argument to data is an array or data.frame with zone data, this argument -must explicitly be set to NULL otherwise an error will be thrown.

    data

    matrix, Matrix, data.frame, or +must explicitly be set to NULL otherwise an error will be thrown.

    +
    data
    +

    matrix, Matrix, data.frame, or array object containing connectivity data. The connectivity values correspond to the strength of connectivity between different planning units. Thus connections between planning units that are associated with higher values are more favorable in the solution. -See the Data format section for more information.

    - -

    Value

    - -

    tibble::tibble() object describing the connectivity of the +See the Data format section for more information.

    +
    +
    +

    Value

    +

    tibble::tibble() object describing the connectivity of the solution. -It contains the following columns:

    -
    - -
    summary

    character description of the summary statistic. +It contains the following columns:

    summary
    +

    character description of the summary statistic. The statistic associated with the "overall" value in this column is calculated using the entire solution (including all management zones if there are multiple zones). @@ -265,43 +170,47 @@

    Value

    are also provided for each zone separately (indicated using zone names).

    -
    connectivity

    numeric connectivity value. + +

    connectivity
    +

    numeric connectivity value. Greater values correspond to solutions associated with greater connectivity. Thus conservation planning exercises typically prefer solutions with greater values.

    -
    - -

    Details

    +
    +
    +

    Details

    This summary statistic is comparable to the Connectivity_In metric reported by the -Marxan software (Ball et al. 2009). +Marxan software (Ball et al. 2009). It is calculated using the same equations used to penalize solutions -with connectivity data (i.e. add_connectivity_penalties()). +with connectivity data (i.e., add_connectivity_penalties()). Specifically, it is calculated as the sum of the pair-wise connectivity values in the argument to data, weighted by the value of the planning units in the solution.

    -

    Solution format

    - +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -309,8 +218,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -318,35 +229,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -355,18 +272,17 @@

    Data format

    +

    +
    +

    Data format

    The argument to data can be specified using several different formats. These formats can be used to describe symmetric or asymmetric relationships between planning units.

    -
    - -
    data as a matrix/Matrix object

    where rows and columns represent +

    data as a matrix/Matrix object
    +

    where rows and columns represent different planning units and the value of each cell represents the strength of connectivity between two different planning units. Cells that occur along the matrix diagonal are treated as weights which @@ -376,13 +292,15 @@

    zones is to treat planning units allocated to different zones as having zero connectivity.

    -
    data as a data.frame object

    containing the fields (columns) + +

    data as a data.frame object
    +

    containing the fields (columns) "id1", "id2", and "boundary". Here, each row denotes the connectivity between two planning units following the Marxan format. The data can be used to denote symmetric or asymmetric relationships between planning units. By default, input data is assumed to be symmetric unless asymmetric data is -also included (e.g. if data is present for planning units 2 and 3, then +also included (e.g., if data is present for planning units 2 and 3, then the same amount of connectivity is expected for planning units 3 and 2, unless connectivity data is also provided for planning units 3 and 2). If the argument to x contains multiple zones, then the columns @@ -392,10 +310,12 @@

    "zone2" are present, then the argument to zones must be NULL.

    -
    data as an array object

    containing four-dimensions where cell values + +

    data as an array object
    +

    containing four-dimensions where cell values indicate the strength of connectivity between planning units when they are assigned to specific management zones. The first two -dimensions (i.e. rows and columns) indicate the strength of +dimensions (i.e., rows and columns) indicate the strength of connectivity between different planning units and the second two dimensions indicate the different management zones. Thus the data[1, 2, 3, 4] indicates the strength of @@ -403,209 +323,208 @@

    -
    - -

    References

    +
    +
    +

    References

    Ball IR, Possingham HP, and Watts M (2009) Marxan and relatives: Software for spatial conservation prioritisation in Spatial conservation prioritisation: Quantitative methods and computational tools. Eds Moilanen A, Wilson KA, and Possingham HP. Oxford University Press, Oxford, UK.

    -

    See also

    - -

    See summaries for an overview of all functions for summarizing solutions. -Also, see add_connectivity_penalties() to penalize solutions with low +

    +
    +

    See also

    +

    See summaries for an overview of all functions for summarizing solutions. +Also, see add_connectivity_penalties() to penalize solutions with low connectivity.

    Other summaries: -eval_boundary_summary(), -eval_cost_summary(), -eval_feature_representation_summary(), -eval_n_summary(), -eval_target_coverage_summary()

    - -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_sf, sim_features,
    -     sim_pu_zones_sf, sim_features_zones)
    -
    -# build minimal conservation problem with raster data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# simulate a connectivity matrix to describe the relative strength
    -# of connectivity between different planning units
    -# for brevity, we will use cost data here so that pairs
    -# of adjacent planning units with higher cost values will have a
    -# higher connectivity value
    -# (but see ?connectivity_matrix for more information)
    -cm1 <- connectivity_matrix(sim_pu_raster, sim_pu_raster)
    -
    -# calculate connectivity associated with the solution
    -r1 <- eval_connectivity_summary(p1, s1, data = cm1)
    -print(r1)
    -#> # A tibble: 1 × 2
    -#>   summary connectivity
    -#>   <chr>          <dbl>
    -#> 1 overall         198.
    -
    -# build minimal conservation problem with polygon (sf) data
    -p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print first six rows of the attribute table
    -print(head(s2))
    -#> Simple feature collection with 6 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>       cost locked_in locked_out solution_1                       geometry
    -#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# plot solution
    -plot(s2[, "solution_1"])
    -
    -
    -# simulate connectivity matrix
    -# here, we will generate connectivity values randomly
    -# between all pairs of planning units
    -cm2 <- matrix(runif(nrow(sim_pu_sf) ^ 2), nrow = nrow(sim_pu_sf))
    -
    -# calculate connectivity associated with the solution
    -r2 <- eval_connectivity_summary(p2, s2[, "solution_1"], data = cm2)
    -print(r2)
    -#> # A tibble: 1 × 2
    -#>   summary connectivity
    -#>   <chr>          <dbl>
    -#> 1 overall         36.8
    -
    -# build multi-zone conservation problem with polygon (sf) data
    -p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s3 <- solve(p3)
    -
    -# print first six rows of the attribute table
    -print(head(s3))
    -#> Simple feature collection with 6 features and 9 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    -#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    -#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    -#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    -#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    -#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    -#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    -#>   solution_1_zone_2 solution_1_zone_3                       geometry
    -#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2                 0                 0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4                 0                 0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5                 0                 0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s3$solution <- category_vector(
    -  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -s3$solution <- factor(s3$solution)
    -
    -# plot solution
    -plot(s3[, "solution"])
    -
    -
    -# simulate connectivity matrix
    -# here, we will add a new column to sim_pu_zones_sf with
    -# randomly simulated values and create a connectivity matrix
    -# based on the average simulated values of adjacent planning units
    -sim_pu_zones_sf$con <- runif(nrow(sim_pu_zones_sf))
    -cm3 <- connectivity_matrix(sim_pu_zones_sf, "con")
    -
    -# calculate connectivity associated with the solution
    -r3 <- eval_connectivity_summary(
    -  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")],
    -  data = cm3)
    -print(r3)
    -#> # A tibble: 4 × 2
    -#>   summary connectivity
    -#>   <chr>          <dbl>
    -#> 1 overall        2.93 
    -#> 2 zone_1         1.05 
    -#> 3 zone_2         0.966
    -#> 4 zone_3         0.912
    -
    -# }
    +eval_boundary_summary(),
    +eval_cost_summary(),
    +eval_feature_representation_summary(),
    +eval_n_summary(),
    +eval_target_coverage_summary()

    +
    + +
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_sf, sim_features,
    +     sim_pu_zones_sf, sim_features_zones)
    +
    +# build minimal conservation problem with raster data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# simulate a connectivity matrix to describe the relative strength
    +# of connectivity between different planning units
    +# for brevity, we will use cost data here so that pairs
    +# of adjacent planning units with higher cost values will have a
    +# higher connectivity value
    +# (but see ?connectivity_matrix for more information)
    +cm1 <- connectivity_matrix(sim_pu_raster, sim_pu_raster)
    +
    +# calculate connectivity associated with the solution
    +r1 <- eval_connectivity_summary(p1, s1, data = cm1)
    +print(r1)
    +#> # A tibble: 1 × 2
    +#>   summary connectivity
    +#>   <chr>          <dbl>
    +#> 1 overall         198.
    +
    +# build minimal conservation problem with polygon (sf) data
    +p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print first six rows of the attribute table
    +print(head(s2))
    +#> Simple feature collection with 6 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>       cost locked_in locked_out solution_1                       geometry
    +#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# plot solution
    +plot(s2[, "solution_1"])
    +
    +
    +# simulate connectivity matrix
    +# here, we will generate connectivity values randomly
    +# between all pairs of planning units
    +cm2 <- matrix(runif(nrow(sim_pu_sf) ^ 2), nrow = nrow(sim_pu_sf))
    +
    +# calculate connectivity associated with the solution
    +r2 <- eval_connectivity_summary(p2, s2[, "solution_1"], data = cm2)
    +print(r2)
    +#> # A tibble: 1 × 2
    +#>   summary connectivity
    +#>   <chr>          <dbl>
    +#> 1 overall         36.8
    +
    +# build multi-zone conservation problem with polygon (sf) data
    +p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s3 <- solve(p3)
    +
    +# print first six rows of the attribute table
    +print(head(s3))
    +#> Simple feature collection with 6 features and 9 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    +#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    +#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    +#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    +#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    +#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    +#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    +#>   solution_1_zone_2 solution_1_zone_3                       geometry
    +#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2                 0                 0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4                 0                 0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5                 0                 0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s3$solution <- category_vector(
    +  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +s3$solution <- factor(s3$solution)
    +
    +# plot solution
    +plot(s3[, "solution"])
    +
    +
    +# simulate connectivity matrix
    +# here, we will add a new column to sim_pu_zones_sf with
    +# randomly simulated values and create a connectivity matrix
    +# based on the average simulated values of adjacent planning units
    +sim_pu_zones_sf$con <- runif(nrow(sim_pu_zones_sf))
    +cm3 <- connectivity_matrix(sim_pu_zones_sf, "con")
    +
    +# calculate connectivity associated with the solution
    +r3 <- eval_connectivity_summary(
    +  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")],
    +  data = cm3)
    +print(r3)
    +#> # A tibble: 4 × 2
    +#>   summary connectivity
    +#>   <chr>          <dbl>
    +#> 1 overall        2.93 
    +#> 2 zone_1         1.05 
    +#> 3 zone_2         0.966
    +#> 4 zone_3         0.912
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/eval_cost_summary.html b/docs/reference/eval_cost_summary.html index 3ad1590f1..4aa5b2e1a 100644 --- a/docs/reference/eval_cost_summary.html +++ b/docs/reference/eval_cost_summary.html @@ -1,105 +1,22 @@ - - - - - - - -Evaluate solution cost — eval_cost_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solution cost — eval_cost_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Calculate the total cost of a solution to a conservation planning -problem(). +problem(). For example, if the planning unit cost data describe land acquisition costs (USD), then the total cost would be net cost (USD) needed to acquire all planning units selected within the solution.

    -
    eval_cost_summary(x, solution)
    -
    -# S3 method for default
    -eval_cost_summary(x, solution)
    -
    -# S3 method for ConservationProblem
    -eval_cost_summary(x, solution)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. -The argument should be in the same format as the planning unit cost -data in the argument to x. -See the Solution format section for more information.

    +
    Usage,
    eval_cost_summary(x, solution)
     
    -    

    Value

    +# S3 method for default +eval_cost_summary(x, solution) -

    tibble::tibble() object containing the solution cost. -It contains the following columns:

    -
    +# S3 method for ConservationProblem +eval_cost_summary(x, solution)
    -
    summary

    character description of the summary statistic. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. +The argument should be in the same format as the planning unit cost +data in the argument to x. +See the Solution format section for more information.

    +
    +
    +

    Value

    +

    tibble::tibble() object containing the solution cost. +It contains the following columns:

    summary
    +

    character description of the summary statistic. The statistic associated with the "overall" value in this column is calculated using the entire solution (including all management zones if there are multiple zones). @@ -232,7 +141,9 @@

    Value

    are also provided for each zone separately (indicated using zone names).

    -
    cost

    numeric cost value. + +

    cost
    +

    numeric cost value. Greater values correspond to solutions that are more costly to implement. Thus conservation planning exercises typically prefer solutions @@ -241,34 +152,36 @@

    Value

    are equal).

    -
    - -

    Details

    +
    +
    +

    Details

    This metric is equivalent to the Cost metric reported by the -Marxan software (Ball et al. 2009). +Marxan software (Ball et al. 2009). Specifically, the cost of a solution is defined as the sum of the cost -values, supplied when creating a problem() object -(e.g. using the cost_column argument), +values, supplied when creating a problem() object +(e.g., using the cost_column argument), weighted by the status of each planning unit in the solution.

    -

    Solution format

    - +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -276,8 +189,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -285,35 +200,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -322,190 +243,189 @@

    References

    +

    +
    +

    References

    Ball IR, Possingham HP, and Watts M (2009) Marxan and relatives: Software for spatial conservation prioritisation in Spatial conservation prioritisation: Quantitative methods and computational tools. Eds Moilanen A, Wilson KA, and Possingham HP. Oxford University Press, Oxford, UK.

    -

    See also

    - -

    See summaries for an overview of all functions for summarizing solutions.

    +
    +
    +

    See also

    +

    See summaries for an overview of all functions for summarizing solutions.

    Other summaries: -eval_boundary_summary(), -eval_connectivity_summary(), -eval_feature_representation_summary(), -eval_n_summary(), -eval_target_coverage_summary()

    - -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_sf, sim_features,
    -     sim_pu_zones_sf, sim_features_zones)
    -
    -# build minimal conservation problem with raster data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate cost of the solution
    -r1 <- eval_cost_summary(p1, s1)
    -print(r1)
    -#> # A tibble: 1 × 2
    -#>   summary  cost
    -#>   <chr>   <dbl>
    -#> 1 overall 1987.
    -
    -# build minimal conservation problem with polygon (sf) data
    -p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(s2[, "solution_1"])
    -
    -
    -# print first six rows of the attribute table
    -print(head(s2))
    -#> Simple feature collection with 6 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>       cost locked_in locked_out solution_1                       geometry
    -#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# calculate cost of the solution
    -r2 <- eval_cost_summary(p2, s2[, "solution_1"])
    -print(r2)
    -#> # A tibble: 1 × 2
    -#>   summary  cost
    -#>   <chr>   <dbl>
    -#> 1 overall 1793.
    -
    -# manually calculate cost of the solution
    -r2_manual <- sum(s2$solution * sim_pu_sf$cost, na.rm = TRUE)
    -print(r2_manual)
    -#> [1] 1792.535
    -
    -# build multi-zone conservation problem with polygon (sf) data
    -p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s3 <- solve(p3)
    -
    -# print first six rows of the attribute table
    -print(head(s3))
    -#> Simple feature collection with 6 features and 9 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    -#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    -#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    -#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    -#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    -#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    -#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    -#>   solution_1_zone_2 solution_1_zone_3                       geometry
    -#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s3$solution <- category_vector(
    -  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -s3$solution <- factor(s3$solution)
    -
    -# plot solution
    -plot(s3[, "solution"])
    -
    -
    -# calculate cost of the solution
    -r3 <- eval_cost_summary(
    -  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -print(r3)
    -#> # A tibble: 4 × 2
    -#>   summary   cost
    -#>   <chr>    <dbl>
    -#> 1 overall 10559.
    -#> 2 zone_1   3409.
    -#> 3 zone_2   3513.
    -#> 4 zone_3   3638.
    -# }
    +eval_boundary_summary(),
    +eval_connectivity_summary(),
    +eval_feature_representation_summary(),
    +eval_n_summary(),
    +eval_target_coverage_summary()

    +
    + +
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_sf, sim_features,
    +     sim_pu_zones_sf, sim_features_zones)
    +
    +# build minimal conservation problem with raster data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate cost of the solution
    +r1 <- eval_cost_summary(p1, s1)
    +print(r1)
    +#> # A tibble: 1 × 2
    +#>   summary  cost
    +#>   <chr>   <dbl>
    +#> 1 overall 1987.
    +
    +# build minimal conservation problem with polygon (sf) data
    +p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(s2[, "solution_1"])
    +
    +
    +# print first six rows of the attribute table
    +print(head(s2))
    +#> Simple feature collection with 6 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>       cost locked_in locked_out solution_1                       geometry
    +#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# calculate cost of the solution
    +r2 <- eval_cost_summary(p2, s2[, "solution_1"])
    +print(r2)
    +#> # A tibble: 1 × 2
    +#>   summary  cost
    +#>   <chr>   <dbl>
    +#> 1 overall 1793.
    +
    +# manually calculate cost of the solution
    +r2_manual <- sum(s2$solution * sim_pu_sf$cost, na.rm = TRUE)
    +print(r2_manual)
    +#> [1] 1792.535
    +
    +# build multi-zone conservation problem with polygon (sf) data
    +p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s3 <- solve(p3)
    +
    +# print first six rows of the attribute table
    +print(head(s3))
    +#> Simple feature collection with 6 features and 9 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    +#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    +#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    +#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    +#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    +#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    +#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    +#>   solution_1_zone_2 solution_1_zone_3                       geometry
    +#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s3$solution <- category_vector(
    +  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +s3$solution <- factor(s3$solution)
    +
    +# plot solution
    +plot(s3[, "solution"])
    +
    +
    +# calculate cost of the solution
    +r3 <- eval_cost_summary(
    +  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +print(r3)
    +#> # A tibble: 4 × 2
    +#>   summary   cost
    +#>   <chr>    <dbl>
    +#> 1 overall 10559.
    +#> 2 zone_1   3409.
    +#> 3 zone_2   3513.
    +#> 4 zone_3   3638.
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/eval_feature_representation_summary-2.png b/docs/reference/eval_feature_representation_summary-2.png index f22a803d8034533e7953c92efc1a720fd13e7dbf..dc4e80232bd8cc3945cf5b2a4bb29efb8da0458a 100644 GIT binary patch literal 16601 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
    pPnCvL$ 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 HeJ=eAy~hE% literal 16214 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 - - - - - - -Evaluate feature representation — eval_feature_representation_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate feature representation — eval_feature_representation_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - + diff --git a/docs/reference/eval_ferrier_importance.html b/docs/reference/eval_ferrier_importance.html index 19b65be36..8d64635e1 100644 --- a/docs/reference/eval_ferrier_importance.html +++ b/docs/reference/eval_ferrier_importance.html @@ -1,102 +1,19 @@ - - - - - - - -Evaluate solution importance using Ferrier scores — eval_ferrier_importance • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solution importance using Ferrier scores — eval_ferrier_importance • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,55 +103,50 @@

    Evaluate solution importance using Ferrier scores

    a solution following Ferrier et al. (2000).

    -
    eval_ferrier_importance(x, solution)
    +    
    Usage,
    eval_ferrier_importance(x, solution)
     
    -# S4 method for ConservationProblem,numeric
    -eval_ferrier_importance(x, solution)
    +# S4 method for ConservationProblem,numeric
    +eval_ferrier_importance(x, solution)
     
    -# S4 method for ConservationProblem,matrix
    -eval_ferrier_importance(x, solution)
    +# S4 method for ConservationProblem,matrix
    +eval_ferrier_importance(x, solution)
     
    -# S4 method for ConservationProblem,data.frame
    -eval_ferrier_importance(x, solution)
    +# S4 method for ConservationProblem,data.frame
    +eval_ferrier_importance(x, solution)
     
    -# S4 method for ConservationProblem,Spatial
    -eval_ferrier_importance(x, solution)
    +# S4 method for ConservationProblem,Spatial
    +eval_ferrier_importance(x, solution)
     
    -# S4 method for ConservationProblem,sf
    -eval_ferrier_importance(x, solution)
    +# S4 method for ConservationProblem,sf
    +eval_ferrier_importance(x, solution)
     
    -# S4 method for ConservationProblem,Raster
    -eval_ferrier_importance(x, solution)
    +# S4 method for ConservationProblem,Raster +eval_ferrier_importance(x, solution)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. The argument should be in the same format as the planning unit cost data in the argument to x. -See the Solution format section for more information.

    - -

    Value

    - -

    A matrix, tibble::tibble(), -RasterLayer, or -Spatial object containing the scores for each +See the Solution format section for more information.

    +
    +
    +

    Value

    +

    A matrix, tibble::tibble(), +RasterLayer, or +Spatial object containing the scores for each planning unit selected in the solution. Specifically, the returned object is in the same format (except if the planning units are a numeric vector) as the planning unit data in the argument to x.

    -

    Details

    - +
    +
    +

    Details

    Importance scores are reported separately for each feature within each planning unit. Additionally, a total importance score is also calculated as the sum of the scores for each feature. @@ -245,8 +157,9 @@

    Details the mathematical formulation for computing these scores needs verification, and so this functionality should be considered experimental at this point in time.

    -

    Notes

    - +
    +
    +

    Notes

    In previous versions, the documentation for this function had a warning @@ -254,24 +167,26 @@

    Notes

    verification. The mathematical formulation for this function has since been corrected and verified, so now this function is recommended for general use.

    -

    Solution format

    - +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -279,8 +194,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -288,35 +205,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -325,145 +248,144 @@

    References

    +

    +
    +

    References

    Ferrier S, Pressey RL, and Barrett TW (2000) A new predictor of the irreplaceability of areas for achieving a conservation goal, its application to real-world planning, and a research agenda for further refinement. Biological Conservation, 93: 303--325.

    -

    See also

    - -

    See importance for an overview of all functions for evaluating +

    +
    +

    See also

    +

    See importance for an overview of all functions for evaluating the importance of planning units selected in a solution.

    Other importances: -eval_rare_richness_importance(), -eval_replacement_importance()

    - -

    Examples

    -
    # seed seed for reproducibility
    -set.seed(600)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create minimal problem with binary decisions
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate importance scores using Ferrier et al. 2000 method
    -fs1 <- eval_ferrier_importance(p1, s1)
    -
    -# print importance scores,
    -# each planning unit has an importance score for each feature
    -# (as indicated by the column names) and each planning unit also
    -# has an overall total importance score (in the "total" column)
    -print(fs1)
    -#> class      : RasterStack 
    -#> dimensions : 10, 10, 100, 6  (nrow, ncol, ncell, nlayers)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> names      :     layer.1,     layer.2,     layer.3,     layer.4,     layer.5,       total 
    -#> min values :           0,           0,           0,           0,           0,           0 
    -#> max values : 0.003472042, 0.003596401, 0.003341572, 0.003768489, 0.003504223, 0.016417187 
    -#> 
    -
    -# plot total importance scores
    -plot(fs1, main = "Ferrier scores", axes = FALSE, box = FALSE)
    -
    -
    -# create minimal problem with polygon (sf) planning units
    -p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.05) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem
    -s2 <- solve(p2)
    -
    -# print solution
    -print(s2)
    -#> Simple feature collection with 90 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 1
    -#> CRS:           NA
    -#> First 10 features:
    -#>        cost locked_in locked_out solution_1                       geometry
    -#> 1  215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2  212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3  207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4  208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5  214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6  213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -#> 7  210.4612     FALSE      FALSE          0 POLYGON ((0.6 1, 0.7 1, 0.7...
    -#> 8  211.0424     FALSE       TRUE          0 POLYGON ((0.7 1, 0.8 1, 0.8...
    -#> 9  210.3878     FALSE      FALSE          0 POLYGON ((0.8 1, 0.9 1, 0.9...
    -#> 10 204.3971     FALSE      FALSE          0 POLYGON ((0.9 1, 1 1, 1 0.9...
    -
    -# plot solution
    -plot(s2[, "solution_1"], main = "solution")
    -
    -
    -# calculate importance scores
    -fs2 <- eval_ferrier_importance(p2, s2[, "solution_1"])
    -
    -# plot importance scores
    -plot(fs2, main = "Ferrier scores")
    -
    -
    -# }
    -
    +eval_rare_richness_importance(),
    +eval_replacement_importance()

    +
    + +
    +

    Examples

    +
    # seed seed for reproducibility
    +set.seed(600)
    +
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create minimal problem with binary decisions
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate importance scores using Ferrier et al. 2000 method
    +fs1 <- eval_ferrier_importance(p1, s1)
    +
    +# print importance scores,
    +# each planning unit has an importance score for each feature
    +# (as indicated by the column names) and each planning unit also
    +# has an overall total importance score (in the "total" column)
    +print(fs1)
    +#> class      : RasterStack 
    +#> dimensions : 10, 10, 100, 6  (nrow, ncol, ncell, nlayers)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> names      :     layer.1,     layer.2,     layer.3,     layer.4,     layer.5,       total 
    +#> min values :           0,           0,           0,           0,           0,           0 
    +#> max values : 0.003472042, 0.003596401, 0.003341572, 0.003768489, 0.003504223, 0.016417187 
    +#> 
    +
    +# plot total importance scores
    +plot(fs1, main = "Ferrier scores", axes = FALSE, box = FALSE)
    +
    +
    +# create minimal problem with polygon (sf) planning units
    +p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.05) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem
    +s2 <- solve(p2)
    +
    +# print solution
    +print(s2)
    +#> Simple feature collection with 90 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 1
    +#> CRS:           NA
    +#> First 10 features:
    +#>        cost locked_in locked_out solution_1                       geometry
    +#> 1  215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2  212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3  207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4  208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5  214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6  213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +#> 7  210.4612     FALSE      FALSE          0 POLYGON ((0.6 1, 0.7 1, 0.7...
    +#> 8  211.0424     FALSE       TRUE          0 POLYGON ((0.7 1, 0.8 1, 0.8...
    +#> 9  210.3878     FALSE      FALSE          0 POLYGON ((0.8 1, 0.9 1, 0.9...
    +#> 10 204.3971     FALSE      FALSE          0 POLYGON ((0.9 1, 1 1, 1 0.9...
    +
    +# plot solution
    +plot(s2[, "solution_1"], main = "solution")
    +
    +
    +# calculate importance scores
    +fs2 <- eval_ferrier_importance(p2, s2[, "solution_1"])
    +
    +# plot importance scores
    +plot(fs2, main = "Ferrier scores")
    +
    +
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/eval_n_summary.html b/docs/reference/eval_n_summary.html index 0fa02a224..7d590c6c1 100644 --- a/docs/reference/eval_n_summary.html +++ b/docs/reference/eval_n_summary.html @@ -1,102 +1,19 @@ - - - - - - - -Evaluate number of planning units selected — eval_n_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate number of planning units selected — eval_n_summary • prioritizr - - - - - - - - - - - - - - +
    -
    -

    Calculate the number of planning units selected within a solution -to a conservation planning problem().

    +to a conservation planning problem().

    -
    eval_n_summary(x, solution)
    -
    -# S3 method for default
    -eval_n_summary(x, solution)
    -
    -# S3 method for ConservationProblem
    -eval_n_summary(x, solution)
    - -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. -The argument should be in the same format as the planning unit cost -data in the argument to x. -See the Solution format section for more information.

    +
    Usage,
    eval_n_summary(x, solution)
     
    -    

    Value

    +# S3 method for default +eval_n_summary(x, solution) -

    tibble::tibble() object containing the number of planning -units selected within a solution. -It contains the following columns:

    -
    +# S3 method for ConservationProblem +eval_n_summary(x, solution)
    -
    summary

    character description of the summary statistic. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. +The argument should be in the same format as the planning unit cost +data in the argument to x. +See the Solution format section for more information.

    +
    +
    +

    Value

    +

    tibble::tibble() object containing the number of planning +units selected within a solution. +It contains the following columns:

    summary
    +

    character description of the summary statistic. The statistic associated with the "overall" value in this column is calculated using the entire solution (including all management zones if there are multiple zones). @@ -227,36 +136,40 @@

    Value

    are also provided for each zone separately (indicated using zone names).

    -
    n

    numeric number of selected planning units.

    +
    n
    +

    numeric number of selected planning units.

    -
    -

    Details

    +
    +
    +

    Details

    This summary statistic is calculated as the sum of the values in the solution. As a consequence, this metric can produce a -non-integer value (e.g. 4.3) for solutions containing proportion values -(e.g. generated by solving a problem() built using the -add_proportion_decisions() function).

    -

    Solution format

    - +non-integer value (e.g., 4.3) for solutions containing proportion values +(e.g., generated by solving a problem() built using the +add_proportion_decisions() function).

    +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -264,8 +177,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -273,35 +188,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -310,184 +231,182 @@

    See also

    -

    See summaries for an overview of all functions for summarizing solutions.

    +

    +
    +

    See also

    +

    See summaries for an overview of all functions for summarizing solutions.

    Other summaries: -eval_boundary_summary(), -eval_connectivity_summary(), -eval_cost_summary(), -eval_feature_representation_summary(), -eval_target_coverage_summary()

    - -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_sf, sim_features,
    -     sim_pu_zones_sf, sim_features_zones)
    -
    -# build minimal conservation problem with raster data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate number of selected planning units within solution
    -r1 <- eval_n_summary(p1, s1)
    -print(r1)
    -#> # A tibble: 1 × 2
    -#>   summary  cost
    -#>   <chr>   <dbl>
    -#> 1 overall    10
    -
    -# build minimal conservation problem with polygon (sf) data
    -p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# plot solution
    -plot(s2[, "solution_1"])
    -
    -
    -# print first six rows of the attribute table
    -print(head(s2))
    -#> Simple feature collection with 6 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>       cost locked_in locked_out solution_1                       geometry
    -#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# calculate number of selected planning units within solution
    -r2 <- eval_n_summary(p2, s2[, "solution_1"])
    -print(r2)
    -#> # A tibble: 1 × 2
    -#>   summary  cost
    -#>   <chr>   <dbl>
    -#> 1 overall     9
    -
    -# manually calculate number of selected planning units
    -r2_manual <- sum(s2$solution, na.rm = TRUE)
    -print(r2_manual)
    -#> [1] 9
    -
    -# build multi-zone conservation problem with polygon (sf) data
    -p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s3 <- solve(p3)
    -
    -# print first six rows of the attribute table
    -print(head(s3))
    -#> Simple feature collection with 6 features and 9 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    -#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    -#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    -#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    -#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    -#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    -#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    -#>   solution_1_zone_2 solution_1_zone_3                       geometry
    -#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s3$solution <- category_vector(
    -  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -s3$solution <- factor(s3$solution)
    -
    -# plot solution
    -plot(s3[, "solution"])
    -
    -
    -# calculate number of selected planning units within solution
    -r3 <- eval_n_summary(
    -  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -print(r3)
    -#> # A tibble: 4 × 2
    -#>   summary  cost
    -#>   <chr>   <dbl>
    -#> 1 overall    52
    -#> 2 zone_1     17
    -#> 3 zone_2     18
    -#> 4 zone_3     17
    -# }
    +eval_boundary_summary(),
    +eval_connectivity_summary(),
    +eval_cost_summary(),
    +eval_feature_representation_summary(),
    +eval_target_coverage_summary()

    +
    + +
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_sf, sim_features,
    +     sim_pu_zones_sf, sim_features_zones)
    +
    +# build minimal conservation problem with raster data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate number of selected planning units within solution
    +r1 <- eval_n_summary(p1, s1)
    +print(r1)
    +#> # A tibble: 1 × 2
    +#>   summary  cost
    +#>   <chr>   <dbl>
    +#> 1 overall    10
    +
    +# build minimal conservation problem with polygon (sf) data
    +p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# plot solution
    +plot(s2[, "solution_1"])
    +
    +
    +# print first six rows of the attribute table
    +print(head(s2))
    +#> Simple feature collection with 6 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>       cost locked_in locked_out solution_1                       geometry
    +#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# calculate number of selected planning units within solution
    +r2 <- eval_n_summary(p2, s2[, "solution_1"])
    +print(r2)
    +#> # A tibble: 1 × 2
    +#>   summary  cost
    +#>   <chr>   <dbl>
    +#> 1 overall     9
    +
    +# manually calculate number of selected planning units
    +r2_manual <- sum(s2$solution, na.rm = TRUE)
    +print(r2_manual)
    +#> [1] 9
    +
    +# build multi-zone conservation problem with polygon (sf) data
    +p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s3 <- solve(p3)
    +
    +# print first six rows of the attribute table
    +print(head(s3))
    +#> Simple feature collection with 6 features and 9 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    +#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    +#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    +#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    +#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    +#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    +#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    +#>   solution_1_zone_2 solution_1_zone_3                       geometry
    +#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s3$solution <- category_vector(
    +  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +s3$solution <- factor(s3$solution)
    +
    +# plot solution
    +plot(s3[, "solution"])
    +
    +
    +# calculate number of selected planning units within solution
    +r3 <- eval_n_summary(
    +  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +print(r3)
    +#> # A tibble: 4 × 2
    +#>   summary  cost
    +#>   <chr>   <dbl>
    +#> 1 overall    52
    +#> 2 zone_1     17
    +#> 3 zone_2     18
    +#> 4 zone_3     17
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/eval_rare_richness_importance.html b/docs/reference/eval_rare_richness_importance.html index 483c70b25..2fc8a1962 100644 --- a/docs/reference/eval_rare_richness_importance.html +++ b/docs/reference/eval_rare_richness_importance.html @@ -1,110 +1,27 @@ - - - - - - - -Evaluate solution importance using rarity weighted richness scores — eval_rare_richness_importance • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solution importance using rarity weighted richness scores — eval_rare_richness_importance • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -193,7 +110,7 @@

    Evaluate solution importance using rarity weighted richness scores

    Calculate importance scores for planning units selected in a solution using rarity weighted richness scores (based on Williams et al. 1996). This method is only recommended for large-scaled conservation -planning exercises (i.e. more than 100,000 planning units) where +planning exercises (i.e., more than 100,000 planning units) where importance scores cannot be calculated using other methods in a feasible period of time. This is because rarity weighted richness scores cannot (i) account for the cost of different planning units, (ii) account for multiple @@ -202,63 +119,53 @@

    Evaluate solution importance using rarity weighted richness scores

    limitations.

    -
    eval_rare_richness_importance(x, solution, ...)
    +    
    Usage,
    eval_rare_richness_importance(x, solution, ...)
     
    -# S4 method for ConservationProblem,numeric
    -eval_rare_richness_importance(x, solution, rescale, ...)
    +# S4 method for ConservationProblem,numeric
    +eval_rare_richness_importance(x, solution, rescale, ...)
     
    -# S4 method for ConservationProblem,matrix
    -eval_rare_richness_importance(x, solution, rescale, ...)
    +# S4 method for ConservationProblem,matrix
    +eval_rare_richness_importance(x, solution, rescale, ...)
     
    -# S4 method for ConservationProblem,data.frame
    -eval_rare_richness_importance(x, solution, rescale, ...)
    +# S4 method for ConservationProblem,data.frame
    +eval_rare_richness_importance(x, solution, rescale, ...)
     
    -# S4 method for ConservationProblem,Spatial
    -eval_rare_richness_importance(x, solution, rescale, ...)
    +# S4 method for ConservationProblem,Spatial
    +eval_rare_richness_importance(x, solution, rescale, ...)
     
    -# S4 method for ConservationProblem,sf
    -eval_rare_richness_importance(x, solution, rescale, ...)
    +# S4 method for ConservationProblem,sf
    +eval_rare_richness_importance(x, solution, rescale, ...)
     
    -# S4 method for ConservationProblem,Raster
    -eval_rare_richness_importance(x, solution, rescale, ...)
    +# S4 method for ConservationProblem,Raster +eval_rare_richness_importance(x, solution, rescale, ...)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. The argument should be in the same format as the planning unit cost data in the argument to x. -See the Solution format section for more information.

    ...

    not used.

    rescale

    logical flag indicating if replacement cost +See the Solution format section for more information.

    +
    ...
    +

    not used.

    +
    rescale
    +

    logical flag indicating if replacement cost values---excepting infinite (Inf) and zero values---should be -rescaled to range between 0.01 and 1. Defaults to TRUE.

    - -

    Value

    - -

    A numeric, matrix, data.frame -RasterLayer, Spatial, -or sf::sf() object containing the importance scores for each planning +rescaled to range between 0.01 and 1. Defaults to TRUE.

    +
    +
    +

    Value

    +

    A numeric, matrix, data.frameRasterLayer, Spatial, +or sf::sf() object containing the importance scores for each planning unit in the solution. Specifically, the returned object is in the same format as the planning unit data in the argument to x.

    -

    Details

    - +
    +
    +

    Details

    Rarity weighted richness scores are calculated using the following terms. Let \(I\) denote the set of planning units (indexed by \(i\)), let \(J\) denote the set of conservation features (indexed by @@ -271,24 +178,26 @@

    Details \mathit{RWR}_{k} = \sum_{j}^{J} \frac{ \frac{r_{ik}}{m_j} }{ \sum_{i}^{I}r_{ij}} $$

    -

    Solution format

    - +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -296,8 +205,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -305,35 +216,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -342,142 +259,141 @@

    References

    +

    +
    +

    References

    Williams P, Gibbons D, Margules C, Rebelo A, Humphries C, and Pressey RL (1996) A comparison of richness hotspots, rarity hotspots and complementary areas for conserving diversity using British birds. Conservation Biology, 10: 155--174.

    -

    See also

    - -

    See importance for an overview of all functions for evaluating +

    +
    +

    See also

    +

    See importance for an overview of all functions for evaluating the importance of planning units selected in a solution.

    Other importances: -eval_ferrier_importance(), -eval_replacement_importance()

    - -

    Examples

    -
    # \dontrun{
    -# seed seed for reproducibility
    -set.seed(600)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_sf, sim_features)
    -
    -# create minimal problem with raster planning units
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate importance scores
    -rwr1 <- eval_rare_richness_importance(p1, s1)
    -
    -# print importance scores
    -print(rwr1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : rwr 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot importance scores
    -plot(rwr1, main = "rarity weighted richness", axes = FALSE, box = FALSE)
    -
    -
    -# create minimal problem with polygon (sf) planning units
    -p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.05) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem
    -s2 <- solve(p2)
    -
    -# print solution
    -print(s2)
    -#> Simple feature collection with 90 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 1
    -#> CRS:           NA
    -#> First 10 features:
    -#>        cost locked_in locked_out solution_1                       geometry
    -#> 1  215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2  212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3  207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4  208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5  214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6  213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -#> 7  210.4612     FALSE      FALSE          0 POLYGON ((0.6 1, 0.7 1, 0.7...
    -#> 8  211.0424     FALSE       TRUE          0 POLYGON ((0.7 1, 0.8 1, 0.8...
    -#> 9  210.3878     FALSE      FALSE          0 POLYGON ((0.8 1, 0.9 1, 0.9...
    -#> 10 204.3971     FALSE      FALSE          0 POLYGON ((0.9 1, 1 1, 1 0.9...
    -
    -# plot solution
    -plot(s2[, "solution_1"], main = "solution")
    -
    -
    -# calculate importance scores
    -rwr2 <- eval_rare_richness_importance(p2, s2[, "solution_1"])
    -
    -# plot importance scores
    -plot(rwr2, main = "rarity weighted richness")
    -
    -# }
    -
    +eval_ferrier_importance(),
    +eval_replacement_importance()

    +
    + +
    +

    Examples

    +
    # \dontrun{
    +# seed seed for reproducibility
    +set.seed(600)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_sf, sim_features)
    +
    +# create minimal problem with raster planning units
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate importance scores
    +rwr1 <- eval_rare_richness_importance(p1, s1)
    +
    +# print importance scores
    +print(rwr1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : rwr 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot importance scores
    +plot(rwr1, main = "rarity weighted richness", axes = FALSE, box = FALSE)
    +
    +
    +# create minimal problem with polygon (sf) planning units
    +p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.05) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem
    +s2 <- solve(p2)
    +
    +# print solution
    +print(s2)
    +#> Simple feature collection with 90 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 1
    +#> CRS:           NA
    +#> First 10 features:
    +#>        cost locked_in locked_out solution_1                       geometry
    +#> 1  215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2  212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3  207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4  208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5  214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6  213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +#> 7  210.4612     FALSE      FALSE          0 POLYGON ((0.6 1, 0.7 1, 0.7...
    +#> 8  211.0424     FALSE       TRUE          0 POLYGON ((0.7 1, 0.8 1, 0.8...
    +#> 9  210.3878     FALSE      FALSE          0 POLYGON ((0.8 1, 0.9 1, 0.9...
    +#> 10 204.3971     FALSE      FALSE          0 POLYGON ((0.9 1, 1 1, 1 0.9...
    +
    +# plot solution
    +plot(s2[, "solution_1"], main = "solution")
    +
    +
    +# calculate importance scores
    +rwr2 <- eval_rare_richness_importance(p2, s2[, "solution_1"])
    +
    +# plot importance scores
    +plot(rwr2, main = "rarity weighted richness")
    +
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/eval_replacement_importance.html b/docs/reference/eval_replacement_importance.html index cdb90d16b..96cb51a9e 100644 --- a/docs/reference/eval_replacement_importance.html +++ b/docs/reference/eval_replacement_importance.html @@ -1,102 +1,19 @@ - - - - - - - -Evaluate solution importance using replacement cost scores — eval_replacement_importance • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solution importance using replacement cost scores — eval_replacement_importance • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,82 +103,66 @@

    Evaluate solution importance using replacement cost scores

    based on the replacement cost method (Cabeza and Moilanen 2006).

    -
    eval_replacement_importance(x, solution, ...)
    +    
    Usage,
    eval_replacement_importance(x, solution, ...)
     
    -# S4 method for ConservationProblem,numeric
    -eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
    +# S4 method for ConservationProblem,numeric
    +eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
     
    -# S4 method for ConservationProblem,matrix
    -eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
    +# S4 method for ConservationProblem,matrix
    +eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
     
    -# S4 method for ConservationProblem,data.frame
    -eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
    +# S4 method for ConservationProblem,data.frame
    +eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
     
    -# S4 method for ConservationProblem,Spatial
    -eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
    +# S4 method for ConservationProblem,Spatial
    +eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
     
    -# S4 method for ConservationProblem,sf
    -eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
    +# S4 method for ConservationProblem,sf
    +eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
     
    -# S4 method for ConservationProblem,Raster
    -eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
    +# S4 method for ConservationProblem,Raster +eval_replacement_importance(x, solution, rescale, run_checks, force, threads, ...)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. The argument should be in the same format as the planning unit cost data in the argument to x. -See the Solution format section for more information.

    ...

    not used.

    rescale

    logical flag indicating if replacement cost +See the Solution format section for more information.

    +
    ...
    +

    not used.

    +
    rescale
    +

    logical flag indicating if replacement cost values---excepting infinite (Inf) and zero values---should be -rescaled to range between 0.01 and 1. Defaults to TRUE.

    run_checks

    logical flag indicating whether presolve checks +rescaled to range between 0.01 and 1. Defaults to TRUE.

    +
    run_checks
    +

    logical flag indicating whether presolve checks should be run prior solving the problem. These checks are performed using -the presolve_check() function. Defaults to TRUE. -Skipping these checks may reduce run time for large problems.

    force

    logical flag indicating if an attempt to should be +the presolve_check() function. Defaults to TRUE. +Skipping these checks may reduce run time for large problems.

    +
    force
    +

    logical flag indicating if an attempt to should be made to solve the problem even if potential issues were detected during -the presolve checks. Defaults to FALSE.

    threads

    integer number of threads to use for the +the presolve checks. Defaults to FALSE.

    +
    threads
    +

    integer number of threads to use for the optimization algorithm. Defaults to 1 such that only a single -thread is used.

    - -

    Value

    - -

    A numeric, matrix, data.frame -RasterLayer, Spatial, -or sf::sf() object containing the importance scores for each planning +thread is used.

    +
    +
    +

    Value

    +

    A numeric, matrix, data.frameRasterLayer, Spatial, +or sf::sf() object containing the importance scores for each planning unit in the solution. Specifically, the returned object is in the same format as the planning unit data in the argument to x.

    -

    Details

    - +
    +
    +

    Details

    This function implements a modified version of the replacement cost method (Cabeza and Moilanen 2006). Specifically, the score for each planning unit is calculated @@ -274,46 +175,48 @@

    Details considered instead. Thus planning units with a higher score are more important (and irreplaceable). For example, when using the minimum set objective function -(add_min_set_objective()), the replacement cost scores +(add_min_set_objective()), the replacement cost scores correspond to the additional costs needed to meet targets when each planning unit is locked out. When using the maximum utility -objective function (add_max_utility_objective(), the +objective function (add_max_utility_objective(), the replacement cost scores correspond to the reduction in the utility when each planning unit is locked out. Infinite values mean that no feasible solution exists when planning units are locked out---they are -absolutely essential for obtaining a solution (e.g. they contain rare +absolutely essential for obtaining a solution (e.g., they contain rare species that are not found in any other planning units or were locked in). Zeros values mean that planning units can swapped with other planning units and this will have no effect on the performance of the solution at all -(e.g. because they were only selected due to spatial fragmentation +(e.g., because they were only selected due to spatial fragmentation penalties).

    These calculations can take a long time to complete for large or complex conservation planning problems. As such, we using this method for small or moderate-sized conservation planning problems -(e.g. < 30,000 planning units). To reduce run time, we -recommend calculating these scores without additional penalties (e.g. -add_boundary_penalties()) or spatial constraints (e.g. -add_contiguity_constraints()). To further reduce run time, +(e.g., < 30,000 planning units). To reduce run time, we +recommend calculating these scores without additional penalties (e.g., +add_boundary_penalties()) or spatial constraints (e.g., +add_contiguity_constraints()). To further reduce run time, we recommend using proportion-type decisions when calculating the scores (see below for an example).

    -

    Solution format

    - +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -321,8 +224,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -330,35 +235,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -367,213 +278,212 @@

    References

    +

    +
    +

    References

    Cabeza M and Moilanen A (2006) Replacement cost: A practical measure of site value for cost-effective reserve planning. Biological Conservation, 132: 336--342.

    -

    See also

    - -

    See importance for an overview of all functions for evaluating +

    +
    +

    See also

    +

    See importance for an overview of all functions for evaluating the importance of planning units selected in a solution.

    Other importances: -eval_ferrier_importance(), -eval_rare_richness_importance()

    - -

    Examples

    -
    # \dontrun{
    -# seed seed for reproducibility
    -set.seed(600)
    -
    -# load data
    -data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    -
    -# create minimal problem with binary decisions
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -# solve problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate importance scores
    -rc1 <- eval_replacement_importance(p1, s1)
    -
    -# print importance scores
    -print(rc1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : rc 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot importance scores
    -plot(rc1, main = "replacement cost", axes = FALSE, box = FALSE)
    -
    -
    -# since replacement cost scores can take a long time to calculate with
    -# binary decisions, we can calculate them using proportion-type
    -# decision variables. Note we are still calculating the scores for our
    -# previous solution (s1), we are just using a different optimization
    -# problem when calculating the scores.
    -p2 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_proportion_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# calculate importance scores using proportion type decisions
    -rc2 <- eval_replacement_importance(p2, s1)
    -
    -# print importance scores based on proportion type decisions
    -print(rc2)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : rc 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot importance scores based on proportion type decisions
    -# we can see that the importance values in rc1 and rc2 are similar,
    -# and this confirms that the proportion type decisions are a good
    -# approximation
    -plot(rc2, main = "replacement cost", axes = FALSE, box = FALSE)
    -
    -
    -# create minimal problem with polygon (sf) planning units
    -p3 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.05) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve problem
    -s3 <- solve(p3)
    -
    -# print solution
    -print(s3)
    -#> Simple feature collection with 90 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 1
    -#> CRS:           NA
    -#> First 10 features:
    -#>        cost locked_in locked_out solution_1                       geometry
    -#> 1  215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2  212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3  207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4  208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5  214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6  213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -#> 7  210.4612     FALSE      FALSE          0 POLYGON ((0.6 1, 0.7 1, 0.7...
    -#> 8  211.0424     FALSE       TRUE          0 POLYGON ((0.7 1, 0.8 1, 0.8...
    -#> 9  210.3878     FALSE      FALSE          0 POLYGON ((0.8 1, 0.9 1, 0.9...
    -#> 10 204.3971     FALSE      FALSE          0 POLYGON ((0.9 1, 1 1, 1 0.9...
    -
    -# plot solution
    -plot(s3[, "solution_1"], main = "solution")
    -
    -
    -# calculate importance scores
    -rc3 <- eval_rare_richness_importance(p3, s3[, "solution_1"])
    -
    -# plot importance scores
    -plot(rc3, main = "replacement cost")
    -
    -
    -# build multi-zone conservation problem with raster data
    -p4 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve the problem
    -s4 <- solve(p4)
    -
    -# print solution
    -print(s4)
    -#> class      : RasterStack 
    -#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> names      : zone_1, zone_2, zone_3 
    -#> min values :      0,      0,      0 
    -#> max values :      1,      1,      1 
    -#> 
    -
    -# plot solution
    -# each panel corresponds to a different zone, and data show the
    -# status of each planning unit in a given zone
    -plot(s4, main = paste0("zone ", seq_len(nlayers(s4))), axes = FALSE,
    -     box = FALSE)
    -
    -
    -# calculate importance scores
    -rc4 <- eval_replacement_importance(p4, s4)
    -
    -# plot importance
    -# each panel corresponds to a different zone, and data show the
    -# importance of each planning unit in a given zone
    -plot(rc4, main = paste0("zone ", seq_len(nlayers(s4))), axes = FALSE,
    -     box = FALSE)
    -
    -# }
    -
    +eval_ferrier_importance(),
    +eval_rare_richness_importance()

    +
    + +
    +

    Examples

    +
    # \dontrun{
    +# seed seed for reproducibility
    +set.seed(600)
    +
    +# load data
    +data(sim_pu_raster, sim_features, sim_pu_zones_stack, sim_features_zones)
    +
    +# create minimal problem with binary decisions
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +# solve problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate importance scores
    +rc1 <- eval_replacement_importance(p1, s1)
    +
    +# print importance scores
    +print(rc1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : rc 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot importance scores
    +plot(rc1, main = "replacement cost", axes = FALSE, box = FALSE)
    +
    +
    +# since replacement cost scores can take a long time to calculate with
    +# binary decisions, we can calculate them using proportion-type
    +# decision variables. Note we are still calculating the scores for our
    +# previous solution (s1), we are just using a different optimization
    +# problem when calculating the scores.
    +p2 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_proportion_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# calculate importance scores using proportion type decisions
    +rc2 <- eval_replacement_importance(p2, s1)
    +
    +# print importance scores based on proportion type decisions
    +print(rc2)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : rc 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot importance scores based on proportion type decisions
    +# we can see that the importance values in rc1 and rc2 are similar,
    +# and this confirms that the proportion type decisions are a good
    +# approximation
    +plot(rc2, main = "replacement cost", axes = FALSE, box = FALSE)
    +
    +
    +# create minimal problem with polygon (sf) planning units
    +p3 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.05) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve problem
    +s3 <- solve(p3)
    +
    +# print solution
    +print(s3)
    +#> Simple feature collection with 90 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0 xmax: 1 ymax: 1
    +#> CRS:           NA
    +#> First 10 features:
    +#>        cost locked_in locked_out solution_1                       geometry
    +#> 1  215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2  212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3  207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4  208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5  214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6  213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +#> 7  210.4612     FALSE      FALSE          0 POLYGON ((0.6 1, 0.7 1, 0.7...
    +#> 8  211.0424     FALSE       TRUE          0 POLYGON ((0.7 1, 0.8 1, 0.8...
    +#> 9  210.3878     FALSE      FALSE          0 POLYGON ((0.8 1, 0.9 1, 0.9...
    +#> 10 204.3971     FALSE      FALSE          0 POLYGON ((0.9 1, 1 1, 1 0.9...
    +
    +# plot solution
    +plot(s3[, "solution_1"], main = "solution")
    +
    +
    +# calculate importance scores
    +rc3 <- eval_rare_richness_importance(p3, s3[, "solution_1"])
    +
    +# plot importance scores
    +plot(rc3, main = "replacement cost")
    +
    +
    +# build multi-zone conservation problem with raster data
    +p4 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve the problem
    +s4 <- solve(p4)
    +
    +# print solution
    +print(s4)
    +#> class      : RasterStack 
    +#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> names      : zone_1, zone_2, zone_3 
    +#> min values :      0,      0,      0 
    +#> max values :      1,      1,      1 
    +#> 
    +
    +# plot solution
    +# each panel corresponds to a different zone, and data show the
    +# status of each planning unit in a given zone
    +plot(s4, main = paste0("zone ", seq_len(nlayers(s4))), axes = FALSE,
    +     box = FALSE)
    +
    +
    +# calculate importance scores
    +rc4 <- eval_replacement_importance(p4, s4)
    +
    +# plot importance
    +# each panel corresponds to a different zone, and data show the
    +# importance of each planning unit in a given zone
    +plot(rc4, main = paste0("zone ", seq_len(nlayers(s4))), axes = FALSE,
    +     box = FALSE)
    +
    +# }
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/eval_target_coverage_summary.html b/docs/reference/eval_target_coverage_summary.html index bcf7d45be..59ecb5bae 100644 --- a/docs/reference/eval_target_coverage_summary.html +++ b/docs/reference/eval_target_coverage_summary.html @@ -1,106 +1,23 @@ - - - - - - - -Evaluate target coverage — eval_target_coverage_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate target coverage — eval_target_coverage_summary • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Calculate how well feature representation targets are met by a solution to -a conservation planning problem(). +

    Calculate how well feature representation targets are met by a solution to +a conservation planning problem(). It is useful for understanding if features are adequately represented by a solution. Note that this function can only be used with problems that contain -targets.

    +targets.

    -
    eval_target_coverage_summary(x, solution, include_zone, include_sense)
    -
    -# S3 method for default
    -eval_target_coverage_summary(x, solution, include_zone, include_sense)
    -
    -# S3 method for ConservationProblem
    -eval_target_coverage_summary(
    -  x,
    -  solution,
    -  include_zone = number_of_zones(x) > 1,
    -  include_sense = number_of_zones(x) > 1
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. +

    Usage,
    eval_target_coverage_summary(x, solution, include_zone, include_sense)
    +
    +# S3 method for default
    +eval_target_coverage_summary(x, solution, include_zone, include_sense)
    +
    +# S3 method for ConservationProblem
    +eval_target_coverage_summary(
    +  x,
    +  solution,
    +  include_zone = number_of_zones(x) > 1,
    +  include_sense = number_of_zones(x) > 1
    +)
    + +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. The argument should be in the same format as the planning unit cost data in the argument to x. -See the Solution format section for more information.

    include_zone

    logical include the zone column in the output? -Defaults to TRUE for problems that contain multiple zones.

    include_sense

    logical include the sense column in the output? -Defaults to TRUE for problems that contain multiple zones.

    - -

    Value

    - -

    tibble::tibble() object. +See the Solution format section for more information.

    +
    include_zone
    +

    logical include the zone column in the output? +Defaults to TRUE for problems that contain multiple zones.

    +
    include_sense
    +

    logical include the sense column in the output? +Defaults to TRUE for problems that contain multiple zones.

    +
    +
    +

    Value

    +

    tibble::tibble() object. Here, each row describes information for a different target. -It contains the following columns:

    -
    - -
    feature

    character name of the feature associated with each +It contains the following columns:

    feature
    +

    character name of the feature associated with each target.

    -
    zone

    list of character zone names associated with each target. + +

    zone
    +

    list of character zone names associated with each target. This column is in a list-column format because a single target can -correspond to multiple zones (see add_manual_targets() for details +correspond to multiple zones (see add_manual_targets() for details and examples). For an example of converting the list-column format to a standard character column format, please see the Examples section. This column is only included if the argument to include_zones is TRUE.

    -
    sense

    character sense associated with each target. + +

    sense
    +

    character sense associated with each target. Sense values specify the nature of the target. -Typically (e.g. when using the add_absolute_targets() or -add_relative_targets() functions), targets are specified using sense +Typically (e.g., when using the add_absolute_targets() or +add_relative_targets() functions), targets are specified using sense values indicating that the total amount of a feature held within a solution (ideally) be greater than or equal to a threshold amount -(i.e. a sense value of ">="). -Additionally, targets (i.e. using the add_manual_targets() function) +(i.e., a sense value of ">="). +Additionally, targets (i.e., using the add_manual_targets() function) can also be specified using sense values indicating that the total amount of a feature held within a solution must be equal to a -threshold amount (i.e. a sense value of "=") or smaller than or equal -to a threshold amount (i.e. a sense value of "<="). +threshold amount (i.e., a sense value of "=") or smaller than or equal +to a threshold amount (i.e., a sense value of "<="). This column is only included if the argument to include_sense is TRUE.

    -
    total_amount

    numeric total amount of the feature available across + +

    total_amount
    +

    numeric total amount of the feature available across the entire conservation planning problem for meeting each target (not just planning units selected within the solution). For problems involving a single zone, this column is calculated as the sum of all of the values for a given feature (similar to values in the total_amount column produced by the -eval_feature_representation_summary() function). +eval_feature_representation_summary() function). For problems involving multiple zones, this column is calculated as the sum of the values for the feature associated with target (per the "feature" column), across the zones associated with the target (per the "zone" column).

    -
    absolute_target

    numeric total threshold amount associated with + +

    absolute_target
    +

    numeric total threshold amount associated with each target.

    -
    absolute_held

    numeric total amount held within the solution for + +

    absolute_held
    +

    numeric total amount held within the solution for the feature and (if relevant) zones associated with each target (per the "feature" and "zone" columns, respectively). This column is calculated as the sum of the feature data, -supplied when creating a problem() object -(e.g. presence/absence values), weighted by the status of each -planning unit in the solution (e.g. selected or not for prioritization).

    +supplied when creating a problem() object +(e.g., presence/absence values), weighted by the status of each +planning unit in the solution (e.g., selected or not for +prioritization).

    -
    absolute_shortfall

    numeric total amount by which the solution + +

    absolute_shortfall
    +

    numeric total amount by which the solution fails to meet each target. This column is calculated as the difference between the total amount held within the solution for the feature and (if relevant) zones -associated with the target (i.e. "absolute_held" column) and the -target total threshold amount (i.e. "absolute_target" column), with +associated with the target (i.e., "absolute_held" column) and the +target total threshold amount (i.e., "absolute_target" column), with values set to zero depending on the sense specified for the target -(e.g. if the target sense is >= then the difference is +(e.g., if the target sense is >= then the difference is set to zero if the value in the "absolute_held" is smaller than that in the "absolute_target" column).

    -
    relative_target

    numeric proportion threshold amount associated + +

    relative_target
    +

    numeric proportion threshold amount associated with each target. This column is calculated by dividing the total threshold amount -associated with each target (i.e. "absolute_target" column) by +associated with each target (i.e., "absolute_target" column) by the total amount associated with each target -(i.e. "total_amount" column).

    +(i.e., "total_amount" column).

    -
    relative_held

    numeric proportion held within the solution for the + +

    relative_held
    +

    numeric proportion held within the solution for the feature and (if relevant) zones associated with each target (per the "feature" and "zone" columns, respectively). This column is calculated by dividing the total amount held -for each target (i.e. "absolute_held" column) by the +for each target (i.e., "absolute_held" column) by the total amount for with each target -(i.e. "total_amount" column).

    +(i.e., "total_amount" column).

    + -
    relative_shortfall

    numeric proportion by which the solution fails +

    relative_shortfall
    +

    numeric proportion by which the solution fails to meet each target. This column is calculated by dividing the total shortfall for -each target (i.e. "absolute_shortfall" column) by the -total amount for each target (i.e. "total_amount" column).

    +each target (i.e., "absolute_shortfall" column) by the +total amount for each target (i.e., "total_amount" column).

    -
    met

    logical indicating if each target is met by the solution. This + +

    met
    +

    logical indicating if each target is met by the solution. This column is calculated by checking if the total shortfall associated -with each target (i.e. "absolute_shortfall" column) is equal to +with each target (i.e., "absolute_shortfall" column) is equal to zero.

    -
    - -

    Solution format

    +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -357,8 +284,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -366,35 +295,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -403,272 +338,270 @@

    See also

    -

    See summaries for an overview of all functions for summarizing solutions.

    +

    +
    +

    See also

    +

    See summaries for an overview of all functions for summarizing solutions.

    Other summaries: -eval_boundary_summary(), -eval_connectivity_summary(), -eval_cost_summary(), -eval_feature_representation_summary(), -eval_n_summary()

    - -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_sf, sim_features,
    -     sim_pu_zones_sf, sim_features_zones)
    -
    -# build minimal conservation problem with raster data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate target coverage by the solution
    -r1 <- eval_target_coverage_summary(p1, s1)
    -print(r1, width = Inf) # note: `width = Inf` tells R to print all columns
    -#> # A tibble: 5 × 9
    -#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1 layer.1 TRUE          83.3            8.33          8.91                  0
    -#> 2 layer.2 TRUE          31.2            3.12          3.13                  0
    -#> 3 layer.3 TRUE          72.0            7.20          7.34                  0
    -#> 4 layer.4 TRUE          42.7            4.27          4.35                  0
    -#> 5 layer.5 TRUE          56.7            5.67          6.01                  0
    -#>   relative_target relative_held relative_shortfall
    -#>             <dbl>         <dbl>              <dbl>
    -#> 1             0.1         0.107                  0
    -#> 2             0.1         0.100                  0
    -#> 3             0.1         0.102                  0
    -#> 4             0.1         0.102                  0
    -#> 5             0.1         0.106                  0
    -
    -# build minimal conservation problem with polygon (sf) data
    -p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print first six rows of the attribute table
    -print(head(s2))
    -#> Simple feature collection with 6 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>       cost locked_in locked_out solution_1                       geometry
    -#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# plot solution
    -plot(s2[, "solution_1"])
    -
    -
    -# calculate target coverage by the solution
    -r2 <- eval_target_coverage_summary(p2, s2[, "solution_1"])
    -print(r2, width = Inf)
    -#> # A tibble: 5 × 9
    -#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1 layer.1 TRUE          74.5            7.45          8.05                  0
    -#> 2 layer.2 TRUE          28.1            2.81          2.83                  0
    -#> 3 layer.3 TRUE          64.9            6.49          6.65                  0
    -#> 4 layer.4 TRUE          38.2            3.82          3.87                  0
    -#> 5 layer.5 TRUE          50.7            5.07          5.41                  0
    -#>   relative_target relative_held relative_shortfall
    -#>             <dbl>         <dbl>              <dbl>
    -#> 1             0.1         0.108                  0
    -#> 2             0.1         0.101                  0
    -#> 3             0.1         0.103                  0
    -#> 4             0.1         0.101                  0
    -#> 5             0.1         0.107                  0
    -
    -# build multi-zone conservation problem with polygon (sf) data
    -p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s3 <- solve(p3)
    -
    -# print first six rows of the attribute table
    -print(head(s3))
    -#> Simple feature collection with 6 features and 9 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    -#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    -#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    -#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    -#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    -#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    -#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    -#>   solution_1_zone_2 solution_1_zone_3                       geometry
    -#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s3$solution <- category_vector(
    -  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -s3$solution <- factor(s3$solution)
    -
    -# plot solution
    -plot(s3[, "solution"])
    -
    -
    -# calculate target coverage by the solution
    -r3 <- eval_target_coverage_summary(
    -  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -print(r3, width = Inf)
    -#> # A tibble: 15 × 11
    -#>    feature   zone      sense met   total_amount absolute_target absolute_held
    -#>    <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    -#>  1 feature_1 <chr [1]> >=    TRUE          75.1           13.8          14.9 
    -#>  2 feature_2 <chr [1]> >=    TRUE          28.0            4.82          5.14
    -#>  3 feature_3 <chr [1]> >=    TRUE          65.0           12.8          12.8 
    -#>  4 feature_4 <chr [1]> >=    TRUE          38.0            5.58          6.64
    -#>  5 feature_5 <chr [1]> >=    TRUE          51.2            9.27          9.96
    -#>  6 feature_1 <chr [1]> >=    TRUE          75.1            9.06         15.0 
    -#>  7 feature_2 <chr [1]> >=    TRUE          28.0            4.23          5.58
    -#>  8 feature_3 <chr [1]> >=    TRUE          65.0           12.5          13.6 
    -#>  9 feature_4 <chr [1]> >=    TRUE          38.0            6.96          7.14
    -#> 10 feature_5 <chr [1]> >=    TRUE          51.2            8.76          9.96
    -#> 11 feature_1 <chr [1]> >=    TRUE          75.1            9.63         13.3 
    -#> 12 feature_2 <chr [1]> >=    TRUE          28.0            5.29          5.65
    -#> 13 feature_3 <chr [1]> >=    TRUE          65.0           11.5          11.8 
    -#> 14 feature_4 <chr [1]> >=    TRUE          38.0            4.43          7.71
    -#> 15 feature_5 <chr [1]> >=    TRUE          51.2            8.86          9.04
    -#>    absolute_shortfall relative_target relative_held relative_shortfall
    -#>                 <dbl>           <dbl>         <dbl>              <dbl>
    -#>  1                  0           0.183         0.198                  0
    -#>  2                  0           0.173         0.184                  0
    -#>  3                  0           0.198         0.198                  0
    -#>  4                  0           0.147         0.175                  0
    -#>  5                  0           0.181         0.195                  0
    -#>  6                  0           0.121         0.200                  0
    -#>  7                  0           0.151         0.200                  0
    -#>  8                  0           0.193         0.210                  0
    -#>  9                  0           0.183         0.188                  0
    -#> 10                  0           0.171         0.195                  0
    -#> 11                  0           0.128         0.177                  0
    -#> 12                  0           0.189         0.202                  0
    -#> 13                  0           0.176         0.181                  0
    -#> 14                  0           0.116         0.203                  0
    -#> 15                  0           0.173         0.177                  0
    -
    -# create a new column with character values containing the zone names,
    -# by extracting these data out of the zone column
    -# (which is in list-column format)
    -r3$zone2 <- vapply(r3$zone, FUN.VALUE = character(1), paste, sep = " & ")
    -
    -# print r3 again to show the new column
    -print(r3, width = Inf)
    -#> # A tibble: 15 × 12
    -#>    feature   zone      sense met   total_amount absolute_target absolute_held
    -#>    <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    -#>  1 feature_1 <chr [1]> >=    TRUE          75.1           13.8          14.9 
    -#>  2 feature_2 <chr [1]> >=    TRUE          28.0            4.82          5.14
    -#>  3 feature_3 <chr [1]> >=    TRUE          65.0           12.8          12.8 
    -#>  4 feature_4 <chr [1]> >=    TRUE          38.0            5.58          6.64
    -#>  5 feature_5 <chr [1]> >=    TRUE          51.2            9.27          9.96
    -#>  6 feature_1 <chr [1]> >=    TRUE          75.1            9.06         15.0 
    -#>  7 feature_2 <chr [1]> >=    TRUE          28.0            4.23          5.58
    -#>  8 feature_3 <chr [1]> >=    TRUE          65.0           12.5          13.6 
    -#>  9 feature_4 <chr [1]> >=    TRUE          38.0            6.96          7.14
    -#> 10 feature_5 <chr [1]> >=    TRUE          51.2            8.76          9.96
    -#> 11 feature_1 <chr [1]> >=    TRUE          75.1            9.63         13.3 
    -#> 12 feature_2 <chr [1]> >=    TRUE          28.0            5.29          5.65
    -#> 13 feature_3 <chr [1]> >=    TRUE          65.0           11.5          11.8 
    -#> 14 feature_4 <chr [1]> >=    TRUE          38.0            4.43          7.71
    -#> 15 feature_5 <chr [1]> >=    TRUE          51.2            8.86          9.04
    -#>    absolute_shortfall relative_target relative_held relative_shortfall zone2 
    -#>                 <dbl>           <dbl>         <dbl>              <dbl> <chr> 
    -#>  1                  0           0.183         0.198                  0 zone_1
    -#>  2                  0           0.173         0.184                  0 zone_1
    -#>  3                  0           0.198         0.198                  0 zone_1
    -#>  4                  0           0.147         0.175                  0 zone_1
    -#>  5                  0           0.181         0.195                  0 zone_1
    -#>  6                  0           0.121         0.200                  0 zone_2
    -#>  7                  0           0.151         0.200                  0 zone_2
    -#>  8                  0           0.193         0.210                  0 zone_2
    -#>  9                  0           0.183         0.188                  0 zone_2
    -#> 10                  0           0.171         0.195                  0 zone_2
    -#> 11                  0           0.128         0.177                  0 zone_3
    -#> 12                  0           0.189         0.202                  0 zone_3
    -#> 13                  0           0.176         0.181                  0 zone_3
    -#> 14                  0           0.116         0.203                  0 zone_3
    -#> 15                  0           0.173         0.177                  0 zone_3
    -# }
    +eval_boundary_summary(),
    +eval_connectivity_summary(),
    +eval_cost_summary(),
    +eval_feature_representation_summary(),
    +eval_n_summary()

    +
    + +
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_sf, sim_features,
    +     sim_pu_zones_sf, sim_features_zones)
    +
    +# build minimal conservation problem with raster data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate target coverage by the solution
    +r1 <- eval_target_coverage_summary(p1, s1)
    +print(r1, width = Inf) # note: `width = Inf` tells R to print all columns
    +#> # A tibble: 5 × 9
    +#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1 layer.1 TRUE          83.3            8.33          8.91                  0
    +#> 2 layer.2 TRUE          31.2            3.12          3.13                  0
    +#> 3 layer.3 TRUE          72.0            7.20          7.34                  0
    +#> 4 layer.4 TRUE          42.7            4.27          4.35                  0
    +#> 5 layer.5 TRUE          56.7            5.67          6.01                  0
    +#>   relative_target relative_held relative_shortfall
    +#>             <dbl>         <dbl>              <dbl>
    +#> 1             0.1         0.107                  0
    +#> 2             0.1         0.100                  0
    +#> 3             0.1         0.102                  0
    +#> 4             0.1         0.102                  0
    +#> 5             0.1         0.106                  0
    +
    +# build minimal conservation problem with polygon (sf) data
    +p2 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print first six rows of the attribute table
    +print(head(s2))
    +#> Simple feature collection with 6 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>       cost locked_in locked_out solution_1                       geometry
    +#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# plot solution
    +plot(s2[, "solution_1"])
    +
    +
    +# calculate target coverage by the solution
    +r2 <- eval_target_coverage_summary(p2, s2[, "solution_1"])
    +print(r2, width = Inf)
    +#> # A tibble: 5 × 9
    +#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1 layer.1 TRUE          74.5            7.45          8.05                  0
    +#> 2 layer.2 TRUE          28.1            2.81          2.83                  0
    +#> 3 layer.3 TRUE          64.9            6.49          6.65                  0
    +#> 4 layer.4 TRUE          38.2            3.82          3.87                  0
    +#> 5 layer.5 TRUE          50.7            5.07          5.41                  0
    +#>   relative_target relative_held relative_shortfall
    +#>             <dbl>         <dbl>              <dbl>
    +#> 1             0.1         0.108                  0
    +#> 2             0.1         0.101                  0
    +#> 3             0.1         0.103                  0
    +#> 4             0.1         0.101                  0
    +#> 5             0.1         0.107                  0
    +
    +# build multi-zone conservation problem with polygon (sf) data
    +p3 <- problem(sim_pu_zones_sf, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s3 <- solve(p3)
    +
    +# print first six rows of the attribute table
    +print(head(s3))
    +#> Simple feature collection with 6 features and 9 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    +#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    +#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    +#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    +#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    +#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    +#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    +#>   solution_1_zone_2 solution_1_zone_3                       geometry
    +#> 1                 0                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2                 0                 1 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4                 0                 1 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5                 0                 1 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s3$solution <- category_vector(
    +  s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +s3$solution <- factor(s3$solution)
    +
    +# plot solution
    +plot(s3[, "solution"])
    +
    +
    +# calculate target coverage by the solution
    +r3 <- eval_target_coverage_summary(
    +  p3, s3[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +print(r3, width = Inf)
    +#> # A tibble: 15 × 11
    +#>    feature   zone      sense met   total_amount absolute_target absolute_held
    +#>    <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    +#>  1 feature_1 <chr [1]> >=    TRUE          75.1           13.8          14.9 
    +#>  2 feature_2 <chr [1]> >=    TRUE          28.0            4.82          5.14
    +#>  3 feature_3 <chr [1]> >=    TRUE          65.0           12.8          12.8 
    +#>  4 feature_4 <chr [1]> >=    TRUE          38.0            5.58          6.64
    +#>  5 feature_5 <chr [1]> >=    TRUE          51.2            9.27          9.96
    +#>  6 feature_1 <chr [1]> >=    TRUE          75.1            9.06         15.0 
    +#>  7 feature_2 <chr [1]> >=    TRUE          28.0            4.23          5.58
    +#>  8 feature_3 <chr [1]> >=    TRUE          65.0           12.5          13.6 
    +#>  9 feature_4 <chr [1]> >=    TRUE          38.0            6.96          7.14
    +#> 10 feature_5 <chr [1]> >=    TRUE          51.2            8.76          9.96
    +#> 11 feature_1 <chr [1]> >=    TRUE          75.1            9.63         13.3 
    +#> 12 feature_2 <chr [1]> >=    TRUE          28.0            5.29          5.65
    +#> 13 feature_3 <chr [1]> >=    TRUE          65.0           11.5          11.8 
    +#> 14 feature_4 <chr [1]> >=    TRUE          38.0            4.43          7.71
    +#> 15 feature_5 <chr [1]> >=    TRUE          51.2            8.86          9.04
    +#>    absolute_shortfall relative_target relative_held relative_shortfall
    +#>                 <dbl>           <dbl>         <dbl>              <dbl>
    +#>  1                  0           0.183         0.198                  0
    +#>  2                  0           0.173         0.184                  0
    +#>  3                  0           0.198         0.198                  0
    +#>  4                  0           0.147         0.175                  0
    +#>  5                  0           0.181         0.195                  0
    +#>  6                  0           0.121         0.200                  0
    +#>  7                  0           0.151         0.200                  0
    +#>  8                  0           0.193         0.210                  0
    +#>  9                  0           0.183         0.188                  0
    +#> 10                  0           0.171         0.195                  0
    +#> 11                  0           0.128         0.177                  0
    +#> 12                  0           0.189         0.202                  0
    +#> 13                  0           0.176         0.181                  0
    +#> 14                  0           0.116         0.203                  0
    +#> 15                  0           0.173         0.177                  0
    +
    +# create a new column with character values containing the zone names,
    +# by extracting these data out of the zone column
    +# (which is in list-column format)
    +r3$zone2 <- vapply(r3$zone, FUN.VALUE = character(1), paste, sep = " & ")
    +
    +# print r3 again to show the new column
    +print(r3, width = Inf)
    +#> # A tibble: 15 × 12
    +#>    feature   zone      sense met   total_amount absolute_target absolute_held
    +#>    <chr>     <list>    <chr> <lgl>        <dbl>           <dbl>         <dbl>
    +#>  1 feature_1 <chr [1]> >=    TRUE          75.1           13.8          14.9 
    +#>  2 feature_2 <chr [1]> >=    TRUE          28.0            4.82          5.14
    +#>  3 feature_3 <chr [1]> >=    TRUE          65.0           12.8          12.8 
    +#>  4 feature_4 <chr [1]> >=    TRUE          38.0            5.58          6.64
    +#>  5 feature_5 <chr [1]> >=    TRUE          51.2            9.27          9.96
    +#>  6 feature_1 <chr [1]> >=    TRUE          75.1            9.06         15.0 
    +#>  7 feature_2 <chr [1]> >=    TRUE          28.0            4.23          5.58
    +#>  8 feature_3 <chr [1]> >=    TRUE          65.0           12.5          13.6 
    +#>  9 feature_4 <chr [1]> >=    TRUE          38.0            6.96          7.14
    +#> 10 feature_5 <chr [1]> >=    TRUE          51.2            8.76          9.96
    +#> 11 feature_1 <chr [1]> >=    TRUE          75.1            9.63         13.3 
    +#> 12 feature_2 <chr [1]> >=    TRUE          28.0            5.29          5.65
    +#> 13 feature_3 <chr [1]> >=    TRUE          65.0           11.5          11.8 
    +#> 14 feature_4 <chr [1]> >=    TRUE          38.0            4.43          7.71
    +#> 15 feature_5 <chr [1]> >=    TRUE          51.2            8.86          9.04
    +#>    absolute_shortfall relative_target relative_held relative_shortfall zone2 
    +#>                 <dbl>           <dbl>         <dbl>              <dbl> <chr> 
    +#>  1                  0           0.183         0.198                  0 zone_1
    +#>  2                  0           0.173         0.184                  0 zone_1
    +#>  3                  0           0.198         0.198                  0 zone_1
    +#>  4                  0           0.147         0.175                  0 zone_1
    +#>  5                  0           0.181         0.195                  0 zone_1
    +#>  6                  0           0.121         0.200                  0 zone_2
    +#>  7                  0           0.151         0.200                  0 zone_2
    +#>  8                  0           0.193         0.210                  0 zone_2
    +#>  9                  0           0.183         0.188                  0 zone_2
    +#> 10                  0           0.171         0.195                  0 zone_2
    +#> 11                  0           0.128         0.177                  0 zone_3
    +#> 12                  0           0.189         0.202                  0 zone_3
    +#> 13                  0           0.176         0.181                  0 zone_3
    +#> 14                  0           0.116         0.203                  0 zone_3
    +#> 15                  0           0.173         0.177                  0 zone_3
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/fast_extract.html b/docs/reference/fast_extract.html index a18cc266a..26b0e7881 100644 --- a/docs/reference/fast_extract.html +++ b/docs/reference/fast_extract.html @@ -1,101 +1,18 @@ - - - - - - - -Fast extract — fast_extract • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Fast extract — fast_extract • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Extract data from a Raster object.

    +

    Extract data from a Raster object.

    -
    fast_extract(x, y, ...)
    +    
    Usage,
    fast_extract(x, y, ...)
     
    -# S4 method for Raster,SpatialPolygons
    -fast_extract(x, y, fun = "mean", ...)
    +# S4 method for Raster,SpatialPolygons
    +fast_extract(x, y, fun = "mean", ...)
     
    -# S4 method for Raster,SpatialPoints
    -fast_extract(x, y, fun = "mean", ...)
    +# S4 method for Raster,SpatialPoints
    +fast_extract(x, y, fun = "mean", ...)
     
    -# S4 method for Raster,SpatialLines
    -fast_extract(x, y, fun = "mean", ...)
    +# S4 method for Raster,SpatialLines
    +fast_extract(x, y, fun = "mean", ...)
     
    -# S4 method for Raster,sfc
    -fast_extract(x, y, fun = "mean", ...)
    +# S4 method for Raster,sfc
    +fast_extract(x, y, fun = "mean", ...)
     
    -# S4 method for Raster,sf
    -fast_extract(x, y, fun = "mean", ...)
    +# S4 method for Raster,sf +fast_extract(x, y, fun = "mean", ...)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    Raster object.

    y

    Spatial or -sf::sf() object.

    ...

    not used.

    fun

    character name of statistic to summarize data. Defaults +

    +

    Arguments

    +
    x
    +

    Raster object.

    +
    y
    +

    Spatial or +sf::sf() object.

    +
    ...
    +

    not used.

    +
    fun
    +

    character name of statistic to summarize data. Defaults to "mean". Available options include "sum" or "mean". -Defaults to "mean".

    - -

    Value

    - +Defaults to "mean".

    +
    +
    +

    Value

    matrix containing the summary amount of each feature within each planning unit. Rows correspond to different spatial features in the argument to y and columns correspond to different raster layers in the argument to x.

    -

    Details

    - +
    +
    +

    Details

    This function is simply a wrapper that uses -raster::extract() to extract data for -SpatialPoints and -SpatialLines and -non-polygonal sf::sf() data, and -exactextractr::exact_extract() for -SpatialPolygons and -polygonal sf::sf() data.

    -

    See also

    - - +raster::extract() to extract data for +SpatialPoints and +SpatialLines and +non-polygonal sf::sf() data, and +exactextractr::exact_extract() for +SpatialPolygons and +polygonal sf::sf() data.

    +
    + -

    Examples

    -
    # load data
    -data(sim_pu_sf, sim_features)
    -
    -# extract data
    -result <- fast_extract(sim_features, sim_pu_sf)
    -
    -# show result
    -print(head(result))
    -#>           [,1]      [,2]      [,3]      [,4]      [,5]
    -#> [1,] 0.7150548 0.2900901 0.8178213 0.2199663 0.4533809
    -#> [2,] 0.6990429 0.3052216 0.8064534 0.2713800 0.4413639
    -#> [3,] 0.6859317 0.3266036 0.7852441 0.3296247 0.4343547
    -#> [4,] 0.6783193 0.3510977 0.7541320 0.3900085 0.4355628
    -#> [5,] 0.6782107 0.3750470 0.7131559 0.4468281 0.4474871
    -#> [6,] 0.6863252 0.3950113 0.6628435 0.4946333 0.4714105
    -
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_sf, sim_features)
    +
    +# extract data
    +result <- fast_extract(sim_features, sim_pu_sf)
    +
    +# show result
    +print(head(result))
    +#>           [,1]      [,2]      [,3]      [,4]      [,5]
    +#> [1,] 0.7150548 0.2900901 0.8178213 0.2199663 0.4533809
    +#> [2,] 0.6990429 0.3052216 0.8064534 0.2713800 0.4413639
    +#> [3,] 0.6859317 0.3266036 0.7852441 0.3296247 0.4343547
    +#> [4,] 0.6783193 0.3510977 0.7541320 0.3900085 0.4355628
    +#> [5,] 0.6782107 0.3750470 0.7131559 0.4468281 0.4474871
    +#> [6,] 0.6863252 0.3950113 0.6628435 0.4946333 0.4714105
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/feature_abundances.html b/docs/reference/feature_abundances.html index 0ff84d554..353e3d6cd 100644 --- a/docs/reference/feature_abundances.html +++ b/docs/reference/feature_abundances.html @@ -1,102 +1,19 @@ - - - - - - - -Feature abundances — feature_abundances • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Feature abundances — feature_abundances • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,47 +103,45 @@

    Feature abundances

    of a conservation planning problem.

    -
    feature_abundances(x, na.rm)
    +    
    Usage,
    feature_abundances(x, na.rm)
     
    -# S3 method for ConservationProblem
    -feature_abundances(x, na.rm = FALSE)
    +# S3 method for ConservationProblem +feature_abundances(x, na.rm = FALSE)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    na.rm

    logical should planning units with NA cost +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    na.rm
    +

    logical should planning units with NA cost data be excluded from the abundance calculations? The default argument -is FALSE.

    - -

    Value

    - -

    tibble::tibble() object containing the total amount +is FALSE.

    +
    +
    +

    Value

    +

    tibble::tibble() object containing the total amount ("absolute_abundance") and proportion ("relative_abundance") of the distribution of each feature in the planning units. Here, each row contains data that pertain to a specific feature in a specific management zone (if multiple zones are present). This object -contains the following columns:

    -
    +contains the following columns:

    feature
    +

    character name of the feature.

    -
    feature

    character name of the feature.

    -
    zone

    character name of the zone (not included when the +

    zone
    +

    character name of the zone (not included when the argument to x contains only one management zone).

    -
    absolute_abundance

    numeric amount of each feature in the + +

    absolute_abundance
    +

    numeric amount of each feature in the planning units. If the problem contains multiple zones, then this column shows how well each feature is represented in a each zone.

    -
    relative_abundance

    numeric proportion of the feature's + +

    relative_abundance
    +

    numeric proportion of the feature's distribution in the planning units. If the argument to na.rm is FALSE, then this column will only contain values equal to one. Otherwise, if the argument to na.rm is TRUE and planning @@ -234,15 +149,15 @@

    Value

    then this column will contain values between zero and one.

    -
    - -

    Details

    +
    +
    +

    Details

    Planning units can have cost data with finite values -(e.g. 0.1, 3, 100) and NA values. This functionality is provided so +(e.g., 0.1, 3, 100) and NA values. This functionality is provided so that locations which are not available for protected area acquisition can be included when calculating targets for conservation features -(e.g. when targets are specified using add_relative_targets()). +(e.g., when targets are specified using add_relative_targets()). If the total amount of each feature in all the planning units is required---including the planning units with NA cost data---then the the na.rm argument should be set to FALSE. However, if @@ -250,146 +165,145 @@

    Details excluded---for instance, to calculate the highest feasible targets for each feature---then the na.rm argument should be set to TRUE.

    -

    See also

    - - +
    + -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create a simple conservation planning dataset so we can see exactly
    -# how the feature abundances are calculated
    -pu <- data.frame(id = seq_len(10), cost = c(0.2, NA, runif(8)),
    -                 spp1 = runif(10), spp2 = c(rpois(9, 4), NA))
    -
    -# create problem
    -p1 <- problem(pu, c("spp1", "spp2"), cost_column = "cost")
    -
    -# calculate feature abundances; including planning units with NA costs
    -a1 <- feature_abundances(p1, na.rm = FALSE) # (default)
    -print(a1)
    -#> # A tibble: 2 × 3
    -#>   feature absolute_abundance relative_abundance
    -#>   <chr>                <dbl>              <dbl>
    -#> 1 spp1                  4.10                  1
    -#> 2 spp2                 31                     1
    -
    -# calculate feature abundances; excluding planning units with NA costs
    -a2 <- feature_abundances(p1, na.rm = TRUE)
    -print(a2)
    -#> # A tibble: 2 × 3
    -#>   feature absolute_abundance relative_abundance
    -#>   <chr>                <dbl>              <dbl>
    -#> 1 spp1                  3.84              0.935
    -#> 2 spp2                 28                 0.903
    -
    -# verify correctness of feature abundance calculations
    -all.equal(a1$absolute_abundance,
    -          c(sum(pu$spp1), sum(pu$spp2, na.rm = TRUE)))
    -#> [1] TRUE
    -
    -all.equal(a1$relative_abundance,
    -          c(sum(pu$spp1) / sum(pu$spp1),
    -            sum(pu$spp2, na.rm = TRUE) / sum(pu$spp2, na.rm = TRUE)))
    -#> [1] TRUE
    -
    -all.equal(a2$absolute_abundance,
    -          c(sum(pu$spp1[!is.na(pu$cost)]),
    -            sum(pu$spp2[!is.na(pu$cost)], na.rm = TRUE)))
    -#> [1] TRUE
    -
    -all.equal(a2$relative_abundance,
    -          c(sum(pu$spp1[!is.na(pu$cost)]) / sum(pu$spp1, na.rm = TRUE),
    -            sum(pu$spp2[!is.na(pu$cost)], na.rm = TRUE) / sum(pu$spp2,
    -                                                              na.rm = TRUE)))
    -#> [1] TRUE
    -
    -# initialize conservation problem with raster data
    -p3 <- problem(sim_pu_raster, sim_features)
    -
    -# calculate feature abundances; including planning units with NA costs
    -a3 <- feature_abundances(p3, na.rm = FALSE) # (default)
    -print(a3)
    -#> # A tibble: 5 × 3
    -#>   feature absolute_abundance relative_abundance
    -#>   <chr>                <dbl>              <dbl>
    -#> 1 layer.1               83.3                  1
    -#> 2 layer.2               31.2                  1
    -#> 3 layer.3               72.0                  1
    -#> 4 layer.4               42.7                  1
    -#> 5 layer.5               56.7                  1
    -
    -# create problem using total amounts of features in all the planning units
    -# (including units with NA cost data)
    -p4 <- p3 %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(a3$relative_abundance) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# attempt to solve the problem, but we will see that this problem is
    -# infeasible because the targets cannot be met using only the planning units
    -# with finite cost data
    -# \dontrun{
    -s4 <- try(solve(p4))
    -#> Error in .local(a, b = b, ...) : 
    -#>   no solution found (e.g. due to problem infeasibility or time limits)
    -# }
    -# calculate feature abundances; excluding planning units with NA costs
    -a5 <- feature_abundances(p3, na.rm = TRUE)
    -print(a5)
    -#> # A tibble: 5 × 3
    -#>   feature absolute_abundance relative_abundance
    -#>   <chr>                <dbl>              <dbl>
    -#> 1 layer.1               74.5              0.895
    -#> 2 layer.2               28.1              0.900
    -#> 3 layer.3               64.9              0.902
    -#> 4 layer.4               38.2              0.895
    -#> 5 layer.5               50.7              0.893
    -
    -# create problem using total amounts of features in the planning units with
    -# finite cost data
    -p5 <- p3 %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(a5$relative_abundance) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s5 <- solve(p5)
    -
    -# plot the solution
    -# this solution contains all the planning units with finite cost data (i.e.
    -# cost data that do not have NA values)
    -plot(s5)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create a simple conservation planning dataset so we can see exactly
    +# how the feature abundances are calculated
    +pu <- data.frame(id = seq_len(10), cost = c(0.2, NA, runif(8)),
    +                 spp1 = runif(10), spp2 = c(rpois(9, 4), NA))
    +
    +# create problem
    +p1 <- problem(pu, c("spp1", "spp2"), cost_column = "cost")
    +
    +# calculate feature abundances; including planning units with NA costs
    +a1 <- feature_abundances(p1, na.rm = FALSE) # (default)
    +print(a1)
    +#> # A tibble: 2 × 3
    +#>   feature absolute_abundance relative_abundance
    +#>   <chr>                <dbl>              <dbl>
    +#> 1 spp1                  4.10                  1
    +#> 2 spp2                 31                     1
    +
    +# calculate feature abundances; excluding planning units with NA costs
    +a2 <- feature_abundances(p1, na.rm = TRUE)
    +print(a2)
    +#> # A tibble: 2 × 3
    +#>   feature absolute_abundance relative_abundance
    +#>   <chr>                <dbl>              <dbl>
    +#> 1 spp1                  3.84              0.935
    +#> 2 spp2                 28                 0.903
    +
    +# verify correctness of feature abundance calculations
    +all.equal(a1$absolute_abundance,
    +          c(sum(pu$spp1), sum(pu$spp2, na.rm = TRUE)))
    +#> [1] TRUE
    +
    +all.equal(a1$relative_abundance,
    +          c(sum(pu$spp1) / sum(pu$spp1),
    +            sum(pu$spp2, na.rm = TRUE) / sum(pu$spp2, na.rm = TRUE)))
    +#> [1] TRUE
    +
    +all.equal(a2$absolute_abundance,
    +          c(sum(pu$spp1[!is.na(pu$cost)]),
    +            sum(pu$spp2[!is.na(pu$cost)], na.rm = TRUE)))
    +#> [1] TRUE
    +
    +all.equal(a2$relative_abundance,
    +          c(sum(pu$spp1[!is.na(pu$cost)]) / sum(pu$spp1, na.rm = TRUE),
    +            sum(pu$spp2[!is.na(pu$cost)], na.rm = TRUE) / sum(pu$spp2,
    +                                                              na.rm = TRUE)))
    +#> [1] TRUE
    +
    +# initialize conservation problem with raster data
    +p3 <- problem(sim_pu_raster, sim_features)
    +
    +# calculate feature abundances; including planning units with NA costs
    +a3 <- feature_abundances(p3, na.rm = FALSE) # (default)
    +print(a3)
    +#> # A tibble: 5 × 3
    +#>   feature absolute_abundance relative_abundance
    +#>   <chr>                <dbl>              <dbl>
    +#> 1 layer.1               83.3                  1
    +#> 2 layer.2               31.2                  1
    +#> 3 layer.3               72.0                  1
    +#> 4 layer.4               42.7                  1
    +#> 5 layer.5               56.7                  1
    +
    +# create problem using total amounts of features in all the planning units
    +# (including units with NA cost data)
    +p4 <- p3 %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(a3$relative_abundance) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# attempt to solve the problem, but we will see that this problem is
    +# infeasible because the targets cannot be met using only the planning units
    +# with finite cost data
    +# \dontrun{
    +s4 <- try(solve(p4))
    +#> Error in .local(a, b = b, ...) : 
    +#>   no solution found (e.g., due to problem infeasibility or time limits)
    +# }
    +# calculate feature abundances; excluding planning units with NA costs
    +a5 <- feature_abundances(p3, na.rm = TRUE)
    +print(a5)
    +#> # A tibble: 5 × 3
    +#>   feature absolute_abundance relative_abundance
    +#>   <chr>                <dbl>              <dbl>
    +#> 1 layer.1               74.5              0.895
    +#> 2 layer.2               28.1              0.900
    +#> 3 layer.3               64.9              0.902
    +#> 4 layer.4               38.2              0.895
    +#> 5 layer.5               50.7              0.893
    +
    +# create problem using total amounts of features in the planning units with
    +# finite cost data
    +p5 <- p3 %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(a5$relative_abundance) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s5 <- solve(p5)
    +
    +# plot the solution
    +# this solution contains all the planning units with finite cost data
    +# (i.e., cost data that do not have NA values)
    +plot(s5)
    +
    +# }
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/feature_names.html b/docs/reference/feature_names.html index 0e44a6225..89866267f 100644 --- a/docs/reference/feature_names.html +++ b/docs/reference/feature_names.html @@ -1,101 +1,18 @@ - - - - - - - -Feature names — feature_names • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Feature names — feature_names • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,69 +101,63 @@

    Feature names

    Extract the names of the features in an object.

    -
    feature_names(x)
    -
    -# S4 method for ConservationProblem
    -feature_names(x)
    -
    -# S4 method for ZonesRaster
    -feature_names(x)
    +    
    Usage,
    feature_names(x)
     
    -# S4 method for ZonesCharacter
    -feature_names(x)
    +# S4 method for ConservationProblem +feature_names(x) -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) or Zones() -object.

    +# S4 method for ZonesRaster +feature_names(x) -

    Value

    +# S4 method for ZonesCharacter +feature_names(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) or Zones() +object.

    +
    +
    +

    Value

    character feature names.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.2) %>%
    -     add_binary_decisions()
    -
    -# print feature names
    -print(feature_names(p))
    -#> [1] "layer.1" "layer.2" "layer.3" "layer.4" "layer.5"
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.2) %>%
    +     add_binary_decisions()
    +
    +# print feature names
    +print(feature_names(p))
    +#> [1] "layer.1" "layer.2" "layer.3" "layer.4" "layer.5"
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/importance.html b/docs/reference/importance.html index 1e88cd297..5f8d384b6 100644 --- a/docs/reference/importance.html +++ b/docs/reference/importance.html @@ -1,103 +1,20 @@ - - - - - - - -Evaluate solution importance — importance • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solution importance — importance • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Importance scores (also known as irreplaceability scores) can be used to assess the relative importance of planning units selected in a solution to a -conservation planning problem().

    +conservation planning problem().

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    The following methods are available for calculating importance scores:

    -
    - -
    eval_replacement_importance()

    Calculate importance scores using replacement costs (based +

    eval_replacement_importance()
    +

    Calculate importance scores using replacement costs (based on Cabeza and Moilanen 2006). These scores quantify the change in the objective -function (e.g. additional costs required to meet feature targets) of the +function (e.g., additional costs required to meet feature targets) of the optimal solution if a given planning unit in a solution cannot be acquired. They can (i) account for the cost of different planning units, (ii) account for multiple management zones, (iii) apply to any objective function, and (iv) identify truly irreplaceable planning units (denoted with infinite values).

    -
    eval_ferrier_importance()

    Calculate importance scores following Ferrier et al. (2000). + +

    eval_ferrier_importance()
    +

    Calculate importance scores following Ferrier et al. (2000). These scores measure importance based on how critical planning units are for meeting targets. They can only be applied to -conservation problems with a minimum set objective and a single zone (i.e. +conservation problems with a minimum set objective and a single zone (i.e., the classic Marxan-type problem). Furthermore---unlike the replacement cost scores---these scores provide a score for each feature within each planning unit, providing insight into why certain planning units are more important than other planning units.

    -
    eval_rare_richness_importance()

    Calculate importance scores using the rarity weighted richness metric + +

    eval_rare_richness_importance()
    +

    Calculate importance scores using the rarity weighted richness metric (based on Williams et al. 1996). These score are simply a measure of biodiversity. They do not account for planning costs, multiple management zones, objective @@ -224,24 +144,24 @@

    Details the importance of a planning unit for achieving conservation goals.

    -
    -

    Broadly speaking, we recommend using replacement cost scores where +

    Broadly speaking, we recommend using replacement cost scores where possible. This is because they can be applied to any type of conservation planning problem -- regardless of the objective function or number of zones considered in the problem -- and measure planning unit importance based on degradation of the prioritization. Although the replacement cost scores can be calculated for small and -moderate sized problems (e.g. less than 30,000 planning units), they may not -be feasible for particularly large problems (e.g. more than 100,000 planning +moderate sized problems (e.g., less than 30,000 planning units), they may not +be feasible for particularly large problems (e.g., more than 100,000 planning units). In such cases, we recommend calculating importance scores using the Ferrier method. This is because the Ferrier method can be calculated relatively quickly for large-sized problems and it explicitly accounts for representation targets. We only recommend using the rarity weighted richness metric when neither of the other two methods can be used.

    -

    References

    - +
    +
    +

    References

    Cabeza M and Moilanen A (2006) Replacement cost: A practical measure of site value for cost-effective reserve planning. Biological Conservation, 132: 336--342.

    @@ -253,78 +173,77 @@

    R (1996) A comparison of richness hotspots, rarity hotspots and complementary areas for conserving diversity using British birds. Conservation Biology, 10: 155--174.

    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # \dontrun{
    -# load data
    -data(sim_pu_raster, sim_pu_polygons, sim_features)
    -
    -# build minimal conservation problem with raster data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(gap = 0, verbose = FALSE)
    -
    -# solve the problem
    -s1 <- solve(p1)
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# calculate importance scores using replacement cost scores
    -ir1 <- eval_replacement_importance(p1, s1)
    -
    -# calculate importance scores using Ferrier et al 2000 method,
    -# and extract the total importance scores
    -ir2 <- eval_ferrier_importance(p1, s1)[["total"]]
    -
    -# calculate importance scores using rarity weighted richness scores
    -ir3 <- eval_rare_richness_importance(p1, s1)
    -
    -# plot importance scores
    -plot(stack(ir1, ir2, ir3), axes = FALSE, box = FALSE,
    -     main = c("replacement cost", "Ferrier score",
    -              "rarity weighted richness"))
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# load data
    +data(sim_pu_raster, sim_pu_polygons, sim_features)
    +
    +# build minimal conservation problem with raster data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0, verbose = FALSE)
    +
    +# solve the problem
    +s1 <- solve(p1)
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# calculate importance scores using replacement cost scores
    +ir1 <- eval_replacement_importance(p1, s1)
    +
    +# calculate importance scores using Ferrier et al 2000 method,
    +# and extract the total importance scores
    +ir2 <- eval_ferrier_importance(p1, s1)[["total"]]
    +
    +# calculate importance scores using rarity weighted richness scores
    +ir3 <- eval_rare_richness_importance(p1, s1)
    +
    +# plot importance scores
    +plot(stack(ir1, ir2, ir3), axes = FALSE, box = FALSE,
    +     main = c("replacement cost", "Ferrier score",
    +              "rarity weighted richness"))
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/index.html b/docs/reference/index.html index 4f875f4de..ddf973f0f 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -1,100 +1,18 @@ - - - - - - - -Function reference • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Function reference • prioritizr - - - - - - - - - - - - - - +
    -
    -
    - - - - - - - - - - -
    -

    Overview

    -

    Overview of the package

    + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - + + + + + + + + + + - - - + + + + + + - - - + + - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - + - - - - + - - - - + - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - - + - - - - + + + - - - + + + + - - - - - - - - - - - - - - - - - + - - - - + - - - - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - - - - - - - - - - + + - - - + + + + - - - - - - - - - - - - - + + + + + + - - - - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - - + - - - - - - - - - - - - - - + - - - - + - - - - + - - - - + - - - - + - - - - + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    +

    Overview

    +

    Overview of the package

    +

    prioritizr

    prioritizr: Systematic Conservation Prioritization in R

    -

    Create and solve problems

    -

    Functions for creating new problems and solving them

    +
    +

    Create and solve problems

    +

    Functions for creating new problems and solving them

    +

    problem()

    Conservation planning problem

    +

    marxan_problem()

    Marxan conservation problem

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    Solve

    -

    Data

    -

    Simulated datasets distributed with the package

    +
    +

    Data

    +

    Simulated datasets distributed with the package

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    Simulated conservation planning data

    -

    Objectives

    -

    Functions for adding an objective to a problem

    +
    +

    Objectives

    +

    Functions for adding an objective to a problem

    +

    objectives

    Add an objective

    +

    add_max_cover_objective()

    Add maximum coverage objective

    +

    add_max_features_objective()

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    Add minimum set objective

    +

    add_min_shortfall_objective()

    Add minimum shortfall objective

    -

    Targets

    -

    Functions for adding targets to a problem

    -
    -

    targets

    +
    +

    ArrayParameter-class

    Add representation targets

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    add_absolute_targets()

    Add absolute targets

    -

    add_loglinear_targets()

    +
    +

    add_binary_decisions()

    Add targets using log-linear scaling

    -

    add_manual_targets()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    Add manual targets

    -

    add_relative_targets()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    Add relative targets

    -

    Constraints

    -

    Functions for adding constraints to a problem

    -
    -

    constraints

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    Conservation problem constraints

    +

    Add connectivity penalties

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    Add contiguity constraints

    +
    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    Add feature contiguity constraints

    +
    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    Add linear constraints

    +
    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    add_locked_in_constraints()

    Add locked in constraints

    +

    add_locked_out_constraints()

    Add locked out constraints

    +
    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    add_mandatory_allocation_constraints(<ConservationProblem>)

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    Add manually specified locked constraints

    -

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +
    +

    add_manual_targets()

    Add neighbor constraints

    -

    Penalties

    -

    Functions for adding penalties to a problem

    -
    -

    penalties

    +

    Add manual targets

    +

    add_max_cover_objective()

    Add a penalty

    -

    add_boundary_penalties()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    Add boundary penalties

    -

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    Add connectivity penalties

    -

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    Add linear penalties

    -

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    Add feature weights

    -

    Decisions

    -

    Functions for specifying the type of decisions in a problem

    -
    -

    decisions

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    Add decision types

    -

    add_binary_decisions()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    Add binary decisions

    -

    add_default_decisions()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    Add default decisions

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    add_proportion_decisions()

    Add proportion decisions

    +
    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    add_semicontinuous_decisions()

    Add semi-continuous decisions

    -

    Solvers

    -

    Functions for specifying how a problem should be solved

    -
    -

    solvers

    +
    +

    add_shuffle_portfolio()

    Problem solvers

    -

    add_cbc_solver()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    Add a CBC solver

    -

    add_cplex_solver()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    Add a CPLEX solver

    -

    add_default_solver()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    Add a default solver

    -

    add_gurobi_solver()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Targets

    +

    Functions for adding targets to a problem

    +
    +

    targets

    +

    Add representation targets

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_relative_targets()

    +

    Add relative targets

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Constraints

    +

    Functions for adding constraints to a problem

    +
    +

    constraints

    +

    Conservation problem constraints

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_connected_constraints() add_corridor_constraints() set_number_of_threads() get_number_of_threads() is.parallel() add_pool_portfolio() connected_matrix() feature_representation() replacement_cost() rarity_weighted_richness() ferrier_score()

    +

    Deprecation notice

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Penalties

    +

    Functions for adding penalties to a problem

    +
    +

    penalties

    +

    Add a penalty

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Decisions

    +

    Functions for specifying the type of decisions in a problem

    +
    +

    decisions

    +

    Add decision types

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Solvers

    +

    Functions for specifying how a problem should be solved

    +
    +

    solvers

    +

    Problem solvers

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Portfolios

    +

    Functions for generating a portfolio of solutions

    +
    +

    portfolios

    +

    Solution portfolios

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    add_connected_constraints() add_corridor_constraints() set_number_of_threads() get_number_of_threads() is.parallel() add_pool_portfolio() connected_matrix() feature_representation() replacement_cost() rarity_weighted_richness() ferrier_score()

    +

    Deprecation notice

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Summary statistics

    +

    Functions for summarizing the performance of solutions

    +
    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Importance

    +

    Functions for calculating importance scores for a solution

    +
    +

    importance

    +

    Evaluate solution importance

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Data simulation

    +

    Functions for simulating new datasets

    +
    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    +

    Binary stack

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    compile()

    +

    Compile a problem

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    constraints

    +

    Conservation problem constraints

    +

    decisions

    +

    Add decision types

    +

    distribute_load()

    +

    Distribute load

    +

    eval_boundary_summary()

    +

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Evaluate solution connectivity

    +

    eval_cost_summary()

    +

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    +

    Evaluate feature representation

    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    +

    eval_n_summary()

    +

    Evaluate number of planning units selected

    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    +

    eval_target_coverage_summary()

    +

    Evaluate target coverage

    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    +

    importance

    +

    Evaluate solution importance

    +

    intersecting_units()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    +

    Is it?

    +

    loglinear_interpolation()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    +

    Matrix parameters

    +

    misc_parameter()

    +

    Miscellaneous parameter

    +

    new_id()

    +

    Identifier

    +

    new_optimization_problem()

    +

    Optimization problem

    +

    new_waiver()

    +

    Waiver

    +

    number_of_features()

    +

    Number of features

    +

    number_of_planning_units()

    +

    Number of planning units

    +

    number_of_total_units()

    +

    Number of total units

    +

    number_of_zones()

    +

    Number of zones

    +

    objectives

    +

    Add an objective

    +

    parameters()

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Geoprocessing

    +

    Functions for manipulating spatial datasets

    +
    +

    fast_extract()

    +

    Fast extract

    +

    intersecting_units()

    +

    Find intersecting units

    +

    Processing multi-zone data

    +

    Functions for manipulating data that pertain to multiple zones

    +
    +

    category_layer()

    +

    Category layer

    +

    category_vector()

    +

    Category vector

    +

    binary_stack()

    +

    Binary stack

    +

    Matrix functions

    +

    Functions for creating matrices that are used in conservation planning problems for use in planning problems

    +
    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    boundary_matrix()

    +

    Boundary matrix

    +

    branch_matrix()

    +

    Branch matrix

    +

    connectivity_matrix()

    +

    Connectivity matrix

    +

    marxan_boundary_data_to_matrix()

    +

    Convert Marxan boundary data to a matrix format

    +

    add_connected_constraints() add_corridor_constraints() set_number_of_threads() get_number_of_threads() is.parallel() add_pool_portfolio() connected_matrix() feature_representation() replacement_cost() rarity_weighted_richness() ferrier_score()

    +

    Deprecation notice

    +

    proximity_matrix()

    +

    Proximity matrix

    +

    rij_matrix()

    +

    Feature by planning unit matrix

    +

    ArrayParameter-class

    +

    Array parameter prototype

    +

    Collection-class

    +

    Collection prototype

    +

    ConservationModifier-class

    +

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    +

    Conservation problem class

    +

    Constraint-class

    +

    Constraint prototype

    +

    Decision-class

    +

    Decision prototype

    +

    MiscParameter-class

    +

    Miscellaneous parameter prototype

    +

    Objective-class

    +

    Objective prototype

    +

    OptimizationProblem-class

    +

    Optimization problem class

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    +

    Optimization problem methods

    +

    Parameter-class

    +

    Parameter class

    +

    Parameters-class

    +

    Parameters class

    +

    Penalty-class

    +

    Penalty prototype

    +

    Portfolio-class

    +

    Portfolio prototype

    +

    ScalarParameter-class

    +

    Scalar parameter prototype

    +

    Solver-class

    +

    Solver prototype

    +

    Target-class

    +

    Target prototype

    +

    add_absolute_targets()

    +

    Add absolute targets

    +

    add_binary_decisions()

    +

    Add binary decisions

    +

    add_boundary_penalties()

    +

    Add boundary penalties

    +

    add_cbc_solver()

    +

    Add a CBC solver

    +

    add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) add_connectivity_penalties(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add connectivity penalties

    +

    add_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>)

    +

    Add contiguity constraints

    +

    add_cplex_solver()

    +

    Add a CPLEX solver

    +

    add_cuts_portfolio()

    +

    Add Bender's cuts portfolio

    +

    add_default_decisions()

    +

    Add default decisions

    +

    add_default_solver()

    +

    Add a default solver

    +

    add_extra_portfolio()

    +

    Add an extra portfolio

    +

    add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<Matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<data.frame>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<matrix>) add_feature_contiguity_constraints(<ConservationProblem>,<ANY>,<ANY>)

    +

    Add feature contiguity constraints

    +

    add_feature_weights(<ConservationProblem>,<numeric>) add_feature_weights(<ConservationProblem>,<matrix>)

    +

    Add feature weights

    +

    add_gap_portfolio()

    +

    Add a gap portfolio

    +

    add_gurobi_solver()

    +

    Add a Gurobi solver

    +

    add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<character>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<numeric>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<Raster>) add_linear_constraints(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>)

    +

    Add linear constraints

    +

    add_linear_penalties(<ConservationProblem>,<ANY>,<character>) add_linear_penalties(<ConservationProblem>,<ANY>,<numeric>) add_linear_penalties(<ConservationProblem>,<ANY>,<matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Matrix>) add_linear_penalties(<ConservationProblem>,<ANY>,<Raster>) add_linear_penalties(<ConservationProblem>,<ANY>,<dgCMatrix>)

    +

    Add linear penalties

    +

    add_locked_in_constraints()

    +

    Add locked in constraints

    +

    add_locked_out_constraints()

    +

    Add locked out constraints

    +

    add_loglinear_targets()

    +

    Add targets using log-linear scaling

    +

    add_lpsymphony_solver()

    +

    Add a SYMPHONY solver with lpsymphony

    +

    add_mandatory_allocation_constraints(<ConservationProblem>)

    +

    Add mandatory allocation constraints

    +

    add_manual_bounded_constraints()

    +

    Add manually specified bound constraints

    +

    add_manual_locked_constraints()

    +

    Add manually specified locked constraints

    +

    add_manual_targets()

    +

    Add manual targets

    +

    add_max_cover_objective()

    +

    Add maximum coverage objective

    +

    add_max_features_objective()

    +

    Add maximum feature representation objective

    +

    add_max_phylo_div_objective()

    +

    Add maximum phylogenetic diversity objective

    +

    add_max_phylo_end_objective()

    +

    Add maximum phylogenetic endemism objective

    +

    add_max_utility_objective()

    +

    Add maximum utility objective

    +

    add_min_largest_shortfall_objective()

    +

    Add minimum largest shortfall objective

    +

    add_min_set_objective()

    +

    Add minimum set objective

    +

    add_min_shortfall_objective()

    +

    Add minimum shortfall objective

    +

    add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<ANY>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<matrix>) add_neighbor_constraints(<ConservationProblem>,<ANY>,<ANY>,<array>)

    +

    Add neighbor constraints

    +

    add_proportion_decisions()

    +

    Add proportion decisions

    +

    add_relative_targets()

    +

    Add relative targets

    +

    add_rsymphony_solver()

    +

    Add a SYMPHONY solver with Rsymphony

    +

    add_semicontinuous_decisions()

    +

    Add semi-continuous decisions

    +

    add_shuffle_portfolio()

    +

    Add a shuffle portfolio

    +

    add_top_portfolio()

    +

    Add a top portfolio

    +

    adjacency_matrix()

    +

    Adjacency matrix

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    +

    Array parameters

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    +

    Coerce object to another object

    +

    as.list(<OptimizationProblem>)

    +

    Convert OptimizationProblem to list

    +

    binary_stack()

    Add a Gurobi solver

    -

    add_lpsymphony_solver()

    +

    Binary stack

    +

    boundary_matrix()

    Add a SYMPHONY solver with lpsymphony

    -

    add_rsymphony_solver()

    +

    Boundary matrix

    +

    branch_matrix()

    Add a SYMPHONY solver with Rsymphony

    -

    Portfolios

    -

    Functions for generating a portfolio of solutions

    -
    -

    portfolios

    +

    Branch matrix

    +

    category_layer()

    Solution portfolios

    -

    add_cuts_portfolio()

    +

    Category layer

    +

    category_vector()

    Add Bender's cuts portfolio

    -

    add_extra_portfolio()

    +

    Category vector

    +

    compile()

    Add an extra portfolio

    -

    add_gap_portfolio()

    +

    Compile a problem

    +

    connectivity_matrix()

    Add a gap portfolio

    -

    add_shuffle_portfolio()

    +

    Connectivity matrix

    +

    constraints

    Add a shuffle portfolio

    -

    add_top_portfolio()

    +

    Conservation problem constraints

    +

    decisions

    Add a top portfolio

    -

    Summary statistics

    -

    Functions for summarizing the performance of solutions

    -
    -

    summaries

    +

    Add decision types

    +

    distribute_load()

    Evaluate solutions using summary statistics

    +

    Distribute load

    eval_boundary_summary()

    Evaluate solution boundary length

    +

    eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<Matrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<data.frame>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<dgCMatrix>) eval_connectivity_summary(<ConservationProblem>,<ANY>,<ANY>,<array>)

    Evaluate solution connectivity

    +

    eval_cost_summary()

    Evaluate solution cost

    +

    eval_feature_representation_summary()

    Evaluate feature representation

    +
    +

    eval_ferrier_importance()

    +

    Evaluate solution importance using Ferrier scores

    eval_n_summary()

    Evaluate number of planning units selected

    +
    +

    eval_rare_richness_importance()

    +

    Evaluate solution importance using rarity weighted richness scores

    +

    eval_replacement_importance()

    +

    Evaluate solution importance using replacement cost scores

    eval_target_coverage_summary()

    Evaluate target coverage

    -

    Importance

    -

    Functions for calculating importance scores for a solution

    -
    +
    +

    fast_extract()

    +

    Fast extract

    +

    feature_abundances()

    +

    Feature abundances

    +

    feature_names()

    +

    Feature names

    importance

    Evaluate solution importance

    -

    eval_ferrier_importance()

    +
    +

    intersecting_units()

    Evaluate solution importance using Ferrier scores

    -

    eval_rare_richness_importance()

    +

    Find intersecting units

    +

    is.Id() is.Waiver()

    Evaluate solution importance using rarity weighted richness scores

    -

    eval_replacement_importance()

    +

    Is it?

    +

    loglinear_interpolation()

    Evaluate solution importance using replacement cost scores

    -

    Data simulation

    -

    Functions for simulating new datasets

    -
    -

    simulate_cost()

    +

    Log-linear interpolation

    +

    marxan_boundary_data_to_matrix()

    Simulate cost data

    -

    simulate_data()

    +

    Convert Marxan boundary data to a matrix format

    +

    marxan_problem()

    Simulate data

    -

    simulate_species()

    +

    Marxan conservation problem

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    Simulate species habitat suitability data

    -

    Geoprocessing

    -

    Functions for manipulating spatial datasets

    -
    -

    fast_extract()

    +

    Matrix parameters

    +

    misc_parameter()

    Fast extract

    -

    intersecting_units()

    +

    Miscellaneous parameter

    +

    new_id()

    Find intersecting units

    -

    Processing multi-zone data

    -

    Functions for manipulating data that pertain to multiple zones

    -
    -

    category_layer()

    +

    Identifier

    +

    new_optimization_problem()

    Category layer

    -

    category_vector()

    +

    Optimization problem

    +

    new_waiver()

    Category vector

    -

    binary_stack()

    +

    Waiver

    +

    number_of_features()

    Binary stack

    -

    Matrix functions

    -

    Functions for creating matrices that are used in conservation planning problems for use in planning problems

    -
    -

    adjacency_matrix()

    +

    Number of features

    +

    number_of_planning_units()

    Adjacency matrix

    -

    boundary_matrix()

    +

    Number of planning units

    +

    number_of_total_units()

    Boundary matrix

    -

    branch_matrix()

    +

    Number of total units

    +

    number_of_zones()

    Branch matrix

    -

    connectivity_matrix()

    +

    Number of zones

    +

    objectives

    Connectivity matrix

    -

    marxan_boundary_data_to_matrix()

    +

    Add an objective

    +

    parameters()

    Convert Marxan boundary data to a matrix format

    +

    Parameters

    +

    penalties

    +

    Add a penalty

    +

    %>%

    +

    Pipe operator

    +

    portfolios

    +

    Solution portfolios

    +

    pproto()

    +

    Create a new pproto object

    +

    predefined_optimization_problem()

    +

    Predefined optimization problem

    +

    presolve_check()

    +

    Presolve check

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    +

    Print

    +

    prioritizr

    +

    prioritizr: Systematic Conservation Prioritization in R

    +

    problem()

    +

    Conservation planning problem

    proximity_matrix()

    Proximity matrix

    +

    rij_matrix()

    Feature by planning unit matrix

    -

    Problem manipulation functions

    -

    Functions for extracting information from problems

    +
    +

    run_calculations()

    +

    Run calculations

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    +

    Scalar parameters

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    +

    Show

    +

    sim_pu_polygons sim_pu_zones_polygons sim_pu_points sim_pu_lines sim_pu_sf sim_pu_zones_sf sim_pu_raster sim_locked_in_raster sim_locked_out_raster sim_pu_zones_stack sim_features sim_features_zones sim_phylogeny

    +

    Simulated conservation planning data

    +

    simulate_cost()

    +

    Simulate cost data

    +

    simulate_data()

    +

    Simulate data

    +

    simulate_species()

    +

    Simulate species habitat suitability data

    +

    solve(<OptimizationProblem>,<Solver>) solve(<ConservationProblem>,<missing>)

    +

    Solve

    +

    solvers

    +

    Problem solvers

    +

    summaries

    +

    Evaluate solutions using summary statistics

    +

    targets

    +

    Add representation targets

    +

    %T>%

    +

    Tee operator

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    +

    Manipulate tibbles

    +

    write_problem()

    +

    Write problem

    +

    zone_names()

    +

    Zone names

    +

    zones()

    +

    Management zones

    +

    Problem manipulation functions

    +

    Functions for extracting information from problems

    +

    number_of_features()

    Number of features

    +

    number_of_planning_units()

    Number of planning units

    +

    number_of_total_units()

    Number of total units

    +

    number_of_zones()

    Number of zones

    +

    feature_names()

    Feature names

    +

    feature_abundances()

    Feature abundances

    +

    zone_names()

    Zone names

    -

    Miscellaneous functions

    -

    Assorted functions distributed with the package

    +
    +

    Miscellaneous functions

    +

    Assorted functions distributed with the package

    +

    print(<ConservationProblem>) print(<ConservationModifier>) print(<Id>) print(<Id>) print(<OptimizationProblem>) print(<ScalarParameter>) print(<ArrayParameter>) print(<Solver>) print(<Zones>) print(<tbl_df>)

    Print

    +

    show(<ConservationModifier>) show(<ConservationProblem>) show(<Id>) show(<OptimizationProblem>) show(<Parameter>) show(<Solver>)

    Show

    -

    %>%

    +
    +

    %>%

    Pipe operator

    -

    %T>%

    +
    +

    %T>%

    Tee operator

    +

    is.Id() is.Waiver()

    Is it?

    +

    as.Id() as.list(<Parameters>) as.list(<Zones>)

    Coerce object to another object

    +

    compile()

    Compile a problem

    +

    presolve_check()

    Presolve check

    +

    loglinear_interpolation()

    Log-linear interpolation

    +

    run_calculations()

    Run calculations

    +

    distribute_load()

    Distribute load

    +

    write_problem()

    Write problem

    -

    Class definitions and methods

    -

    These pages document the package’s internal data structures and functions for manipulating them—they contain information that is really only useful when adding new functionality to the package

    +
    +

    Class definitions and methods

    +

    These pages document the package’s internal data structures and functions for manipulating them—they contain information that is really only useful when adding new functionality to the package

    +

    new_id()

    Identifier

    +

    new_waiver()

    Waiver

    +

    pproto()

    Create a new pproto object

    +

    new_optimization_problem()

    Optimization problem

    +

    predefined_optimization_problem()

    Predefined optimization problem

    +

    as.list(<OptimizationProblem>)

    Convert OptimizationProblem to list

    +

    ArrayParameter-class

    Array parameter prototype

    +

    Collection-class

    Collection prototype

    +

    ConservationModifier-class

    Conservation problem modifier prototype

    +

    ConservationProblem-class

    Conservation problem class

    +

    Constraint-class

    Constraint prototype

    +

    Decision-class

    Decision prototype

    +

    MiscParameter-class

    Miscellaneous parameter prototype

    +

    Objective-class

    Objective prototype

    +

    OptimizationProblem-class

    Optimization problem class

    +

    Parameter-class

    Parameter class

    +

    Parameters-class

    Parameters class

    +

    Penalty-class

    Penalty prototype

    +

    Portfolio-class

    Portfolio prototype

    +

    ScalarParameter-class

    Scalar parameter prototype

    +

    Solver-class

    Solver prototype

    +

    Target-class

    Target prototype

    +

    zones()

    Management zones

    +

    nrow() ncol() ncell() modelsense() vtype() obj() A() rhs() sense() lb() ub() col_ids() row_ids() compressed_formulation()

    Optimization problem methods

    +

    nrow(<tbl_df>) ncol(<tbl_df>) as.list(<tbl_df>)

    Manipulate tibbles

    -

    Parameter definitions

    -

    These pages document the package’s internal data structures for representing different types of variables—they contain information that is really only useful when adding new functionality to the package

    +
    +

    Parameter definitions

    +

    These pages document the package’s internal data structures for representing different types of variables—they contain information that is really only useful when adding new functionality to the package

    +

    proportion_parameter_array() binary_parameter_array() integer_parameter_array() numeric_parameter_array()

    Array parameters

    +

    numeric_matrix_parameter() binary_matrix_parameter()

    Matrix parameters

    +

    misc_parameter()

    Miscellaneous parameter

    +

    proportion_parameter() binary_parameter() integer_parameter() numeric_parameter()

    Scalar parameters

    +

    parameters()

    Parameters

    -

    Deprecated functions

    +
    +

    Deprecated functions

    +

    add_connected_constraints() add_corridor_constraints() set_number_of_threads() get_number_of_threads() is.parallel() add_pool_portfolio() connected_matrix() feature_representation() replacement_cost() rarity_weighted_richness() ferrier_score()

    Deprecation notice

    - +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/intersecting_units.html b/docs/reference/intersecting_units.html index c2dacf2dc..088f7c14f 100644 --- a/docs/reference/intersecting_units.html +++ b/docs/reference/intersecting_units.html @@ -1,102 +1,19 @@ - - - - - - - -Find intersecting units — intersecting_units • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Find intersecting units — intersecting_units • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,132 +103,125 @@

    Find intersecting units

    with the units in another spatial data object.

    -
    intersecting_units(x, y)
    +    
    Usage,
    intersecting_units(x, y)
     
    -# S4 method for Raster,Raster
    -intersecting_units(x, y)
    +# S4 method for Raster,Raster
    +intersecting_units(x, y)
     
    -# S4 method for Spatial,Spatial
    -intersecting_units(x, y)
    +# S4 method for Spatial,Spatial
    +intersecting_units(x, y)
     
    -# S4 method for sf,Spatial
    -intersecting_units(x, y)
    +# S4 method for sf,Spatial
    +intersecting_units(x, y)
     
    -# S4 method for Spatial,Raster
    -intersecting_units(x, y)
    +# S4 method for Spatial,Raster
    +intersecting_units(x, y)
     
    -# S4 method for Spatial,sf
    -intersecting_units(x, y)
    +# S4 method for Spatial,sf
    +intersecting_units(x, y)
     
    -# S4 method for Raster,Spatial
    -intersecting_units(x, y)
    +# S4 method for Raster,Spatial
    +intersecting_units(x, y)
     
    -# S4 method for sf,sf
    -intersecting_units(x, y)
    +# S4 method for sf,sf
    +intersecting_units(x, y)
     
    -# S4 method for Raster,sf
    -intersecting_units(x, y)
    +# S4 method for Raster,sf
    +intersecting_units(x, y)
     
    -# S4 method for sf,Raster
    -intersecting_units(x, y)
    +# S4 method for sf,Raster
    +intersecting_units(x, y)
     
    -# S4 method for data.frame,ANY
    -intersecting_units(x, y)
    - -

    Arguments

    - - - - - - - - - - -
    x

    Spatial or Raster object.

    y

    Spatial or Raster object.

    - -

    Value

    +# S4 method for data.frame,ANY +intersecting_units(x, y)
    +
    +

    Arguments

    +
    x
    +

    Spatial or Raster object.

    +
    y
    +

    Spatial or Raster object.

    +
    +
    +

    Value

    integer indices of the units in x that intersect with y.

    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # create data
    -r <- raster(matrix(1:9, byrow = TRUE, ncol=3))
    -r_with_holes <- r
    -r_with_holes[c(1, 5, 9)] <- NA
    -ply <- rasterToPolygons(r)
    -ply_with_holes <- st_as_sf(rasterToPolygons(r_with_holes))
    -
    -# intersect raster with raster
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(r, main = "x=Raster")
    -plot(r_with_holes, main = "y=Raster")
    -
    -# }
    -print(intersecting_units(r, r_with_holes))
    -#> [1] 2 3 4 6 7 8
    -
    -# intersect raster with polygons (sf)
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(r, main = "x=Raster")
    -plot(ply_with_holes, main = "y=sf", key.pos = NULL, reset = FALSE)
    -
    -# }
    -print(intersecting_units(r, ply_with_holes))
    -#> [1] 2 3 4 6 7 8
    -
    -# intersect polygons (Spatial) with raster
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(ply, main = "x=Spatial")
    -plot(r_with_holes, main = "y=Raster")
    -
    -# }
    -print(intersecting_units(ply, r_with_holes))
    -#> [1] 2 3 4 5 6 7 8
    -
    -# intersect polygons (Spatial) with polygons (sf)
    -# \dontrun{
    -par(mfrow = c(1, 2))
    -plot(ply, main = "x=Spatial")
    -plot(ply_with_holes, main = "y=sf", key.pos = NULL, reset = FALSE)
    -
    -# }
    -print(intersecting_units(ply, ply_with_holes))
    -#> [1] 2 3 4 6 7 8
    -
    +    
    +

    Examples

    +
    # create data
    +r <- raster(matrix(1:9, byrow = TRUE, ncol=3))
    +r_with_holes <- r
    +r_with_holes[c(1, 5, 9)] <- NA
    +ply <- rasterToPolygons(r)
    +ply_with_holes <- st_as_sf(rasterToPolygons(r_with_holes))
    +
    +# intersect raster with raster
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(r, main = "x=Raster")
    +plot(r_with_holes, main = "y=Raster")
    +
    +# }
    +print(intersecting_units(r, r_with_holes))
    +#> [1] 2 3 4 6 7 8
    +
    +# intersect raster with polygons (sf)
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(r, main = "x=Raster")
    +plot(ply_with_holes, main = "y=sf", key.pos = NULL, reset = FALSE)
    +
    +# }
    +print(intersecting_units(r, ply_with_holes))
    +#> [1] 2 3 4 6 7 8
    +
    +# intersect polygons (Spatial) with raster
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(ply, main = "x=Spatial")
    +plot(r_with_holes, main = "y=Raster")
    +
    +# }
    +print(intersecting_units(ply, r_with_holes))
    +#> [1] 2 3 4 5 6 7 8
    +
    +# intersect polygons (Spatial) with polygons (sf)
    +# \dontrun{
    +par(mfrow = c(1, 2))
    +plot(ply, main = "x=Spatial")
    +plot(ply_with_holes, main = "y=sf", key.pos = NULL, reset = FALSE)
    +
    +# }
    +print(intersecting_units(ply, ply_with_holes))
    +#> [1] 2 3 4 6 7 8
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/is.html b/docs/reference/is.html index 350b69040..27a418ab7 100644 --- a/docs/reference/is.html +++ b/docs/reference/is.html @@ -1,101 +1,18 @@ - - - - - - - -Is it? — is.Id • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Is it? — is.Id • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,47 +101,39 @@

    Is it?

    Test if an object inherits from a class.

    -
    is.Id(x)
    -
    -is.Waiver(x)
    +
    Usage,
    is.Id(x)
     
    -    

    Arguments

    - - - - - - -
    x

    Object.

    - -

    Value

    +is.Waiver(x)
    +
    +

    Arguments

    +
    x
    +

    Object.

    +
    +
    +

    Value

    logical indicating if it inherits from the class.

    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/loglinear_interpolation-1.png b/docs/reference/loglinear_interpolation-1.png index 0fb08f8a9ebedd310dc5b71e45a15b93610f27c0..dfcc969078d76927ee27e774d78e467c14826bb4 100644 GIT binary patch literal 25515 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 ZHNsa16S_X9XRCklIO6s1x - - - - - - -Log-linear interpolation — loglinear_interpolation • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Log-linear interpolation — loglinear_interpolation • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,129 +101,116 @@

    Log-linear interpolation

    Log-linearly interpolate values between two thresholds.

    -
    loglinear_interpolation(
    -  x,
    -  coordinate_one_x,
    -  coordinate_one_y,
    -  coordinate_two_x,
    -  coordinate_two_y
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    x

    numeric x values for which interpolate y -values.

    coordinate_one_x

    numeric value for lower x-coordinate.

    coordinate_one_y

    numeric value for lower y-coordinate.

    coordinate_two_x

    numeric value for upper x-coordinate.

    coordinate_two_y

    numeric value for upper y-coordinate.

    - -

    Value

    - +
    Usage,
    loglinear_interpolation(
    +  x,
    +  coordinate_one_x,
    +  coordinate_one_y,
    +  coordinate_two_x,
    +  coordinate_two_y
    +)
    + +
    +

    Arguments

    +
    x
    +

    numeric x values for which interpolate y +values.

    +
    coordinate_one_x
    +

    numeric value for lower x-coordinate.

    +
    coordinate_one_y
    +

    numeric value for lower y-coordinate.

    +
    coordinate_two_x
    +

    numeric value for upper x-coordinate.

    +
    coordinate_two_y
    +

    numeric value for upper y-coordinate.

    +
    +
    +

    Value

    numeric values.

    -

    Details

    - +
    +
    +

    Details

    Values are log-linearly interpolated at the x-coordinates specified in x using the lower and upper coordinate arguments to define the line. Values lesser or greater than these numbers are assigned the minimum and maximum y coordinates.

    +
    -

    Examples

    -
    # create series of x-values
    -x <- seq(0, 1000)
    -
    -# interpolate y-values for the x-values given the two reference points:
    -# (200, 100) and (900, 15)
    -y <- loglinear_interpolation(x, 200, 100, 900, 15)
    -
    -# plot the interpolated values
    -# \dontrun{
    -plot(y ~ x)
    -
    -# add the reference points to the plot (shown in red)
    -points(x = c(200, 900), y = c(100, 15), pch = 18, col = "red", cex = 2)
    -
    -# }
    -
    -# this function can also be used to calculate representation targets
    -# following Rodrigues et al. (2014). For example, let's say that
    -# we had a set of species we were interested in calculating representation
    -# targets for and we had information on their range sizes (in km^2).
    -spp_range_size_km2 <- seq(0.01, 15000000, by = 100)
    -
    -# we can now use this function to calculate representation targets
    -# (expressed as a percentage of the species' range sizes) using
    -# the thresholds and cap sizes reported by Rodrigues et al. 2014
    -spp_target_percentage_rodrigues <-
    -  loglinear_interpolation(
    -    x = spp_range_size_km2,
    -    coordinate_one_x = 1000,
    -    coordinate_one_y = 1,
    -    coordinate_two_x = 250000,
    -    coordinate_two_y = 0.1) * 100
    -
    -# it is also common to apply a cap to the representation targets,
    -# so let's apply the cap these targets following Butchart et al. (2015)
    -spp_target_percentage_butchart <- ifelse(
    -  spp_range_size_km2 >= 10000000,
    -  (1000000 / spp_range_size_km2) * 100,
    -  spp_target_percentage_rodrigues)
    -
    -# plot species range sizes and representation targets
    -plot(spp_target_percentage_butchart ~ spp_range_size_km2,
    -  xlab = "Range size km^2" , ylab = "Representation target (%)", type = "l")
    -
    -
    -# plot species range sizes and representation targets on a log10 scale
    -plot(spp_target_percentage_butchart ~ log10(spp_range_size_km2),
    -  xlab = "Range size km^2" , ylab = "Representation target (%)",
    -  type = "l", xaxt = "n")
    -axis(1, pretty(log10(spp_range_size_km2)),
    -     10^pretty(log10(spp_range_size_km2))) 
    -
    -
    +    
    +

    Examples

    +
    # create series of x-values
    +x <- seq(0, 1000)
    +
    +# interpolate y-values for the x-values given the two reference points:
    +# (200, 100) and (900, 15)
    +y <- loglinear_interpolation(x, 200, 100, 900, 15)
    +
    +# plot the interpolated values
    +# \dontrun{
    +plot(y ~ x)
    +
    +# add the reference points to the plot (shown in red)
    +points(x = c(200, 900), y = c(100, 15), pch = 18, col = "red", cex = 2)
    +
    +# }
    +
    +# this function can also be used to calculate representation targets
    +# following Rodrigues et al. (2014). For example, let's say that
    +# we had a set of species we were interested in calculating representation
    +# targets for and we had information on their range sizes (in km^2).
    +spp_range_size_km2 <- seq(0.01, 15000000, by = 100)
    +
    +# we can now use this function to calculate representation targets
    +# (expressed as a percentage of the species' range sizes) using
    +# the thresholds and cap sizes reported by Rodrigues et al. 2014
    +spp_target_percentage_rodrigues <-
    +  loglinear_interpolation(
    +    x = spp_range_size_km2,
    +    coordinate_one_x = 1000,
    +    coordinate_one_y = 1,
    +    coordinate_two_x = 250000,
    +    coordinate_two_y = 0.1) * 100
    +
    +# it is also common to apply a cap to the representation targets,
    +# so let's apply the cap these targets following Butchart et al. (2015)
    +spp_target_percentage_butchart <- ifelse(
    +  spp_range_size_km2 >= 10000000,
    +  (1000000 / spp_range_size_km2) * 100,
    +  spp_target_percentage_rodrigues)
    +
    +# plot species range sizes and representation targets
    +plot(spp_target_percentage_butchart ~ spp_range_size_km2,
    +  xlab = "Range size km^2" , ylab = "Representation target (%)", type = "l")
    +
    +
    +# plot species range sizes and representation targets on a log10 scale
    +plot(spp_target_percentage_butchart ~ log10(spp_range_size_km2),
    +  xlab = "Range size km^2" , ylab = "Representation target (%)",
    +  type = "l", xaxt = "n")
    +axis(1, pretty(log10(spp_range_size_km2)),
    +     10^pretty(log10(spp_range_size_km2))) 
    +
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/marxan_boundary_data_to_matrix-1.png b/docs/reference/marxan_boundary_data_to_matrix-1.png index edb2a1dea11737097bd4b0456c056bc7ccb6da4d..73aac1771f0dc875a1f60090de75516e4d44f9c3 100644 GIT binary patch literal 14650 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 literal 14622 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 - - - - - - -Convert Marxan boundary data to a matrix format — marxan_boundary_data_to_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Convert Marxan boundary data to a matrix format — marxan_boundary_data_to_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -190,116 +107,108 @@

    Convert Marxan boundary data to a matrix format

    Convert a data.frame object that follows the Marxan format to a matrix format. This function is useful for converting data.frame objects to matrix or array objects that -are used by the various penalties and -constraints functions. If the boundary data contains data for +are used by the various penalties and +constraints functions. If the boundary data contains data for a single zone, then a matrix object is returned. Otherwise if the boundary data contains data for multiple zones, then an array is returned.

    -
    marxan_boundary_data_to_matrix(x, data)
    +
    Usage,
    marxan_boundary_data_to_matrix(x, data)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object that +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object that contains planning unit and zone data to ensure that the argument to data is converted correctly. This argument can be set to -NULL if checks are not required (not recommended).

    data

    data.frame object with the columns "id1", +NULL if checks are not required (not recommended).

    +
    data
    +

    data.frame object with the columns "id1", "id2", and "boundary". The columns "zone1" and -"zone2" can also be provided to indicate zone data.

    - -

    Value

    - -

    array or dgCMatrix sparse matrix object.

    +"zone2" can also be provided to indicate zone data.

    +
    +
    +

    Value

    +

    array or dgCMatrix sparse matrix object.

    +
    -

    Examples

    -
    # create marxan boundary with four planning units and one zone
    -bldf1 <- expand.grid(id1 = seq_len(4), id2 = seq_len(4))
    -bldf1$boundary <- 1
    -bldf1$boundary[bldf1$id1 == bldf1$id2] <- 0.5
    -
    -# convert to matrix
    -m1 <- marxan_boundary_data_to_matrix(NULL, bldf1)
    -
    -# visualize matrix
    -# \dontrun{
    -image(m1)
    -
    -# }
    -# create marxan boundary with three planning units and two zones
    -bldf2 <- expand.grid(id1 = seq_len(3), id2 = seq_len(3),
    -                     zone1 = c("z1", "z2"),
    -                     zone2 = c("z1", "z2"))
    -bldf2$boundary <- 1
    -bldf2$boundary[bldf2$id1 == bldf2$id2 & bldf2$zone1 == bldf2$zone2] <- 0.5
    -bldf2$boundary[bldf2$id1 == bldf2$id2 & bldf2$zone1 != bldf2$zone2] <- 0
    -
    -# convert to array
    -m2 <- marxan_boundary_data_to_matrix(NULL, bldf2)
    -
    -# print array
    -print(m2)
    -#> , , 1, 1
    -#> 
    -#>      [,1] [,2] [,3]
    -#> [1,]  0.5  1.0  1.0
    -#> [2,]  1.0  0.5  1.0
    -#> [3,]  1.0  1.0  0.5
    -#> 
    -#> , , 2, 1
    -#> 
    -#>      [,1] [,2] [,3]
    -#> [1,]    0    1    1
    -#> [2,]    1    0    1
    -#> [3,]    1    1    0
    -#> 
    -#> , , 1, 2
    -#> 
    -#>      [,1] [,2] [,3]
    -#> [1,]    0    1    1
    -#> [2,]    1    0    1
    -#> [3,]    1    1    0
    -#> 
    -#> , , 2, 2
    -#> 
    -#>      [,1] [,2] [,3]
    -#> [1,]  0.5  1.0  1.0
    -#> [2,]  1.0  0.5  1.0
    -#> [3,]  1.0  1.0  0.5
    -#> 
    +    
    +

    Examples

    +
    # create marxan boundary with four planning units and one zone
    +bldf1 <- expand.grid(id1 = seq_len(4), id2 = seq_len(4))
    +bldf1$boundary <- 1
    +bldf1$boundary[bldf1$id1 == bldf1$id2] <- 0.5
    +
    +# convert to matrix
    +m1 <- marxan_boundary_data_to_matrix(NULL, bldf1)
    +
    +# visualize matrix
    +# \dontrun{
    +image(m1)
    +
    +# }
    +# create marxan boundary with three planning units and two zones
    +bldf2 <- expand.grid(id1 = seq_len(3), id2 = seq_len(3),
    +                     zone1 = c("z1", "z2"),
    +                     zone2 = c("z1", "z2"))
    +bldf2$boundary <- 1
    +bldf2$boundary[bldf2$id1 == bldf2$id2 & bldf2$zone1 == bldf2$zone2] <- 0.5
    +bldf2$boundary[bldf2$id1 == bldf2$id2 & bldf2$zone1 != bldf2$zone2] <- 0
    +
    +# convert to array
    +m2 <- marxan_boundary_data_to_matrix(NULL, bldf2)
    +
    +# print array
    +print(m2)
    +#> , , 1, 1
    +#> 
    +#>      [,1] [,2] [,3]
    +#> [1,]  0.5  1.0  1.0
    +#> [2,]  1.0  0.5  1.0
    +#> [3,]  1.0  1.0  0.5
    +#> 
    +#> , , 2, 1
    +#> 
    +#>      [,1] [,2] [,3]
    +#> [1,]    0    1    1
    +#> [2,]    1    0    1
    +#> [3,]    1    1    0
    +#> 
    +#> , , 1, 2
    +#> 
    +#>      [,1] [,2] [,3]
    +#> [1,]    0    1    1
    +#> [2,]    1    0    1
    +#> [3,]    1    1    0
    +#> 
    +#> , , 2, 2
    +#> 
    +#>      [,1] [,2] [,3]
    +#> [1,]  0.5  1.0  1.0
    +#> [2,]  1.0  0.5  1.0
    +#> [3,]  1.0  1.0  0.5
    +#> 
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/marxan_problem.html b/docs/reference/marxan_problem.html index 9c7d754e7..bb7f7cac1 100644 --- a/docs/reference/marxan_problem.html +++ b/docs/reference/marxan_problem.html @@ -1,104 +1,21 @@ - - - - - - - -Marxan conservation problem — marxan_problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Marxan conservation problem — marxan_problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Create a conservation planning problem() following the +

    Create a conservation planning problem() following the mathematical formulations used in Marxan (detailed in Beyer et al. 2016). Note that these problems are solved using -exact algorithms and not simulated annealing (i.e. the Marxan software).

    +exact algorithms and not simulated annealing (i.e., the Marxan software).

    -
    marxan_problem(x, ...)
    +    
    Usage,
    marxan_problem(x, ...)
     
    -# S3 method for default
    -marxan_problem(x, ...)
    +# S3 method for default
    +marxan_problem(x, ...)
     
    -# S3 method for data.frame
    -marxan_problem(x, spec, puvspr, bound = NULL, blm = 0, ...)
    +# S3 method for data.frame
    +marxan_problem(x, spec, puvspr, bound = NULL, blm = 0, ...)
     
    -# S3 method for character
    -marxan_problem(x, ...)
    +# S3 method for character +marxan_problem(x, ...)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    x

    character file path for a Marxan input file (typically +

    +

    Arguments

    +
    x
    +

    character file path for a Marxan input file (typically called "input.dat"), or data.frame containing planning unit data (typically called "pu.dat"). If the argument to x is a data.frame, then each row corresponds to a different planning unit, and it must have the following columns:

    -
    - -
    id

    integer unique identifier for each planning unit. +

    id
    +

    integer unique identifier for each planning unit. These identifiers are used in the argument to puvspr.

    -
    cost

    numeric cost of each planning unit.

    -
    status

    integer indicating if each planning unit +

    cost
    +

    numeric cost of each planning unit.

    + + +
    status
    +

    integer indicating if each planning unit should not be locked in the solution (0) or if it should be locked in (2) or locked out (3) of the solution. Although Marxan allows planning units to be selected in the initial solution (using values of 1), these values have no effect here. This column is optional.

    -
    ...

    not used.

    spec

    data.frame containing information on the features. + + +

    ...
    +

    not used.

    +
    spec
    +

    data.frame containing information on the features. The argument to spec must follow the conventions used by Marxan for the species data file (conventionally called "spec.dat"). Each row corresponds to a different feature and @@ -242,85 +157,94 @@

    Arg spec must contain at least one column named "prop" or "amount"---but not both columns with both of these names---to specify the target for each feature.

    -
    - -
    id

    integer unique identifier for each feature +

    id
    +

    integer unique identifier for each feature These identifiers are used in the argument to puvspr.

    -
    name

    character name for each feature.

    -
    prop

    numeric relative target for each feature -(optional).

    ' +
    name
    +

    character name for each feature.

    + + +
    prop
    +

    numeric relative target for each feature +(optional).

    +' -
    amount

    numeric absolute target for each +

    amount
    +

    numeric absolute target for each feature (optional).

    -

    puvspr

    data.frame containing information on the amount of + + +

    puvspr
    +

    data.frame containing information on the amount of each feature in each planning unit. The argument to puvspr must follow the conventions used in the Marxan input data file (conventionally called "puvspr.dat"). It must contain the following columns:

    -
    +
    pu
    +

    integer planning unit identifier.

    + -
    pu

    integer planning unit identifier.

    +
    species
    +

    integer feature identifier.

    -
    species

    integer feature identifier.

    -
    amount

    numeric amount of the feature in the +

    amount
    +

    numeric amount of the feature in the planning unit.

    -
    bound

    NULL object indicating that no boundary data + + +

    bound
    +

    NULL object indicating that no boundary data is required for the conservation planning problem, or a data.frame containing information on the planning units' boundaries. The argument to bound must follow the conventions used in the Marxan input data file (conventionally called "bound.dat"). It must contain the following columns:

    -
    - -
    id1

    integer planning unit identifier.

    +
    id1
    +

    integer planning unit identifier.

    -
    id2

    integer planning unit identifier.

    -
    boundary

    numeric length of shared boundary -between the planning units identified in the previous two columns.

    +
    id2
    +

    integer planning unit identifier.

    -
    blm

    numeric boundary length modifier. This argument only -has an effect when argument to x is a data.frame. The -default argument is zero.

    +
    boundary
    +

    numeric length of shared boundary +between the planning units identified in the previous two columns.

    -

    Value

    -

    problem() (i.e. ConservationProblem) object.

    -

    Details

    + +
    blm
    +

    numeric boundary length modifier. This argument only +has an effect when argument to x is a data.frame. The +default argument is zero.

    +
    +
    +

    Value

    +

    problem() (i.e., ConservationProblem) object.

    +
    +
    +

    Details

    This function is provided as a convenient wrapper for solving Marxan problems using prioritizr.

    -

    Notes

    - +
    +
    +

    Notes

    In early versions, this function could accommodate asymmetric connectivity data. This functionality is no longer supported. To specify asymmetric -connectivity, please see the add_connectivity_penalties() function.

    -

    References

    - +connectivity, please see the add_connectivity_penalties() function.

    +
    +
    +

    References

    Ball IR, Possingham HP, and Watts M (2009) Marxan and relatives: Software for spatial conservation prioritisation in Spatial conservation prioritisation: Quantitative methods and computational tools. Eds Moilanen @@ -328,142 +252,141 @@

    R

    Beyer HL, Dujardin Y, Watts ME, and Possingham HP (2016) Solving conservation planning problems with integer linear programming. Ecological Modelling, 228: 14--22.

    -

    See also

    - -

    For more information on the correct format for +

    +
    +

    See also

    +

    For more information on the correct format for for Marxan input data, see the -official Marxan website and Ball +official Marxan website and Ball et al. (2009).

    +
    -

    Examples

    -
    # create Marxan problem using Marxan input file
    -input_file <- system.file("extdata/input.dat", package = "prioritizr")
    -p1 <- marxan_problem(input_file) %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s1 <- solve(p1)
    -
    -# print solution
    -head(s1)
    -#>   id       cost status    xloc     yloc locked_in locked_out solution_1
    -#> 1  3        0.0      0 1116623 -4493479     FALSE      FALSE          0
    -#> 2 30   752727.5      3 1110623 -4496943     FALSE       TRUE          0
    -#> 3 56  3734907.5      0 1092623 -4500408     FALSE      FALSE          0
    -#> 4 58  1695902.1      0 1116623 -4500408     FALSE      FALSE          0
    -#> 5 84  3422025.6      0 1098623 -4503872     FALSE      FALSE          0
    -#> 6 85 17890758.4      0 1110623 -4503872     FALSE      FALSE          0
    -# }
    -# create Marxan problem using data.frames that have been loaded into R
    -## load in planning unit data
    -pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr")
    -pu_dat <- data.table::fread(pu_path, data.table = FALSE)
    -head(pu_dat)
    -#>   id       cost status    xloc     yloc
    -#> 1  3        0.0      0 1116623 -4493479
    -#> 2 30   752727.5      3 1110623 -4496943
    -#> 3 56  3734907.5      0 1092623 -4500408
    -#> 4 58  1695902.1      0 1116623 -4500408
    -#> 5 84  3422025.6      0 1098623 -4503872
    -#> 6 85 17890758.4      0 1110623 -4503872
    -
    -## load in feature data
    -spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr")
    -spec_dat <- data.table::fread(spec_path, data.table = FALSE)
    -head(spec_dat)
    -#>   id prop spf   name
    -#> 1 10  0.3   1  bird1
    -#> 2 11  0.3   1  nvis2
    -#> 3 12  0.3   1  nvis8
    -#> 4 13  0.3   1  nvis9
    -#> 5 14  0.3   1 nvis14
    -#> 6 15  0.3   1 nvis20
    -
    -## load in planning unit vs feature data
    -puvspr_path <- system.file("extdata/input/puvspr.dat",
    -                           package = "prioritizr")
    -puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE)
    -head(puvspr_dat)
    -#>   species  pu     amount
    -#> 1      26  56 1203448.84
    -#> 2      26  58  451670.10
    -#> 3      26  84  680473.75
    -#> 4      26  85   97356.24
    -#> 5      26  86   78034.76
    -#> 6      26 111 4783274.17
    -
    -## load in the boundary data
    -bound_path <- system.file("extdata/input/bound.dat", package = "prioritizr")
    -bound_dat <- data.table::fread(bound_path, data.table = FALSE)
    -head(bound_dat)
    -#>   id1 id2 boundary
    -#> 1   3   3    16000
    -#> 2   3  30     4000
    -#> 3   3  58     4000
    -#> 4  30  30    12000
    -#> 5  30  58     4000
    -#> 6  30  85     4000
    -
    -# create problem without the boundary data
    -p2 <- marxan_problem(pu_dat, spec_dat, puvspr_dat) %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s2 <- solve(p2)
    -
    -# print solution
    -head(s2)
    -#>   id       cost status    xloc     yloc locked_in locked_out solution_1
    -#> 1  3        0.0      0 1116623 -4493479     FALSE      FALSE          0
    -#> 2 30   752727.5      3 1110623 -4496943     FALSE       TRUE          0
    -#> 3 56  3734907.5      0 1092623 -4500408     FALSE      FALSE          0
    -#> 4 58  1695902.1      0 1116623 -4500408     FALSE      FALSE          0
    -#> 5 84  3422025.6      0 1098623 -4503872     FALSE      FALSE          0
    -#> 6 85 17890758.4      0 1110623 -4503872     FALSE      FALSE          0
    -# }
    -# create problem with the boundary data and a boundary length modifier
    -# set to 5
    -p3 <- marxan_problem(pu_dat, spec_dat, puvspr_dat, bound_dat, 5) %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s3 <- solve(p3)
    -
    -# print solution
    -head(s3)
    -#>   id       cost status    xloc     yloc locked_in locked_out solution_1
    -#> 1  3        0.0      0 1116623 -4493479     FALSE      FALSE          0
    -#> 2 30   752727.5      3 1110623 -4496943     FALSE       TRUE          0
    -#> 3 56  3734907.5      0 1092623 -4500408     FALSE      FALSE          0
    -#> 4 58  1695902.1      0 1116623 -4500408     FALSE      FALSE          0
    -#> 5 84  3422025.6      0 1098623 -4503872     FALSE      FALSE          0
    -#> 6 85 17890758.4      0 1110623 -4503872     FALSE      FALSE          0
    -# }
    +    
    +

    Examples

    +
    # create Marxan problem using Marxan input file
    +input_file <- system.file("extdata/input.dat", package = "prioritizr")
    +p1 <- marxan_problem(input_file) %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s1 <- solve(p1)
    +
    +# print solution
    +head(s1)
    +#>   id       cost status    xloc     yloc locked_in locked_out solution_1
    +#> 1  3        0.0      0 1116623 -4493479     FALSE      FALSE          0
    +#> 2 30   752727.5      3 1110623 -4496943     FALSE       TRUE          0
    +#> 3 56  3734907.5      0 1092623 -4500408     FALSE      FALSE          0
    +#> 4 58  1695902.1      0 1116623 -4500408     FALSE      FALSE          0
    +#> 5 84  3422025.6      0 1098623 -4503872     FALSE      FALSE          0
    +#> 6 85 17890758.4      0 1110623 -4503872     FALSE      FALSE          0
    +# }
    +# create Marxan problem using data.frames that have been loaded into R
    +## load in planning unit data
    +pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr")
    +pu_dat <- data.table::fread(pu_path, data.table = FALSE)
    +head(pu_dat)
    +#>   id       cost status    xloc     yloc
    +#> 1  3        0.0      0 1116623 -4493479
    +#> 2 30   752727.5      3 1110623 -4496943
    +#> 3 56  3734907.5      0 1092623 -4500408
    +#> 4 58  1695902.1      0 1116623 -4500408
    +#> 5 84  3422025.6      0 1098623 -4503872
    +#> 6 85 17890758.4      0 1110623 -4503872
    +
    +## load in feature data
    +spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr")
    +spec_dat <- data.table::fread(spec_path, data.table = FALSE)
    +head(spec_dat)
    +#>   id prop spf   name
    +#> 1 10  0.3   1  bird1
    +#> 2 11  0.3   1  nvis2
    +#> 3 12  0.3   1  nvis8
    +#> 4 13  0.3   1  nvis9
    +#> 5 14  0.3   1 nvis14
    +#> 6 15  0.3   1 nvis20
    +
    +## load in planning unit vs feature data
    +puvspr_path <- system.file("extdata/input/puvspr.dat",
    +                           package = "prioritizr")
    +puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE)
    +head(puvspr_dat)
    +#>   species  pu     amount
    +#> 1      26  56 1203448.84
    +#> 2      26  58  451670.10
    +#> 3      26  84  680473.75
    +#> 4      26  85   97356.24
    +#> 5      26  86   78034.76
    +#> 6      26 111 4783274.17
    +
    +## load in the boundary data
    +bound_path <- system.file("extdata/input/bound.dat", package = "prioritizr")
    +bound_dat <- data.table::fread(bound_path, data.table = FALSE)
    +head(bound_dat)
    +#>   id1 id2 boundary
    +#> 1   3   3    16000
    +#> 2   3  30     4000
    +#> 3   3  58     4000
    +#> 4  30  30    12000
    +#> 5  30  58     4000
    +#> 6  30  85     4000
    +
    +# create problem without the boundary data
    +p2 <- marxan_problem(pu_dat, spec_dat, puvspr_dat) %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s2 <- solve(p2)
    +
    +# print solution
    +head(s2)
    +#>   id       cost status    xloc     yloc locked_in locked_out solution_1
    +#> 1  3        0.0      0 1116623 -4493479     FALSE      FALSE          0
    +#> 2 30   752727.5      3 1110623 -4496943     FALSE       TRUE          0
    +#> 3 56  3734907.5      0 1092623 -4500408     FALSE      FALSE          0
    +#> 4 58  1695902.1      0 1116623 -4500408     FALSE      FALSE          0
    +#> 5 84  3422025.6      0 1098623 -4503872     FALSE      FALSE          0
    +#> 6 85 17890758.4      0 1110623 -4503872     FALSE      FALSE          0
    +# }
    +# create problem with the boundary data and a boundary length modifier
    +# set to 5
    +p3 <- marxan_problem(pu_dat, spec_dat, puvspr_dat, bound_dat, 5) %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s3 <- solve(p3)
    +
    +# print solution
    +head(s3)
    +#>   id       cost status    xloc     yloc locked_in locked_out solution_1
    +#> 1  3        0.0      0 1116623 -4493479     FALSE      FALSE          0
    +#> 2 30   752727.5      3 1110623 -4496943     FALSE       TRUE          0
    +#> 3 56  3734907.5      0 1092623 -4500408     FALSE      FALSE          0
    +#> 4 58  1695902.1      0 1116623 -4500408     FALSE      FALSE          0
    +#> 5 84  3422025.6      0 1098623 -4503872     FALSE      FALSE          0
    +#> 6 85 17890758.4      0 1110623 -4503872     FALSE      FALSE          0
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/matrix_parameters.html b/docs/reference/matrix_parameters.html index 296325d93..eee89720b 100644 --- a/docs/reference/matrix_parameters.html +++ b/docs/reference/matrix_parameters.html @@ -1,101 +1,18 @@ - - - - - - - -Matrix parameters — matrix_parameters • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Matrix parameters — matrix_parameters • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,129 +101,115 @@

    Matrix parameters

    Create a parameter that represents a matrix object.

    -
    numeric_matrix_parameter(
    -  name,
    -  value,
    -  lower_limit = .Machine$double.xmin,
    -  upper_limit = .Machine$double.xmax,
    -  symmetric = FALSE
    -)
    -
    -binary_matrix_parameter(name, value, symmetric = FALSE)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    name

    character name of parameter.

    value

    matrix object.

    lower_limit

    numeric values denoting the minimum acceptable +

    Usage,
    numeric_matrix_parameter(
    +  name,
    +  value,
    +  lower_limit = .Machine$double.xmin,
    +  upper_limit = .Machine$double.xmax,
    +  symmetric = FALSE
    +)
    +
    +binary_matrix_parameter(name, value, symmetric = FALSE)
    + +
    +

    Arguments

    +
    name
    +

    character name of parameter.

    +
    value
    +

    matrix object.

    +
    lower_limit
    +

    numeric values denoting the minimum acceptable value in the matrix. Defaults to the smallest possible number on the -system.

    upper_limit

    numeric values denoting the maximum acceptable +system.

    +
    upper_limit
    +

    numeric values denoting the maximum acceptable value in the matrix. Defaults to the smallest possible number on the -system.

    symmetric

    logical must the must be matrix be symmetric? -Defaults to FALSE.

    - -

    Value

    - -

    MiscParameter object.

    +system.

    +
    symmetric
    +

    logical must the must be matrix be symmetric? +Defaults to FALSE.

    +
    +
    +

    Value

    +

    MiscParameter object.

    +
    -

    Examples

    -
    # create matrix
    -m <- matrix(runif(9), ncol = 3)
    -colnames(m) <- letters[1:3]
    -rownames(m) <- letters[1:3]
    -
    -# create a numeric matrix parameter
    -p1 <- numeric_matrix_parameter("m", m)
    -print(p1) # print it
    -#> <environment: 0x56421961e978>
    -#> attr(,"class")
    -#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    -#> [5] "proto"         "environment"  
    -p1$get() # get value
    -#>           a         b         c
    -#> a 0.8334376 0.5892456 0.2387031
    -#> b 0.4846932 0.5758689 0.2372128
    -#> c 0.8543447 0.4674843 0.2312509
    -p1$id # get id
    -#> id: 2248e95f-421a-435d-8db1-c600ca82cc25
    -p1$validate(m[, -1]) # check if parameter can be updated
    -#> [1] FALSE
    -#> attr(,"msg")
    -#> [1] "ncol(m) not equal to ncol(value)"
    -p1$set(m + 1) # set parameter to new values
    -p1$print() # print it again
    -#> [1] "m"
    -
    -# create a binary matrix parameter
    -m <- matrix(round(runif(9)), ncol = 3)
    -colnames(m) <- letters[1:3]
    -rownames(m) <- letters[1:3]
    -
    -# create a binary matrix parameter
    -p2 <- binary_matrix_parameter("m", m)
    -print(p2) # print it
    -#> <environment: 0x5642173f7e50>
    -#> attr(,"class")
    -#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    -#> [5] "proto"         "environment"  
    -p2$get() # get value
    -#>   a b c
    -#> a 1 0 1
    -#> b 0 1 1
    -#> c 0 0 1
    -p2$id # get id
    -#> id: dba810e0-8f3a-4645-bf60-aba12ac1db20
    -p2$validate(m[, -1]) # check if parameter can be updated
    -#> [1] FALSE
    -#> attr(,"msg")
    -#> [1] "ncol(m) not equal to ncol(value)"
    -p2$set(m + 1) # set parameter to new values
    -p2$print() # print it again
    -#> [1] "m"
    +    
    +

    Examples

    +
    # create matrix
    +m <- matrix(runif(9), ncol = 3)
    +colnames(m) <- letters[1:3]
    +rownames(m) <- letters[1:3]
    +
    +# create a numeric matrix parameter
    +p1 <- numeric_matrix_parameter("m", m)
    +print(p1) # print it
    +#> <environment: 0x55f268cc53e8>
    +#> attr(,"class")
    +#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    +#> [5] "proto"         "environment"  
    +p1$get() # get value
    +#>           a         b         c
    +#> a 0.8334376 0.5892456 0.2387031
    +#> b 0.4846932 0.5758689 0.2372128
    +#> c 0.8543447 0.4674843 0.2312509
    +p1$id # get id
    +#> id: 13f9d51f-08c9-4f23-b46a-c63798e6e9d8
    +p1$validate(m[, -1]) # check if parameter can be updated
    +#> [1] FALSE
    +#> attr(,"msg")
    +#> [1] "ncol(m) not equal to ncol(value)"
    +p1$set(m + 1) # set parameter to new values
    +p1$print() # print it again
    +#> [1] "m"
    +
    +# create a binary matrix parameter
    +m <- matrix(round(runif(9)), ncol = 3)
    +colnames(m) <- letters[1:3]
    +rownames(m) <- letters[1:3]
    +
    +# create a binary matrix parameter
    +p2 <- binary_matrix_parameter("m", m)
    +print(p2) # print it
    +#> <environment: 0x55f2684deee0>
    +#> attr(,"class")
    +#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    +#> [5] "proto"         "environment"  
    +p2$get() # get value
    +#>   a b c
    +#> a 1 0 1
    +#> b 0 1 1
    +#> c 0 0 1
    +p2$id # get id
    +#> id: a6f935ec-bc06-4421-9875-522a7f1ad24d
    +p2$validate(m[, -1]) # check if parameter can be updated
    +#> [1] FALSE
    +#> attr(,"msg")
    +#> [1] "ncol(m) not equal to ncol(value)"
    +p2$set(m + 1) # set parameter to new values
    +p2$print() # print it again
    +#> [1] "m"
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/misc_parameter.html b/docs/reference/misc_parameter.html index 457770fc9..b1f55c333 100644 --- a/docs/reference/misc_parameter.html +++ b/docs/reference/misc_parameter.html @@ -1,101 +1,18 @@ - - - - - - - -Miscellaneous parameter — misc_parameter • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Miscellaneous parameter — misc_parameter • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,403 +101,393 @@

    Miscellaneous parameter

    Create a parameter that consists of a miscellaneous object.

    -
    misc_parameter(name, value, validator)
    +
    Usage,
    misc_parameter(name, value, validator)
    -

    Arguments

    - - - - - - - - - - - - - - -
    name

    character name of parameter.

    value

    object.

    validator

    function to validate changes to the parameter. This +

    +

    Arguments

    +
    name
    +

    character name of parameter.

    +
    value
    +

    object.

    +
    validator
    +

    function to validate changes to the parameter. This function must have a single argument and return either TRUE or FALSE depending on if the argument is valid candidate for the -parameter.

    - -

    Value

    - -

    MiscParameter object.

    +parameter.

    +
    +
    +

    Value

    +

    MiscParameter object.

    +
    -

    Examples

    -
    # load data
    -data(iris, mtcars)
    -
    -# create table parameter can that can be updated to any other object
    -p1 <- misc_parameter("tbl", iris, function(x) TRUE)
    -print(p1) # print it
    -#> <environment: 0x564226189508>
    -#> attr(,"class")
    -#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    -#> [5] "proto"         "environment"  
    -p1$get() # get value
    -#>     Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
    -#> 1            5.1         3.5          1.4         0.2     setosa
    -#> 2            4.9         3.0          1.4         0.2     setosa
    -#> 3            4.7         3.2          1.3         0.2     setosa
    -#> 4            4.6         3.1          1.5         0.2     setosa
    -#> 5            5.0         3.6          1.4         0.2     setosa
    -#> 6            5.4         3.9          1.7         0.4     setosa
    -#> 7            4.6         3.4          1.4         0.3     setosa
    -#> 8            5.0         3.4          1.5         0.2     setosa
    -#> 9            4.4         2.9          1.4         0.2     setosa
    -#> 10           4.9         3.1          1.5         0.1     setosa
    -#> 11           5.4         3.7          1.5         0.2     setosa
    -#> 12           4.8         3.4          1.6         0.2     setosa
    -#> 13           4.8         3.0          1.4         0.1     setosa
    -#> 14           4.3         3.0          1.1         0.1     setosa
    -#> 15           5.8         4.0          1.2         0.2     setosa
    -#> 16           5.7         4.4          1.5         0.4     setosa
    -#> 17           5.4         3.9          1.3         0.4     setosa
    -#> 18           5.1         3.5          1.4         0.3     setosa
    -#> 19           5.7         3.8          1.7         0.3     setosa
    -#> 20           5.1         3.8          1.5         0.3     setosa
    -#> 21           5.4         3.4          1.7         0.2     setosa
    -#> 22           5.1         3.7          1.5         0.4     setosa
    -#> 23           4.6         3.6          1.0         0.2     setosa
    -#> 24           5.1         3.3          1.7         0.5     setosa
    -#> 25           4.8         3.4          1.9         0.2     setosa
    -#> 26           5.0         3.0          1.6         0.2     setosa
    -#> 27           5.0         3.4          1.6         0.4     setosa
    -#> 28           5.2         3.5          1.5         0.2     setosa
    -#> 29           5.2         3.4          1.4         0.2     setosa
    -#> 30           4.7         3.2          1.6         0.2     setosa
    -#> 31           4.8         3.1          1.6         0.2     setosa
    -#> 32           5.4         3.4          1.5         0.4     setosa
    -#> 33           5.2         4.1          1.5         0.1     setosa
    -#> 34           5.5         4.2          1.4         0.2     setosa
    -#> 35           4.9         3.1          1.5         0.2     setosa
    -#> 36           5.0         3.2          1.2         0.2     setosa
    -#> 37           5.5         3.5          1.3         0.2     setosa
    -#> 38           4.9         3.6          1.4         0.1     setosa
    -#> 39           4.4         3.0          1.3         0.2     setosa
    -#> 40           5.1         3.4          1.5         0.2     setosa
    -#> 41           5.0         3.5          1.3         0.3     setosa
    -#> 42           4.5         2.3          1.3         0.3     setosa
    -#> 43           4.4         3.2          1.3         0.2     setosa
    -#> 44           5.0         3.5          1.6         0.6     setosa
    -#> 45           5.1         3.8          1.9         0.4     setosa
    -#> 46           4.8         3.0          1.4         0.3     setosa
    -#> 47           5.1         3.8          1.6         0.2     setosa
    -#> 48           4.6         3.2          1.4         0.2     setosa
    -#> 49           5.3         3.7          1.5         0.2     setosa
    -#> 50           5.0         3.3          1.4         0.2     setosa
    -#> 51           7.0         3.2          4.7         1.4 versicolor
    -#> 52           6.4         3.2          4.5         1.5 versicolor
    -#> 53           6.9         3.1          4.9         1.5 versicolor
    -#> 54           5.5         2.3          4.0         1.3 versicolor
    -#> 55           6.5         2.8          4.6         1.5 versicolor
    -#> 56           5.7         2.8          4.5         1.3 versicolor
    -#> 57           6.3         3.3          4.7         1.6 versicolor
    -#> 58           4.9         2.4          3.3         1.0 versicolor
    -#> 59           6.6         2.9          4.6         1.3 versicolor
    -#> 60           5.2         2.7          3.9         1.4 versicolor
    -#> 61           5.0         2.0          3.5         1.0 versicolor
    -#> 62           5.9         3.0          4.2         1.5 versicolor
    -#> 63           6.0         2.2          4.0         1.0 versicolor
    -#> 64           6.1         2.9          4.7         1.4 versicolor
    -#> 65           5.6         2.9          3.6         1.3 versicolor
    -#> 66           6.7         3.1          4.4         1.4 versicolor
    -#> 67           5.6         3.0          4.5         1.5 versicolor
    -#> 68           5.8         2.7          4.1         1.0 versicolor
    -#> 69           6.2         2.2          4.5         1.5 versicolor
    -#> 70           5.6         2.5          3.9         1.1 versicolor
    -#> 71           5.9         3.2          4.8         1.8 versicolor
    -#> 72           6.1         2.8          4.0         1.3 versicolor
    -#> 73           6.3         2.5          4.9         1.5 versicolor
    -#> 74           6.1         2.8          4.7         1.2 versicolor
    -#> 75           6.4         2.9          4.3         1.3 versicolor
    -#> 76           6.6         3.0          4.4         1.4 versicolor
    -#> 77           6.8         2.8          4.8         1.4 versicolor
    -#> 78           6.7         3.0          5.0         1.7 versicolor
    -#> 79           6.0         2.9          4.5         1.5 versicolor
    -#> 80           5.7         2.6          3.5         1.0 versicolor
    -#> 81           5.5         2.4          3.8         1.1 versicolor
    -#> 82           5.5         2.4          3.7         1.0 versicolor
    -#> 83           5.8         2.7          3.9         1.2 versicolor
    -#> 84           6.0         2.7          5.1         1.6 versicolor
    -#> 85           5.4         3.0          4.5         1.5 versicolor
    -#> 86           6.0         3.4          4.5         1.6 versicolor
    -#> 87           6.7         3.1          4.7         1.5 versicolor
    -#> 88           6.3         2.3          4.4         1.3 versicolor
    -#> 89           5.6         3.0          4.1         1.3 versicolor
    -#> 90           5.5         2.5          4.0         1.3 versicolor
    -#> 91           5.5         2.6          4.4         1.2 versicolor
    -#> 92           6.1         3.0          4.6         1.4 versicolor
    -#> 93           5.8         2.6          4.0         1.2 versicolor
    -#> 94           5.0         2.3          3.3         1.0 versicolor
    -#> 95           5.6         2.7          4.2         1.3 versicolor
    -#> 96           5.7         3.0          4.2         1.2 versicolor
    -#> 97           5.7         2.9          4.2         1.3 versicolor
    -#> 98           6.2         2.9          4.3         1.3 versicolor
    -#> 99           5.1         2.5          3.0         1.1 versicolor
    -#> 100          5.7         2.8          4.1         1.3 versicolor
    -#> 101          6.3         3.3          6.0         2.5  virginica
    -#> 102          5.8         2.7          5.1         1.9  virginica
    -#> 103          7.1         3.0          5.9         2.1  virginica
    -#> 104          6.3         2.9          5.6         1.8  virginica
    -#> 105          6.5         3.0          5.8         2.2  virginica
    -#> 106          7.6         3.0          6.6         2.1  virginica
    -#> 107          4.9         2.5          4.5         1.7  virginica
    -#> 108          7.3         2.9          6.3         1.8  virginica
    -#> 109          6.7         2.5          5.8         1.8  virginica
    -#> 110          7.2         3.6          6.1         2.5  virginica
    -#> 111          6.5         3.2          5.1         2.0  virginica
    -#> 112          6.4         2.7          5.3         1.9  virginica
    -#> 113          6.8         3.0          5.5         2.1  virginica
    -#> 114          5.7         2.5          5.0         2.0  virginica
    -#> 115          5.8         2.8          5.1         2.4  virginica
    -#> 116          6.4         3.2          5.3         2.3  virginica
    -#> 117          6.5         3.0          5.5         1.8  virginica
    -#> 118          7.7         3.8          6.7         2.2  virginica
    -#> 119          7.7         2.6          6.9         2.3  virginica
    -#> 120          6.0         2.2          5.0         1.5  virginica
    -#> 121          6.9         3.2          5.7         2.3  virginica
    -#> 122          5.6         2.8          4.9         2.0  virginica
    -#> 123          7.7         2.8          6.7         2.0  virginica
    -#> 124          6.3         2.7          4.9         1.8  virginica
    -#> 125          6.7         3.3          5.7         2.1  virginica
    -#> 126          7.2         3.2          6.0         1.8  virginica
    -#> 127          6.2         2.8          4.8         1.8  virginica
    -#> 128          6.1         3.0          4.9         1.8  virginica
    -#> 129          6.4         2.8          5.6         2.1  virginica
    -#> 130          7.2         3.0          5.8         1.6  virginica
    -#> 131          7.4         2.8          6.1         1.9  virginica
    -#> 132          7.9         3.8          6.4         2.0  virginica
    -#> 133          6.4         2.8          5.6         2.2  virginica
    -#> 134          6.3         2.8          5.1         1.5  virginica
    -#> 135          6.1         2.6          5.6         1.4  virginica
    -#> 136          7.7         3.0          6.1         2.3  virginica
    -#> 137          6.3         3.4          5.6         2.4  virginica
    -#> 138          6.4         3.1          5.5         1.8  virginica
    -#> 139          6.0         3.0          4.8         1.8  virginica
    -#> 140          6.9         3.1          5.4         2.1  virginica
    -#> 141          6.7         3.1          5.6         2.4  virginica
    -#> 142          6.9         3.1          5.1         2.3  virginica
    -#> 143          5.8         2.7          5.1         1.9  virginica
    -#> 144          6.8         3.2          5.9         2.3  virginica
    -#> 145          6.7         3.3          5.7         2.5  virginica
    -#> 146          6.7         3.0          5.2         2.3  virginica
    -#> 147          6.3         2.5          5.0         1.9  virginica
    -#> 148          6.5         3.0          5.2         2.0  virginica
    -#> 149          6.2         3.4          5.4         2.3  virginica
    -#> 150          5.9         3.0          5.1         1.8  virginica
    -p1$id # get id
    -#> id: 63b54362-2dd2-4512-bb80-773bdce363f6
    -p1$validate(mtcars) # check if parameter can be updated
    -#> [1] TRUE
    -p1$set(mtcars) # set parameter to mtcars
    -p1$print() # print it again
    -#> [1] "tbl"
    -
    -# create table parameter with validation function that requires
    -# all values in the first column to be less then 200 and that the
    -# parameter have the same column names as the iris dataset
    -p2 <- misc_parameter("tbl2", iris,
    -                     function(x) all(names(x) %in% names(iris)) &&
    -                                 all(x[[1]] < 200))
    -print(p2) # print it
    -#> <environment: 0x56421aa597b0>
    -#> attr(,"class")
    -#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    -#> [5] "proto"         "environment"  
    -p2$get() # get value
    -#>     Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
    -#> 1            5.1         3.5          1.4         0.2     setosa
    -#> 2            4.9         3.0          1.4         0.2     setosa
    -#> 3            4.7         3.2          1.3         0.2     setosa
    -#> 4            4.6         3.1          1.5         0.2     setosa
    -#> 5            5.0         3.6          1.4         0.2     setosa
    -#> 6            5.4         3.9          1.7         0.4     setosa
    -#> 7            4.6         3.4          1.4         0.3     setosa
    -#> 8            5.0         3.4          1.5         0.2     setosa
    -#> 9            4.4         2.9          1.4         0.2     setosa
    -#> 10           4.9         3.1          1.5         0.1     setosa
    -#> 11           5.4         3.7          1.5         0.2     setosa
    -#> 12           4.8         3.4          1.6         0.2     setosa
    -#> 13           4.8         3.0          1.4         0.1     setosa
    -#> 14           4.3         3.0          1.1         0.1     setosa
    -#> 15           5.8         4.0          1.2         0.2     setosa
    -#> 16           5.7         4.4          1.5         0.4     setosa
    -#> 17           5.4         3.9          1.3         0.4     setosa
    -#> 18           5.1         3.5          1.4         0.3     setosa
    -#> 19           5.7         3.8          1.7         0.3     setosa
    -#> 20           5.1         3.8          1.5         0.3     setosa
    -#> 21           5.4         3.4          1.7         0.2     setosa
    -#> 22           5.1         3.7          1.5         0.4     setosa
    -#> 23           4.6         3.6          1.0         0.2     setosa
    -#> 24           5.1         3.3          1.7         0.5     setosa
    -#> 25           4.8         3.4          1.9         0.2     setosa
    -#> 26           5.0         3.0          1.6         0.2     setosa
    -#> 27           5.0         3.4          1.6         0.4     setosa
    -#> 28           5.2         3.5          1.5         0.2     setosa
    -#> 29           5.2         3.4          1.4         0.2     setosa
    -#> 30           4.7         3.2          1.6         0.2     setosa
    -#> 31           4.8         3.1          1.6         0.2     setosa
    -#> 32           5.4         3.4          1.5         0.4     setosa
    -#> 33           5.2         4.1          1.5         0.1     setosa
    -#> 34           5.5         4.2          1.4         0.2     setosa
    -#> 35           4.9         3.1          1.5         0.2     setosa
    -#> 36           5.0         3.2          1.2         0.2     setosa
    -#> 37           5.5         3.5          1.3         0.2     setosa
    -#> 38           4.9         3.6          1.4         0.1     setosa
    -#> 39           4.4         3.0          1.3         0.2     setosa
    -#> 40           5.1         3.4          1.5         0.2     setosa
    -#> 41           5.0         3.5          1.3         0.3     setosa
    -#> 42           4.5         2.3          1.3         0.3     setosa
    -#> 43           4.4         3.2          1.3         0.2     setosa
    -#> 44           5.0         3.5          1.6         0.6     setosa
    -#> 45           5.1         3.8          1.9         0.4     setosa
    -#> 46           4.8         3.0          1.4         0.3     setosa
    -#> 47           5.1         3.8          1.6         0.2     setosa
    -#> 48           4.6         3.2          1.4         0.2     setosa
    -#> 49           5.3         3.7          1.5         0.2     setosa
    -#> 50           5.0         3.3          1.4         0.2     setosa
    -#> 51           7.0         3.2          4.7         1.4 versicolor
    -#> 52           6.4         3.2          4.5         1.5 versicolor
    -#> 53           6.9         3.1          4.9         1.5 versicolor
    -#> 54           5.5         2.3          4.0         1.3 versicolor
    -#> 55           6.5         2.8          4.6         1.5 versicolor
    -#> 56           5.7         2.8          4.5         1.3 versicolor
    -#> 57           6.3         3.3          4.7         1.6 versicolor
    -#> 58           4.9         2.4          3.3         1.0 versicolor
    -#> 59           6.6         2.9          4.6         1.3 versicolor
    -#> 60           5.2         2.7          3.9         1.4 versicolor
    -#> 61           5.0         2.0          3.5         1.0 versicolor
    -#> 62           5.9         3.0          4.2         1.5 versicolor
    -#> 63           6.0         2.2          4.0         1.0 versicolor
    -#> 64           6.1         2.9          4.7         1.4 versicolor
    -#> 65           5.6         2.9          3.6         1.3 versicolor
    -#> 66           6.7         3.1          4.4         1.4 versicolor
    -#> 67           5.6         3.0          4.5         1.5 versicolor
    -#> 68           5.8         2.7          4.1         1.0 versicolor
    -#> 69           6.2         2.2          4.5         1.5 versicolor
    -#> 70           5.6         2.5          3.9         1.1 versicolor
    -#> 71           5.9         3.2          4.8         1.8 versicolor
    -#> 72           6.1         2.8          4.0         1.3 versicolor
    -#> 73           6.3         2.5          4.9         1.5 versicolor
    -#> 74           6.1         2.8          4.7         1.2 versicolor
    -#> 75           6.4         2.9          4.3         1.3 versicolor
    -#> 76           6.6         3.0          4.4         1.4 versicolor
    -#> 77           6.8         2.8          4.8         1.4 versicolor
    -#> 78           6.7         3.0          5.0         1.7 versicolor
    -#> 79           6.0         2.9          4.5         1.5 versicolor
    -#> 80           5.7         2.6          3.5         1.0 versicolor
    -#> 81           5.5         2.4          3.8         1.1 versicolor
    -#> 82           5.5         2.4          3.7         1.0 versicolor
    -#> 83           5.8         2.7          3.9         1.2 versicolor
    -#> 84           6.0         2.7          5.1         1.6 versicolor
    -#> 85           5.4         3.0          4.5         1.5 versicolor
    -#> 86           6.0         3.4          4.5         1.6 versicolor
    -#> 87           6.7         3.1          4.7         1.5 versicolor
    -#> 88           6.3         2.3          4.4         1.3 versicolor
    -#> 89           5.6         3.0          4.1         1.3 versicolor
    -#> 90           5.5         2.5          4.0         1.3 versicolor
    -#> 91           5.5         2.6          4.4         1.2 versicolor
    -#> 92           6.1         3.0          4.6         1.4 versicolor
    -#> 93           5.8         2.6          4.0         1.2 versicolor
    -#> 94           5.0         2.3          3.3         1.0 versicolor
    -#> 95           5.6         2.7          4.2         1.3 versicolor
    -#> 96           5.7         3.0          4.2         1.2 versicolor
    -#> 97           5.7         2.9          4.2         1.3 versicolor
    -#> 98           6.2         2.9          4.3         1.3 versicolor
    -#> 99           5.1         2.5          3.0         1.1 versicolor
    -#> 100          5.7         2.8          4.1         1.3 versicolor
    -#> 101          6.3         3.3          6.0         2.5  virginica
    -#> 102          5.8         2.7          5.1         1.9  virginica
    -#> 103          7.1         3.0          5.9         2.1  virginica
    -#> 104          6.3         2.9          5.6         1.8  virginica
    -#> 105          6.5         3.0          5.8         2.2  virginica
    -#> 106          7.6         3.0          6.6         2.1  virginica
    -#> 107          4.9         2.5          4.5         1.7  virginica
    -#> 108          7.3         2.9          6.3         1.8  virginica
    -#> 109          6.7         2.5          5.8         1.8  virginica
    -#> 110          7.2         3.6          6.1         2.5  virginica
    -#> 111          6.5         3.2          5.1         2.0  virginica
    -#> 112          6.4         2.7          5.3         1.9  virginica
    -#> 113          6.8         3.0          5.5         2.1  virginica
    -#> 114          5.7         2.5          5.0         2.0  virginica
    -#> 115          5.8         2.8          5.1         2.4  virginica
    -#> 116          6.4         3.2          5.3         2.3  virginica
    -#> 117          6.5         3.0          5.5         1.8  virginica
    -#> 118          7.7         3.8          6.7         2.2  virginica
    -#> 119          7.7         2.6          6.9         2.3  virginica
    -#> 120          6.0         2.2          5.0         1.5  virginica
    -#> 121          6.9         3.2          5.7         2.3  virginica
    -#> 122          5.6         2.8          4.9         2.0  virginica
    -#> 123          7.7         2.8          6.7         2.0  virginica
    -#> 124          6.3         2.7          4.9         1.8  virginica
    -#> 125          6.7         3.3          5.7         2.1  virginica
    -#> 126          7.2         3.2          6.0         1.8  virginica
    -#> 127          6.2         2.8          4.8         1.8  virginica
    -#> 128          6.1         3.0          4.9         1.8  virginica
    -#> 129          6.4         2.8          5.6         2.1  virginica
    -#> 130          7.2         3.0          5.8         1.6  virginica
    -#> 131          7.4         2.8          6.1         1.9  virginica
    -#> 132          7.9         3.8          6.4         2.0  virginica
    -#> 133          6.4         2.8          5.6         2.2  virginica
    -#> 134          6.3         2.8          5.1         1.5  virginica
    -#> 135          6.1         2.6          5.6         1.4  virginica
    -#> 136          7.7         3.0          6.1         2.3  virginica
    -#> 137          6.3         3.4          5.6         2.4  virginica
    -#> 138          6.4         3.1          5.5         1.8  virginica
    -#> 139          6.0         3.0          4.8         1.8  virginica
    -#> 140          6.9         3.1          5.4         2.1  virginica
    -#> 141          6.7         3.1          5.6         2.4  virginica
    -#> 142          6.9         3.1          5.1         2.3  virginica
    -#> 143          5.8         2.7          5.1         1.9  virginica
    -#> 144          6.8         3.2          5.9         2.3  virginica
    -#> 145          6.7         3.3          5.7         2.5  virginica
    -#> 146          6.7         3.0          5.2         2.3  virginica
    -#> 147          6.3         2.5          5.0         1.9  virginica
    -#> 148          6.5         3.0          5.2         2.0  virginica
    -#> 149          6.2         3.4          5.4         2.3  virginica
    -#> 150          5.9         3.0          5.1         1.8  virginica
    -p2$id # get id
    -#> id: e50355ed-fb0a-46e8-abbe-51e5b7af1617
    -p2$validate(mtcars) # check if parameter can be updated
    -#> [1] FALSE
    -iris2 <- iris; iris2[1,1] <- 300 # create updated iris dataset
    -p2$validate(iris2) # check if parameter can be updated
    -#> [1] FALSE
    -iris3 <- iris; iris2[1,1] <- 100 # create updated iris dataset
    -p2$set(iris3) # set parameter to iris3
    -p2$print() # print it again
    -#> [1] "tbl2"
    -
    +    
    +

    Examples

    +
    # load data
    +data(iris, mtcars)
    +
    +# create table parameter can that can be updated to any other object
    +p1 <- misc_parameter("tbl", iris, function(x) TRUE)
    +print(p1) # print it
    +#> <environment: 0x55f2795ce6b0>
    +#> attr(,"class")
    +#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    +#> [5] "proto"         "environment"  
    +p1$get() # get value
    +#>     Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
    +#> 1            5.1         3.5          1.4         0.2     setosa
    +#> 2            4.9         3.0          1.4         0.2     setosa
    +#> 3            4.7         3.2          1.3         0.2     setosa
    +#> 4            4.6         3.1          1.5         0.2     setosa
    +#> 5            5.0         3.6          1.4         0.2     setosa
    +#> 6            5.4         3.9          1.7         0.4     setosa
    +#> 7            4.6         3.4          1.4         0.3     setosa
    +#> 8            5.0         3.4          1.5         0.2     setosa
    +#> 9            4.4         2.9          1.4         0.2     setosa
    +#> 10           4.9         3.1          1.5         0.1     setosa
    +#> 11           5.4         3.7          1.5         0.2     setosa
    +#> 12           4.8         3.4          1.6         0.2     setosa
    +#> 13           4.8         3.0          1.4         0.1     setosa
    +#> 14           4.3         3.0          1.1         0.1     setosa
    +#> 15           5.8         4.0          1.2         0.2     setosa
    +#> 16           5.7         4.4          1.5         0.4     setosa
    +#> 17           5.4         3.9          1.3         0.4     setosa
    +#> 18           5.1         3.5          1.4         0.3     setosa
    +#> 19           5.7         3.8          1.7         0.3     setosa
    +#> 20           5.1         3.8          1.5         0.3     setosa
    +#> 21           5.4         3.4          1.7         0.2     setosa
    +#> 22           5.1         3.7          1.5         0.4     setosa
    +#> 23           4.6         3.6          1.0         0.2     setosa
    +#> 24           5.1         3.3          1.7         0.5     setosa
    +#> 25           4.8         3.4          1.9         0.2     setosa
    +#> 26           5.0         3.0          1.6         0.2     setosa
    +#> 27           5.0         3.4          1.6         0.4     setosa
    +#> 28           5.2         3.5          1.5         0.2     setosa
    +#> 29           5.2         3.4          1.4         0.2     setosa
    +#> 30           4.7         3.2          1.6         0.2     setosa
    +#> 31           4.8         3.1          1.6         0.2     setosa
    +#> 32           5.4         3.4          1.5         0.4     setosa
    +#> 33           5.2         4.1          1.5         0.1     setosa
    +#> 34           5.5         4.2          1.4         0.2     setosa
    +#> 35           4.9         3.1          1.5         0.2     setosa
    +#> 36           5.0         3.2          1.2         0.2     setosa
    +#> 37           5.5         3.5          1.3         0.2     setosa
    +#> 38           4.9         3.6          1.4         0.1     setosa
    +#> 39           4.4         3.0          1.3         0.2     setosa
    +#> 40           5.1         3.4          1.5         0.2     setosa
    +#> 41           5.0         3.5          1.3         0.3     setosa
    +#> 42           4.5         2.3          1.3         0.3     setosa
    +#> 43           4.4         3.2          1.3         0.2     setosa
    +#> 44           5.0         3.5          1.6         0.6     setosa
    +#> 45           5.1         3.8          1.9         0.4     setosa
    +#> 46           4.8         3.0          1.4         0.3     setosa
    +#> 47           5.1         3.8          1.6         0.2     setosa
    +#> 48           4.6         3.2          1.4         0.2     setosa
    +#> 49           5.3         3.7          1.5         0.2     setosa
    +#> 50           5.0         3.3          1.4         0.2     setosa
    +#> 51           7.0         3.2          4.7         1.4 versicolor
    +#> 52           6.4         3.2          4.5         1.5 versicolor
    +#> 53           6.9         3.1          4.9         1.5 versicolor
    +#> 54           5.5         2.3          4.0         1.3 versicolor
    +#> 55           6.5         2.8          4.6         1.5 versicolor
    +#> 56           5.7         2.8          4.5         1.3 versicolor
    +#> 57           6.3         3.3          4.7         1.6 versicolor
    +#> 58           4.9         2.4          3.3         1.0 versicolor
    +#> 59           6.6         2.9          4.6         1.3 versicolor
    +#> 60           5.2         2.7          3.9         1.4 versicolor
    +#> 61           5.0         2.0          3.5         1.0 versicolor
    +#> 62           5.9         3.0          4.2         1.5 versicolor
    +#> 63           6.0         2.2          4.0         1.0 versicolor
    +#> 64           6.1         2.9          4.7         1.4 versicolor
    +#> 65           5.6         2.9          3.6         1.3 versicolor
    +#> 66           6.7         3.1          4.4         1.4 versicolor
    +#> 67           5.6         3.0          4.5         1.5 versicolor
    +#> 68           5.8         2.7          4.1         1.0 versicolor
    +#> 69           6.2         2.2          4.5         1.5 versicolor
    +#> 70           5.6         2.5          3.9         1.1 versicolor
    +#> 71           5.9         3.2          4.8         1.8 versicolor
    +#> 72           6.1         2.8          4.0         1.3 versicolor
    +#> 73           6.3         2.5          4.9         1.5 versicolor
    +#> 74           6.1         2.8          4.7         1.2 versicolor
    +#> 75           6.4         2.9          4.3         1.3 versicolor
    +#> 76           6.6         3.0          4.4         1.4 versicolor
    +#> 77           6.8         2.8          4.8         1.4 versicolor
    +#> 78           6.7         3.0          5.0         1.7 versicolor
    +#> 79           6.0         2.9          4.5         1.5 versicolor
    +#> 80           5.7         2.6          3.5         1.0 versicolor
    +#> 81           5.5         2.4          3.8         1.1 versicolor
    +#> 82           5.5         2.4          3.7         1.0 versicolor
    +#> 83           5.8         2.7          3.9         1.2 versicolor
    +#> 84           6.0         2.7          5.1         1.6 versicolor
    +#> 85           5.4         3.0          4.5         1.5 versicolor
    +#> 86           6.0         3.4          4.5         1.6 versicolor
    +#> 87           6.7         3.1          4.7         1.5 versicolor
    +#> 88           6.3         2.3          4.4         1.3 versicolor
    +#> 89           5.6         3.0          4.1         1.3 versicolor
    +#> 90           5.5         2.5          4.0         1.3 versicolor
    +#> 91           5.5         2.6          4.4         1.2 versicolor
    +#> 92           6.1         3.0          4.6         1.4 versicolor
    +#> 93           5.8         2.6          4.0         1.2 versicolor
    +#> 94           5.0         2.3          3.3         1.0 versicolor
    +#> 95           5.6         2.7          4.2         1.3 versicolor
    +#> 96           5.7         3.0          4.2         1.2 versicolor
    +#> 97           5.7         2.9          4.2         1.3 versicolor
    +#> 98           6.2         2.9          4.3         1.3 versicolor
    +#> 99           5.1         2.5          3.0         1.1 versicolor
    +#> 100          5.7         2.8          4.1         1.3 versicolor
    +#> 101          6.3         3.3          6.0         2.5  virginica
    +#> 102          5.8         2.7          5.1         1.9  virginica
    +#> 103          7.1         3.0          5.9         2.1  virginica
    +#> 104          6.3         2.9          5.6         1.8  virginica
    +#> 105          6.5         3.0          5.8         2.2  virginica
    +#> 106          7.6         3.0          6.6         2.1  virginica
    +#> 107          4.9         2.5          4.5         1.7  virginica
    +#> 108          7.3         2.9          6.3         1.8  virginica
    +#> 109          6.7         2.5          5.8         1.8  virginica
    +#> 110          7.2         3.6          6.1         2.5  virginica
    +#> 111          6.5         3.2          5.1         2.0  virginica
    +#> 112          6.4         2.7          5.3         1.9  virginica
    +#> 113          6.8         3.0          5.5         2.1  virginica
    +#> 114          5.7         2.5          5.0         2.0  virginica
    +#> 115          5.8         2.8          5.1         2.4  virginica
    +#> 116          6.4         3.2          5.3         2.3  virginica
    +#> 117          6.5         3.0          5.5         1.8  virginica
    +#> 118          7.7         3.8          6.7         2.2  virginica
    +#> 119          7.7         2.6          6.9         2.3  virginica
    +#> 120          6.0         2.2          5.0         1.5  virginica
    +#> 121          6.9         3.2          5.7         2.3  virginica
    +#> 122          5.6         2.8          4.9         2.0  virginica
    +#> 123          7.7         2.8          6.7         2.0  virginica
    +#> 124          6.3         2.7          4.9         1.8  virginica
    +#> 125          6.7         3.3          5.7         2.1  virginica
    +#> 126          7.2         3.2          6.0         1.8  virginica
    +#> 127          6.2         2.8          4.8         1.8  virginica
    +#> 128          6.1         3.0          4.9         1.8  virginica
    +#> 129          6.4         2.8          5.6         2.1  virginica
    +#> 130          7.2         3.0          5.8         1.6  virginica
    +#> 131          7.4         2.8          6.1         1.9  virginica
    +#> 132          7.9         3.8          6.4         2.0  virginica
    +#> 133          6.4         2.8          5.6         2.2  virginica
    +#> 134          6.3         2.8          5.1         1.5  virginica
    +#> 135          6.1         2.6          5.6         1.4  virginica
    +#> 136          7.7         3.0          6.1         2.3  virginica
    +#> 137          6.3         3.4          5.6         2.4  virginica
    +#> 138          6.4         3.1          5.5         1.8  virginica
    +#> 139          6.0         3.0          4.8         1.8  virginica
    +#> 140          6.9         3.1          5.4         2.1  virginica
    +#> 141          6.7         3.1          5.6         2.4  virginica
    +#> 142          6.9         3.1          5.1         2.3  virginica
    +#> 143          5.8         2.7          5.1         1.9  virginica
    +#> 144          6.8         3.2          5.9         2.3  virginica
    +#> 145          6.7         3.3          5.7         2.5  virginica
    +#> 146          6.7         3.0          5.2         2.3  virginica
    +#> 147          6.3         2.5          5.0         1.9  virginica
    +#> 148          6.5         3.0          5.2         2.0  virginica
    +#> 149          6.2         3.4          5.4         2.3  virginica
    +#> 150          5.9         3.0          5.1         1.8  virginica
    +p1$id # get id
    +#> id: 040b7e90-039b-4388-ac59-1eeee8f7c100
    +p1$validate(mtcars) # check if parameter can be updated
    +#> [1] TRUE
    +p1$set(mtcars) # set parameter to mtcars
    +p1$print() # print it again
    +#> [1] "tbl"
    +
    +# create table parameter with validation function that requires
    +# all values in the first column to be less then 200 and that the
    +# parameter have the same column names as the iris dataset
    +p2 <- misc_parameter("tbl2", iris,
    +                     function(x) all(names(x) %in% names(iris)) &&
    +                                 all(x[[1]] < 200))
    +print(p2) # print it
    +#> <environment: 0x55f2791d5880>
    +#> attr(,"class")
    +#> [1] "MiscParameter" "MiscParameter" "Parameter"     "pproto"       
    +#> [5] "proto"         "environment"  
    +p2$get() # get value
    +#>     Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
    +#> 1            5.1         3.5          1.4         0.2     setosa
    +#> 2            4.9         3.0          1.4         0.2     setosa
    +#> 3            4.7         3.2          1.3         0.2     setosa
    +#> 4            4.6         3.1          1.5         0.2     setosa
    +#> 5            5.0         3.6          1.4         0.2     setosa
    +#> 6            5.4         3.9          1.7         0.4     setosa
    +#> 7            4.6         3.4          1.4         0.3     setosa
    +#> 8            5.0         3.4          1.5         0.2     setosa
    +#> 9            4.4         2.9          1.4         0.2     setosa
    +#> 10           4.9         3.1          1.5         0.1     setosa
    +#> 11           5.4         3.7          1.5         0.2     setosa
    +#> 12           4.8         3.4          1.6         0.2     setosa
    +#> 13           4.8         3.0          1.4         0.1     setosa
    +#> 14           4.3         3.0          1.1         0.1     setosa
    +#> 15           5.8         4.0          1.2         0.2     setosa
    +#> 16           5.7         4.4          1.5         0.4     setosa
    +#> 17           5.4         3.9          1.3         0.4     setosa
    +#> 18           5.1         3.5          1.4         0.3     setosa
    +#> 19           5.7         3.8          1.7         0.3     setosa
    +#> 20           5.1         3.8          1.5         0.3     setosa
    +#> 21           5.4         3.4          1.7         0.2     setosa
    +#> 22           5.1         3.7          1.5         0.4     setosa
    +#> 23           4.6         3.6          1.0         0.2     setosa
    +#> 24           5.1         3.3          1.7         0.5     setosa
    +#> 25           4.8         3.4          1.9         0.2     setosa
    +#> 26           5.0         3.0          1.6         0.2     setosa
    +#> 27           5.0         3.4          1.6         0.4     setosa
    +#> 28           5.2         3.5          1.5         0.2     setosa
    +#> 29           5.2         3.4          1.4         0.2     setosa
    +#> 30           4.7         3.2          1.6         0.2     setosa
    +#> 31           4.8         3.1          1.6         0.2     setosa
    +#> 32           5.4         3.4          1.5         0.4     setosa
    +#> 33           5.2         4.1          1.5         0.1     setosa
    +#> 34           5.5         4.2          1.4         0.2     setosa
    +#> 35           4.9         3.1          1.5         0.2     setosa
    +#> 36           5.0         3.2          1.2         0.2     setosa
    +#> 37           5.5         3.5          1.3         0.2     setosa
    +#> 38           4.9         3.6          1.4         0.1     setosa
    +#> 39           4.4         3.0          1.3         0.2     setosa
    +#> 40           5.1         3.4          1.5         0.2     setosa
    +#> 41           5.0         3.5          1.3         0.3     setosa
    +#> 42           4.5         2.3          1.3         0.3     setosa
    +#> 43           4.4         3.2          1.3         0.2     setosa
    +#> 44           5.0         3.5          1.6         0.6     setosa
    +#> 45           5.1         3.8          1.9         0.4     setosa
    +#> 46           4.8         3.0          1.4         0.3     setosa
    +#> 47           5.1         3.8          1.6         0.2     setosa
    +#> 48           4.6         3.2          1.4         0.2     setosa
    +#> 49           5.3         3.7          1.5         0.2     setosa
    +#> 50           5.0         3.3          1.4         0.2     setosa
    +#> 51           7.0         3.2          4.7         1.4 versicolor
    +#> 52           6.4         3.2          4.5         1.5 versicolor
    +#> 53           6.9         3.1          4.9         1.5 versicolor
    +#> 54           5.5         2.3          4.0         1.3 versicolor
    +#> 55           6.5         2.8          4.6         1.5 versicolor
    +#> 56           5.7         2.8          4.5         1.3 versicolor
    +#> 57           6.3         3.3          4.7         1.6 versicolor
    +#> 58           4.9         2.4          3.3         1.0 versicolor
    +#> 59           6.6         2.9          4.6         1.3 versicolor
    +#> 60           5.2         2.7          3.9         1.4 versicolor
    +#> 61           5.0         2.0          3.5         1.0 versicolor
    +#> 62           5.9         3.0          4.2         1.5 versicolor
    +#> 63           6.0         2.2          4.0         1.0 versicolor
    +#> 64           6.1         2.9          4.7         1.4 versicolor
    +#> 65           5.6         2.9          3.6         1.3 versicolor
    +#> 66           6.7         3.1          4.4         1.4 versicolor
    +#> 67           5.6         3.0          4.5         1.5 versicolor
    +#> 68           5.8         2.7          4.1         1.0 versicolor
    +#> 69           6.2         2.2          4.5         1.5 versicolor
    +#> 70           5.6         2.5          3.9         1.1 versicolor
    +#> 71           5.9         3.2          4.8         1.8 versicolor
    +#> 72           6.1         2.8          4.0         1.3 versicolor
    +#> 73           6.3         2.5          4.9         1.5 versicolor
    +#> 74           6.1         2.8          4.7         1.2 versicolor
    +#> 75           6.4         2.9          4.3         1.3 versicolor
    +#> 76           6.6         3.0          4.4         1.4 versicolor
    +#> 77           6.8         2.8          4.8         1.4 versicolor
    +#> 78           6.7         3.0          5.0         1.7 versicolor
    +#> 79           6.0         2.9          4.5         1.5 versicolor
    +#> 80           5.7         2.6          3.5         1.0 versicolor
    +#> 81           5.5         2.4          3.8         1.1 versicolor
    +#> 82           5.5         2.4          3.7         1.0 versicolor
    +#> 83           5.8         2.7          3.9         1.2 versicolor
    +#> 84           6.0         2.7          5.1         1.6 versicolor
    +#> 85           5.4         3.0          4.5         1.5 versicolor
    +#> 86           6.0         3.4          4.5         1.6 versicolor
    +#> 87           6.7         3.1          4.7         1.5 versicolor
    +#> 88           6.3         2.3          4.4         1.3 versicolor
    +#> 89           5.6         3.0          4.1         1.3 versicolor
    +#> 90           5.5         2.5          4.0         1.3 versicolor
    +#> 91           5.5         2.6          4.4         1.2 versicolor
    +#> 92           6.1         3.0          4.6         1.4 versicolor
    +#> 93           5.8         2.6          4.0         1.2 versicolor
    +#> 94           5.0         2.3          3.3         1.0 versicolor
    +#> 95           5.6         2.7          4.2         1.3 versicolor
    +#> 96           5.7         3.0          4.2         1.2 versicolor
    +#> 97           5.7         2.9          4.2         1.3 versicolor
    +#> 98           6.2         2.9          4.3         1.3 versicolor
    +#> 99           5.1         2.5          3.0         1.1 versicolor
    +#> 100          5.7         2.8          4.1         1.3 versicolor
    +#> 101          6.3         3.3          6.0         2.5  virginica
    +#> 102          5.8         2.7          5.1         1.9  virginica
    +#> 103          7.1         3.0          5.9         2.1  virginica
    +#> 104          6.3         2.9          5.6         1.8  virginica
    +#> 105          6.5         3.0          5.8         2.2  virginica
    +#> 106          7.6         3.0          6.6         2.1  virginica
    +#> 107          4.9         2.5          4.5         1.7  virginica
    +#> 108          7.3         2.9          6.3         1.8  virginica
    +#> 109          6.7         2.5          5.8         1.8  virginica
    +#> 110          7.2         3.6          6.1         2.5  virginica
    +#> 111          6.5         3.2          5.1         2.0  virginica
    +#> 112          6.4         2.7          5.3         1.9  virginica
    +#> 113          6.8         3.0          5.5         2.1  virginica
    +#> 114          5.7         2.5          5.0         2.0  virginica
    +#> 115          5.8         2.8          5.1         2.4  virginica
    +#> 116          6.4         3.2          5.3         2.3  virginica
    +#> 117          6.5         3.0          5.5         1.8  virginica
    +#> 118          7.7         3.8          6.7         2.2  virginica
    +#> 119          7.7         2.6          6.9         2.3  virginica
    +#> 120          6.0         2.2          5.0         1.5  virginica
    +#> 121          6.9         3.2          5.7         2.3  virginica
    +#> 122          5.6         2.8          4.9         2.0  virginica
    +#> 123          7.7         2.8          6.7         2.0  virginica
    +#> 124          6.3         2.7          4.9         1.8  virginica
    +#> 125          6.7         3.3          5.7         2.1  virginica
    +#> 126          7.2         3.2          6.0         1.8  virginica
    +#> 127          6.2         2.8          4.8         1.8  virginica
    +#> 128          6.1         3.0          4.9         1.8  virginica
    +#> 129          6.4         2.8          5.6         2.1  virginica
    +#> 130          7.2         3.0          5.8         1.6  virginica
    +#> 131          7.4         2.8          6.1         1.9  virginica
    +#> 132          7.9         3.8          6.4         2.0  virginica
    +#> 133          6.4         2.8          5.6         2.2  virginica
    +#> 134          6.3         2.8          5.1         1.5  virginica
    +#> 135          6.1         2.6          5.6         1.4  virginica
    +#> 136          7.7         3.0          6.1         2.3  virginica
    +#> 137          6.3         3.4          5.6         2.4  virginica
    +#> 138          6.4         3.1          5.5         1.8  virginica
    +#> 139          6.0         3.0          4.8         1.8  virginica
    +#> 140          6.9         3.1          5.4         2.1  virginica
    +#> 141          6.7         3.1          5.6         2.4  virginica
    +#> 142          6.9         3.1          5.1         2.3  virginica
    +#> 143          5.8         2.7          5.1         1.9  virginica
    +#> 144          6.8         3.2          5.9         2.3  virginica
    +#> 145          6.7         3.3          5.7         2.5  virginica
    +#> 146          6.7         3.0          5.2         2.3  virginica
    +#> 147          6.3         2.5          5.0         1.9  virginica
    +#> 148          6.5         3.0          5.2         2.0  virginica
    +#> 149          6.2         3.4          5.4         2.3  virginica
    +#> 150          5.9         3.0          5.1         1.8  virginica
    +p2$id # get id
    +#> id: 14b3e9a0-0615-40ab-a93b-7c4044df4025
    +p2$validate(mtcars) # check if parameter can be updated
    +#> [1] FALSE
    +iris2 <- iris; iris2[1,1] <- 300 # create updated iris dataset
    +p2$validate(iris2) # check if parameter can be updated
    +#> [1] FALSE
    +iris3 <- iris; iris2[1,1] <- 100 # create updated iris dataset
    +p2$set(iris3) # set parameter to iris3
    +p2$print() # print it again
    +#> [1] "tbl2"
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/new_id.html b/docs/reference/new_id.html index 03704eec7..47674f574 100644 --- a/docs/reference/new_id.html +++ b/docs/reference/new_id.html @@ -1,101 +1,18 @@ - - - - - - - -Identifier — new_id • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Identifier — new_id • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,60 +101,59 @@

    Identifier

    Generate a new unique identifier.

    -
    new_id()
    - - -

    Value

    +
    Usage,
    new_id()
    +
    +

    Value

    Id object.

    -

    Details

    - -

    Identifiers are made using the uuid::UUIDgenerate().

    -

    See also

    - - +
    +
    +

    Details

    +

    Identifiers are made using the uuid::UUIDgenerate().

    +
    +
    +

    See also

    + +
    -

    Examples

    -
    # create new id
    -i <- new_id()
    -
    -# print id
    -print(i)
    -#> id: 43a158b3-35cc-4e7a-972f-6694d5f49029
    -
    -# convert to character
    -as.character(i)
    -#> [1] "43a158b3-35cc-4e7a-972f-6694d5f49029"
    -
    -# check if it is an Id object
    -is.Id(i)
    -#> [1] TRUE
    -
    +    
    +

    Examples

    +
    # create new id
    +i <- new_id()
    +
    +# print id
    +print(i)
    +#> id: cadf51e9-ebce-425a-a5b7-d25c37c10b4e
    +
    +# convert to character
    +as.character(i)
    +#> [1] "cadf51e9-ebce-425a-a5b7-d25c37c10b4e"
    +
    +# check if it is an Id object
    +is.Id(i)
    +#> [1] TRUE
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/new_optimization_problem.html b/docs/reference/new_optimization_problem.html index 195dc76ab..cdc17fb33 100644 --- a/docs/reference/new_optimization_problem.html +++ b/docs/reference/new_optimization_problem.html @@ -1,101 +1,18 @@ - - - - - - - -Optimization problem — new_optimization_problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Optimization problem — new_optimization_problem • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Generate a new empty OptimizationProblem object.

    +

    Generate a new empty OptimizationProblem object.

    -
    new_optimization_problem()
    +
    Usage,
    new_optimization_problem()
    +
    +

    Value

    +

    OptimizationProblem object.

    +
    + -

    Value

    - -

    OptimizationProblem object.

    -

    See also

    - - - -

    Examples

    -
    # create empty OptimizationProblem object
    -x <- new_optimization_problem()
    -
    -# print new object
    -print(x)
    -#> optimization problem (empty)
    +    
    +

    Examples

    +
    # create empty OptimizationProblem object
    +x <- new_optimization_problem()
    +
    +# print new object
    +print(x)
    +#> optimization problem (empty)
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/new_waiver.html b/docs/reference/new_waiver.html index 7bada84fd..304e96e5f 100644 --- a/docs/reference/new_waiver.html +++ b/docs/reference/new_waiver.html @@ -1,101 +1,18 @@ - - - - - - - -Waiver — new_waiver • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Waiver — new_waiver • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,59 +101,57 @@

    Waiver

    Create a waiver object.

    -
    new_waiver()
    - - -

    Value

    +
    Usage,
    new_waiver()
    +
    +

    Value

    Object of class Waiver.

    -

    Details

    - +
    +
    +

    Details

    This object is used to represent that the user has not manually specified a setting, and so defaults should be used. By explicitly using a new_waiver(), this means that NULL objects can be a valid setting. The use of a "waiver" object was inspired by the ggplot2 package.

    +
    -

    Examples

    -
    # create new waiver object
    -w <- new_waiver()
    -
    -# print object
    -print(w)
    -#> list()
    -#> attr(,"class")
    -#> [1] "Waiver"
    -
    -# is it a waiver object?
    -is.Waiver(w)
    -#> [1] TRUE
    -
    +    
    +

    Examples

    +
    # create new waiver object
    +w <- new_waiver()
    +
    +# print object
    +print(w)
    +#> list()
    +#> attr(,"class")
    +#> [1] "Waiver"
    +
    +# is it a waiver object?
    +is.Waiver(w)
    +#> [1] TRUE
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/number_of_features.html b/docs/reference/number_of_features.html index f3cd964b8..30d9495c3 100644 --- a/docs/reference/number_of_features.html +++ b/docs/reference/number_of_features.html @@ -1,101 +1,18 @@ - - - - - - - -Number of features — number_of_features • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Number of features — number_of_features • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,72 +101,66 @@

    Number of features

    Extract the number of features in an object.

    -
    number_of_features(x)
    -
    -# S4 method for ConservationProblem
    -number_of_features(x)
    -
    -# S4 method for OptimizationProblem
    -number_of_features(x)
    +    
    Usage,
    number_of_features(x)
     
    -# S4 method for ZonesRaster
    -number_of_features(x)
    +# S4 method for ConservationProblem
    +number_of_features(x)
     
    -# S4 method for ZonesCharacter
    -number_of_features(x)
    +# S4 method for OptimizationProblem +number_of_features(x) -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem), -OptimizationProblem, or Zones() object.

    +# S4 method for ZonesRaster +number_of_features(x) -

    Value

    +# S4 method for ZonesCharacter +number_of_features(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem), +OptimizationProblem, or Zones() object.

    +
    +
    +

    Value

    integer number of features.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.2) %>%
    -     add_binary_decisions()
    -
    -# print number of features
    -print(number_of_features(p))
    -#> [1] 5
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.2) %>%
    +     add_binary_decisions()
    +
    +# print number of features
    +print(number_of_features(p))
    +#> [1] 5
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/number_of_planning_units.html b/docs/reference/number_of_planning_units.html index 7e15f5915..348bc7403 100644 --- a/docs/reference/number_of_planning_units.html +++ b/docs/reference/number_of_planning_units.html @@ -1,101 +1,18 @@ - - - - - - - -Number of planning units — number_of_planning_units • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Number of planning units — number_of_planning_units • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,66 +101,60 @@

    Number of planning units

    Extract the number of planning units in an object.

    -
    number_of_planning_units(x)
    -
    -# S4 method for ConservationProblem
    -number_of_planning_units(x)
    -
    -# S4 method for OptimizationProblem
    -number_of_planning_units(x)
    +
    Usage,
    number_of_planning_units(x)
     
    -    

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem), -OptimizationProblem, or Zones() object.

    +# S4 method for ConservationProblem +number_of_planning_units(x) -

    Value

    +# S4 method for OptimizationProblem +number_of_planning_units(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem), +OptimizationProblem, or Zones() object.

    +
    +
    +

    Value

    integer number of planning units.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.2) %>%
    -     add_binary_decisions()
    -
    -# print number of planning units
    -print(number_of_planning_units(p))
    -#> [1] 90
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.2) %>%
    +     add_binary_decisions()
    +
    +# print number of planning units
    +print(number_of_planning_units(p))
    +#> [1] 90
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/number_of_total_units.html b/docs/reference/number_of_total_units.html index 043f5baa7..629e333e3 100644 --- a/docs/reference/number_of_total_units.html +++ b/docs/reference/number_of_total_units.html @@ -1,101 +1,18 @@ - - - - - - - -Number of total units — number_of_total_units • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Number of total units — number_of_total_units • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,81 +101,75 @@

    Number of total units

    Extract the number of total units in an object.

    -
    number_of_total_units(x)
    -
    -# S4 method for ConservationProblem
    -number_of_total_units(x)
    - -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem), -OptimizationProblem, or Zones() object.

    +
    Usage,
    number_of_total_units(x)
     
    -    

    Value

    +# S4 method for ConservationProblem +number_of_total_units(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem), +OptimizationProblem, or Zones() object.

    +
    +
    +

    Value

    integer number of total units.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# create problem with one zone
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions()
    -
    -# print number of planning units
    -print(number_of_planning_units(p1))
    -#> [1] 90
    -
    -# print number of total units
    -print(number_of_total_units(p1))
    -#> [1] 100
    -
    -# create problem with multiple zones
    -p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    -      add_binary_decisions()
    -
    -# print number of planning units
    -print(number_of_planning_units(p2))
    -#> [1] 90
    -
    -# print number of total units
    -print(number_of_total_units(p2))
    -#> [1] 100
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# create problem with one zone
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions()
    +
    +# print number of planning units
    +print(number_of_planning_units(p1))
    +#> [1] 90
    +
    +# print number of total units
    +print(number_of_total_units(p1))
    +#> [1] 100
    +
    +# create problem with multiple zones
    +p2 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    +      add_binary_decisions()
    +
    +# print number of planning units
    +print(number_of_planning_units(p2))
    +#> [1] 90
    +
    +# print number of total units
    +print(number_of_total_units(p2))
    +#> [1] 100
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/number_of_zones.html b/docs/reference/number_of_zones.html index 89f83ea26..0a6687bef 100644 --- a/docs/reference/number_of_zones.html +++ b/docs/reference/number_of_zones.html @@ -1,101 +1,18 @@ - - - - - - - -Number of zones — number_of_zones • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Number of zones — number_of_zones • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,75 +101,69 @@

    Number of zones

    Extract the number of zones in an object.

    -
    number_of_zones(x)
    -
    -# S4 method for ConservationProblem
    -number_of_zones(x)
    -
    -# S4 method for OptimizationProblem
    -number_of_zones(x)
    +    
    Usage,
    number_of_zones(x)
     
    -# S4 method for ZonesRaster
    -number_of_zones(x)
    +# S4 method for ConservationProblem
    +number_of_zones(x)
     
    -# S4 method for ZonesCharacter
    -number_of_zones(x)
    +# S4 method for OptimizationProblem +number_of_zones(x) -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem), -OptimizationProblem, or Zones() object.

    +# S4 method for ZonesRaster +number_of_zones(x) -

    Value

    +# S4 method for ZonesCharacter +number_of_zones(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem), +OptimizationProblem, or Zones() object.

    +
    +
    +

    Value

    integer number of zones.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_zones_stack, sim_features_zones)
    -
    -# print number of zones in a Zones object
    -print(number_of_zones(sim_features_zones))
    -#> [1] 3
    -# create problem with multiple zones
    -p <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    -     add_binary_decisions()
    -
    -# print number of zones in the problem
    -print(number_of_zones(p))
    -#> [1] 3
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_zones_stack, sim_features_zones)
    +
    +# print number of zones in a Zones object
    +print(number_of_zones(sim_features_zones))
    +#> [1] 3
    +# create problem with multiple zones
    +p <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    +     add_binary_decisions()
    +
    +# print number of zones in the problem
    +print(number_of_zones(p))
    +#> [1] 3
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/objectives.html b/docs/reference/objectives.html index 52d355f18..b6615553b 100644 --- a/docs/reference/objectives.html +++ b/docs/reference/objectives.html @@ -1,107 +1,24 @@ - - - - - - - -Add an objective — objectives • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add an objective — objectives • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    An objective is used to specify the overall goal of a conservation planning -problem(). All conservation planning problems involve minimizing +problem(). All conservation planning problems involve minimizing or maximizing some kind of objective. For instance, the planner may require a solution that conserves enough habitat for each species while minimizing the overall cost of the reserve network. Alternatively, the planner may @@ -196,141 +113,152 @@

    Add an objective

    ensuring that the cost of the reserve network does not exceed the budget.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    Please note that all conservation planning problems formulated using the prioritizr package require an objective function---failing to do so will return an error message when attempting to solve problem.

    The following objectives can be added to a conservation planning -problem():

    -
    - -
    add_min_set_objective()

    Minimize the cost of the +problem():

    +
    add_min_set_objective()
    +

    Minimize the cost of the solution whilst ensuring that all targets are met. This objective is similar to that used in Marxan.

    -
    add_max_cover_objective()

    Represent at least one + +

    add_max_cover_objective()
    +

    Represent at least one instance of as many features as possible within a given budget.

    -
    add_max_features_objective()

    Fulfill as many targets as + +

    add_max_features_objective()
    +

    Fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget.

    -
    add_min_shortfall_objective()

    Minimize the overall + +

    add_min_shortfall_objective()
    +

    Minimize the overall (weighted sum) shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget.

    -
    add_min_largest_shortfall_objective()

    Minimize the + +

    add_min_largest_shortfall_objective()
    +

    Minimize the largest (maximum) shortfall among all targets while ensuring that the cost of the solution does not exceed a budget.

    -
    add_max_phylo_div_objective()

    Maximize the phylogenetic + +

    add_max_phylo_div_objective()
    +

    Maximize the phylogenetic diversity of the features represented in the solution subject to a budget.

    -
    add_max_phylo_end_objective()

    Maximize the phylogenetic + +

    add_max_phylo_end_objective()
    +

    Maximize the phylogenetic endemism of the features represented in the solution subject to a budget.

    -
    add_max_utility_objective()

    Secure as much of the + +

    add_max_utility_objective()
    +

    Secure as much of the features as possible without exceeding a budget.

    -
    - -

    See also

    - - - -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features, sim_phylogeny)
    -
    -# create base problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_relative_targets(0.1) %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(verbose = FALSE)
    -
    - # create problem with added minimum set objective
    -p1 <- p %>% add_min_set_objective()
    -
    -# create problem with added maximum coverage objective
    -# note that this objective does not use targets
    -p2 <- p %>% add_max_cover_objective(500)
    -
    -# create problem with added maximum feature representation objective
    -p3 <- p %>% add_max_features_objective(1900)
    -
    -# create problem with added minimum shortfall objective
    -p4 <- p %>% add_min_shortfall_objective(1900)
    -
    -# create problem with added minimum largest shortfall objective
    -p5 <- p %>% add_min_largest_shortfall_objective(1900)
    -
    -# create problem with added maximum phylogenetic diversity objective
    -p6 <- p %>% add_max_phylo_div_objective(1900, sim_phylogeny)
    -
    -# create problem with added maximum phylogenetic diversity objective
    -p7 <- p %>% add_max_phylo_end_objective(1900, sim_phylogeny)
    -
    -# create problem with added maximum utility objective
    -# note that this objective does not use targets
    -p8 <- p %>% add_max_utility_objective(1900)
    -
    -# \dontrun{
    -# solve problems
    -s <- stack(solve(p1), solve(p2), solve(p3), solve(p4), solve(p5), solve(p6),
    -           solve(p7), solve(p8))
    -#> Warning: ignoring targets since the specified objective function doesn't use targets
    -#> Warning: ignoring targets since the specified objective function doesn't use targets
    -#> Warning: ignoring targets since the specified objective function doesn't use targets
    -#> Warning: ignoring targets since the specified objective function doesn't use targets
    -
    -# plot solutions
    -plot(s, axes = FALSE, box = FALSE,
    -     main = c("min set", "max coverage", "max features",
    -              "min shortfall", "min largest shortfall",
    -              "max phylogenetic diversity",
    -              "max phylogenetic endemism", "max utility"))
    -
    -# }
    +
    +
    +
    +

    See also

    + +
    + +
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features, sim_phylogeny)
    +
    +# create base problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_relative_targets(0.1) %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(verbose = FALSE)
    +
    + # create problem with added minimum set objective
    +p1 <- p %>% add_min_set_objective()
    +
    +# create problem with added maximum coverage objective
    +# note that this objective does not use targets
    +p2 <- p %>% add_max_cover_objective(500)
    +
    +# create problem with added maximum feature representation objective
    +p3 <- p %>% add_max_features_objective(1900)
    +
    +# create problem with added minimum shortfall objective
    +p4 <- p %>% add_min_shortfall_objective(1900)
    +
    +# create problem with added minimum largest shortfall objective
    +p5 <- p %>% add_min_largest_shortfall_objective(1900)
    +
    +# create problem with added maximum phylogenetic diversity objective
    +p6 <- p %>% add_max_phylo_div_objective(1900, sim_phylogeny)
    +
    +# create problem with added maximum phylogenetic diversity objective
    +p7 <- p %>% add_max_phylo_end_objective(1900, sim_phylogeny)
    +
    +# create problem with added maximum utility objective
    +# note that this objective does not use targets
    +p8 <- p %>% add_max_utility_objective(1900)
    +
    +# \dontrun{
    +# solve problems
    +s <- stack(solve(p1), solve(p2), solve(p3), solve(p4), solve(p5), solve(p6),
    +           solve(p7), solve(p8))
    +#> Warning: ignoring targets since the specified objective function doesn't use targets
    +#> Warning: ignoring targets since the specified objective function doesn't use targets
    +#> Warning: ignoring targets since the specified objective function doesn't use targets
    +#> Warning: ignoring targets since the specified objective function doesn't use targets
    +
    +# plot solutions
    +plot(s, axes = FALSE, box = FALSE,
    +     main = c("min set", "max coverage", "max features",
    +              "min shortfall", "min largest shortfall",
    +              "max phylogenetic diversity",
    +              "max phylogenetic endemism", "max utility"))
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/parameters.html b/docs/reference/parameters.html index 6a3bdfe11..bdd9d2aae 100644 --- a/docs/reference/parameters.html +++ b/docs/reference/parameters.html @@ -1,101 +1,18 @@ - - - - - - - -Parameters — parameters • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Parameters — parameters • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,66 +101,61 @@

    Parameters

    Create a new collection of Parameter objects.

    -
    parameters(...)
    +
    Usage,
    parameters(...)
    -

    Arguments

    - - - - - - -
    ...

    Parameter objects.

    - -

    Value

    - -

    Parameters object.

    -

    See also

    - - +
    +

    Arguments

    +
    ...
    +

    Parameter objects.

    +
    +
    +

    Value

    +

    Parameters object.

    +
    + -

    Examples

    -
    # create two Parameter objects
    -p1 <- binary_parameter("parameter one", 1)
    -print(p1)
    -#> parameter one (1)
    -
    -p2 <- numeric_parameter("parameter two", 5)
    -print(p2)
    -#> parameter two (5)
    -
    -# store Parameter objects in a Parameters object
    -p <- parameters(p1, p2)
    -print(p)
    -#> <environment: 0x5642197ee900>
    -#> attr(,"class")
    -#> [1] "Parameters"  "pproto"      "proto"       "environment"
    -
    +    
    +

    Examples

    +
    # create two Parameter objects
    +p1 <- binary_parameter("parameter one", 1)
    +print(p1)
    +#> parameter one (1)
    +
    +p2 <- numeric_parameter("parameter two", 5)
    +print(p2)
    +#> parameter two (5)
    +
    +# store Parameter objects in a Parameters object
    +p <- parameters(p1, p2)
    +print(p)
    +#> <environment: 0x55f26c2269d8>
    +#> attr(,"class")
    +#> [1] "Parameters"  "pproto"      "proto"       "environment"
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/penalties.html b/docs/reference/penalties.html index e0750733d..4737e9bf6 100644 --- a/docs/reference/penalties.html +++ b/docs/reference/penalties.html @@ -1,105 +1,22 @@ - - - - - - - -Add a penalty — penalties • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add a penalty — penalties • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    A penalty can be applied to a conservation planning problem() to +

    A penalty can be applied to a conservation planning problem() to penalize solutions according to a specific metric. Penalties---unlike -constraints---act as an explicit trade-off with the objective -being minimized or maximized (e.g. solution cost when used with -add_min_set_objective()).

    +constraints---act as an explicit trade-off with the objective +being minimized or maximized (e.g., solution cost when used with +add_min_set_objective()).

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    Both penalties and constraints can be used to modify a problem and identify solutions that exhibit specific characteristics. Constraints work by invalidating solutions that do not exhibit specific characteristics. On the other hand, penalties work by specifying trade-offs against the main problem objective and are mediated by a penalty factor.

    The following penalties can be added to a conservation planning -problem():

    -
    - -
    add_boundary_penalties()

    Add penalties to a +problem():

    +
    add_boundary_penalties()
    +

    Add penalties to a conservation problem to favor solutions that have planning units clumped together into contiguous areas.

    -
    add_connectivity_penalties()

    Add penalties to a + +

    add_connectivity_penalties()
    +

    Add penalties to a conservation problem to favor solutions that select planning units with high connectivity between them.

    -
    add_linear_penalties()

    Add penalties to a + +

    add_linear_penalties()
    +

    Add penalties to a conservation problem to favor solutions that avoid selecting planning units based on a certain variable -(e.g. anthropogenic pressure).

    - - -
    - -

    See also

    - - +(e.g., anthropogenic pressure).

    + + + +
    +
    +

    See also

    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create basic problem
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with boundary penalties
    -p2 <- p1 %>% add_boundary_penalties(5, 1)
    -
    -# create connectivity matrix based on spatial proximity
    - scm <- as.data.frame(sim_pu_raster, xy = TRUE, na.rm = FALSE)
    - scm <- 1 / (as.matrix(dist(scm)) + 1)
    -
    -# remove weak and moderate connections between planning units to reduce
    -# run time
    -scm[scm < 0.85] <- 0
    -
    -# create problem with connectivity penalties
    -p3 <- p1 %>% add_connectivity_penalties(25, data = scm)
    -
    -# create problem with linear penalties,
    -# here the penalties will be based on random numbers to keep it simple
    -# \dontrun{
    -# simulate penalty data
    -# (note this requires the RandomFields package to be installed)
    -sim_penalty_raster <- simulate_cost(sim_pu_raster)
    -
    -# plot penalty data
    -plot(sim_penalty_raster, main = "penalty data", axes = FALSE, box = FALSE)
    -
    -
    -# create problem with linear penalties, with a penalty scaling factor of 100
    -p4 <- p1 %>% add_linear_penalties(100, data = sim_penalty_raster)
    -
    -# solve problems
    -s <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    -
    -# plot solutions
    -plot(s, axes = FALSE, box = FALSE,
    -     main = c("basic solution", "boundary penalties",
    -              "connectivity penalties", "linear penalties"))
    -
    - # }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create basic problem
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with boundary penalties
    +p2 <- p1 %>% add_boundary_penalties(5, 1)
    +
    +# create connectivity matrix based on spatial proximity
    + scm <- as.data.frame(sim_pu_raster, xy = TRUE, na.rm = FALSE)
    + scm <- 1 / (as.matrix(dist(scm)) + 1)
    +
    +# remove weak and moderate connections between planning units to reduce
    +# run time
    +scm[scm < 0.85] <- 0
    +
    +# create problem with connectivity penalties
    +p3 <- p1 %>% add_connectivity_penalties(25, data = scm)
    +
    +# create problem with linear penalties,
    +# here the penalties will be based on random numbers to keep it simple
    +# \dontrun{
    +# simulate penalty data
    +# (note this requires the RandomFields package to be installed)
    +sim_penalty_raster <- simulate_cost(sim_pu_raster)
    +
    +# plot penalty data
    +plot(sim_penalty_raster, main = "penalty data", axes = FALSE, box = FALSE)
    +
    +
    +# create problem with linear penalties, with a penalty scaling factor of 100
    +p4 <- p1 %>% add_linear_penalties(100, data = sim_penalty_raster)
    +
    +# solve problems
    +s <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    +
    +# plot solutions
    +plot(s, axes = FALSE, box = FALSE,
    +     main = c("basic solution", "boundary penalties",
    +              "connectivity penalties", "linear penalties"))
    +
    + # }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/pipe.html b/docs/reference/pipe.html index 2ff599fc3..140894358 100644 --- a/docs/reference/pipe.html +++ b/docs/reference/pipe.html @@ -1,102 +1,19 @@ - - - - - - - -Pipe operator — %>% • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Pipe operator — %>% • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,64 +103,60 @@

    Pipe operator

    as a series of imperative procedures.

    +
    Usage,NULL
    -

    Arguments

    - - - - - - -
    lhs, rhs

    An object and a function.

    - -

    Value

    - +
    +

    Arguments

    +
    lhs, rhs
    +

    An object and a function.

    +
    +
    +

    Value

    An object.

    -

    See also

    - -

    magrittr::%>%(), tee().

    +
    +
    +

    See also

    +

    magrittr::%>%(), tee().

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# generate 100 random numbers and calculate the mean
    -mean(runif(100))
    -#> [1] 0.473179
    -
    -# reset the seed
    -set.seed(500)
    -
    -# repeat the previous procedure but use the pipe operator instead of nesting
    -# function calls inside each other.
    -runif(100) %>% mean()
    -#> [1] 0.473179
    -
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# generate 100 random numbers and calculate the mean
    +mean(runif(100))
    +#> [1] 0.473179
    +
    +# reset the seed
    +set.seed(500)
    +
    +# repeat the previous procedure but use the pipe operator instead of nesting
    +# function calls inside each other.
    +runif(100) %>% mean()
    +#> [1] 0.473179
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/portfolios.html b/docs/reference/portfolios.html index e89300785..83e4f2c6e 100644 --- a/docs/reference/portfolios.html +++ b/docs/reference/portfolios.html @@ -1,109 +1,26 @@ - - - - - - - -Solution portfolios — portfolios • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Solution portfolios — portfolios • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -192,32 +109,33 @@

    Solution portfolios

    Conservation planning exercises rarely have access to all the data needed to identify the truly perfect solution. This is because available data may lack important details -(e.g. land acquisition costs may be unavailable), contain errors -(e.g. species presence/absence data may have false positives), +(e.g., land acquisition costs may be unavailable), contain errors +(e.g., species presence/absence data may have false positives), or key objectives may not be formally incorporated into the -prioritization process (e.g. future land use requirements). +prioritization process (e.g., future land use requirements). As such, conservation planners can help decision makers by providing them with a portfolio of solutions to inform their decision.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    The following portfolios can be added to a conservation planning -problem(). +problem(). Note that all methods for generating portfolios return solutions that are within the specified optimality gap.

    -
    - -
    add_extra_portfolio()

    Generate a portfolio of solutions +

    add_extra_portfolio()
    +

    Generate a portfolio of solutions by storing feasible solutions found during the optimization process. This method is useful for quickly obtaining multiple solutions, but does not provide any guarantees on the number of solutions, or the quality of solutions. Note that it requires the Gurobi solver.

    -
    add_top_portfolio()

    Generate a portfolio of + +

    add_top_portfolio()
    +

    Generate a portfolio of solutions by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions). This is useful for examining differences among near-optimal solutions. @@ -225,113 +143,117 @@

    Details to calculate selection frequencies for small problems. Note that it requires the Gurobi solver.

    -
    add_gap_portfolio()

    Generate a portfolio of solutions + +

    add_gap_portfolio()
    +

    Generate a portfolio of solutions by finding a certain number of solutions that are all within a pre- specified optimality gap. This method is useful for generating multiple solutions that can be used to calculate selection frequencies for moderate and large-sized problems (similar to Marxan). Note that it requires the Gurobi solver.

    -
    add_cuts_portfolio()

    Generate a portfolio of distinct + +

    add_cuts_portfolio()
    +

    Generate a portfolio of distinct solutions within a pre-specified optimality gap using Bender's cuts. -This is recommended as a replacement for add_top_portfolio() +This is recommended as a replacement for add_top_portfolio() when the Gurobi software is not available.

    -
    add_shuffle_portfolio()

    Generate a portfolio of + +

    add_shuffle_portfolio()
    +

    Generate a portfolio of solutions by randomly reordering the data prior to attempting to solve the problem. -This is recommended as a replacement for add_gap_portfolio() +This is recommended as a replacement for add_gap_portfolio() when the Gurobi software is not available.

    -
    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.1) %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(gap = 0.02, verbose = FALSE)
    -
    -# create problem with cuts portfolio with 4 solutions
    -p1 <- p %>% add_cuts_portfolio(4)
    -
    -# create problem with shuffle portfolio with 4 solutions
    -p2 <- p %>% add_shuffle_portfolio(4)
    -# \dontrun{
    -# create problem with extra portfolio
    -p3 <- p %>% add_extra_portfolio()
    -
    -# create problem with top portfolio with 4 solutions
    -p4 <- p %>% add_top_portfolio(4)
    -
    -# create problem with gap portfolio with 4 solutions within 50% of optimality
    -p5 <- p %>% add_gap_portfolio(4, 0.5)
    -
    -# solve problems and create solution portfolios
    -s <- list(solve(p1), solve(p2), solve(p3), solve(p4), solve(p5))
    -
    -# plot solutions from extra portfolio
    -plot(stack(s[[1]]), axes = FALSE, box = FALSE)
    -
    -
    -# plot solutions from top portfolio
    -plot(stack(s[[2]]), axes = FALSE, box = FALSE)
    -
    -
    -# plot solutions from gap portfolio
    -plot(stack(s[[3]]), axes = FALSE, box = FALSE)
    -
    -
    -# plot solutions from cuts portfolio
    -plot(stack(s[[4]]), axes = FALSE, box = FALSE)
    -
    -
    -# plot solutions from shuffle portfolio
    -plot(stack(s[[5]]), axes = FALSE, box = FALSE)
    -
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.1) %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(gap = 0.02, verbose = FALSE)
    +
    +# create problem with cuts portfolio with 4 solutions
    +p1 <- p %>% add_cuts_portfolio(4)
    +
    +# create problem with shuffle portfolio with 4 solutions
    +p2 <- p %>% add_shuffle_portfolio(4)
    +# \dontrun{
    +# create problem with extra portfolio
    +p3 <- p %>% add_extra_portfolio()
    +
    +# create problem with top portfolio with 4 solutions
    +p4 <- p %>% add_top_portfolio(4)
    +
    +# create problem with gap portfolio with 4 solutions within 50% of optimality
    +p5 <- p %>% add_gap_portfolio(4, 0.5)
    +
    +# solve problems and create solution portfolios
    +s <- list(solve(p1), solve(p2), solve(p3), solve(p4), solve(p5))
    +
    +# plot solutions from extra portfolio
    +plot(stack(s[[1]]), axes = FALSE, box = FALSE)
    +
    +
    +# plot solutions from top portfolio
    +plot(stack(s[[2]]), axes = FALSE, box = FALSE)
    +
    +
    +# plot solutions from gap portfolio
    +plot(stack(s[[3]]), axes = FALSE, box = FALSE)
    +
    +
    +# plot solutions from cuts portfolio
    +plot(stack(s[[4]]), axes = FALSE, box = FALSE)
    +
    +
    +# plot solutions from shuffle portfolio
    +plot(stack(s[[5]]), axes = FALSE, box = FALSE)
    +
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/pproto.html b/docs/reference/pproto.html index 6f6831aa0..6f027b21a 100644 --- a/docs/reference/pproto.html +++ b/docs/reference/pproto.html @@ -1,102 +1,19 @@ - - - - - - - -Create a new pproto object — pproto • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Create a new pproto object — pproto • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,79 +103,68 @@

    Create a new pproto object

    from the ggproto system used in the ggplot2 package.

    -
    pproto(`_class` = NULL, `_inherit` = NULL, ...)
    +
    Usage,
    pproto(`_class` = NULL, `_inherit` = NULL, ...)
    -

    Arguments

    - - - - - - - - - - - - - - -
    _class

    Class name to assign to the object. This is stored as the class +

    +

    Arguments

    +
    _class
    +

    Class name to assign to the object. This is stored as the class attribute of the object. This is optional: if NULL (the default), -no class name will be added to the object.

    _inherit

    pproto object to inherit from. If NULL, don"t -inherit from any object.

    ...

    A list of members to add to the new pproto object.

    - - -

    Examples

    -
    Adder <- pproto("Adder",
    -  x = 0,
    -  add = function(self, n) {
    -    self$x <- self$x + n
    -    self$x
    -  }
    -)
    -
    -Adder$add(10)
    -#> [1] 10
    -Adder$add(10)
    -#> [1] 20
    -
    -Abacus <- pproto("Abacus", Adder,
    -  subtract = function(self, n) {
    -    self$x <- self$x - n
    -    self$x
    -  }
    -)
    -Abacus$add(10)
    -#> [1] 30
    -Abacus$subtract(10)
    -#> [1] 20
    -
    +no class name will be added to the object.

    +
    _inherit
    +

    pproto object to inherit from. If NULL, don"t +inherit from any object.

    +
    ...
    +

    A list of members to add to the new pproto object.

    +
    + +
    +

    Examples

    +
    Adder <- pproto("Adder",
    +  x = 0,
    +  add = function(self, n) {
    +    self$x <- self$x + n
    +    self$x
    +  }
    +)
    +
    +Adder$add(10)
    +#> [1] 10
    +Adder$add(10)
    +#> [1] 20
    +
    +Abacus <- pproto("Abacus", Adder,
    +  subtract = function(self, n) {
    +    self$x <- self$x - n
    +    self$x
    +  }
    +)
    +Abacus$add(10)
    +#> [1] 30
    +Abacus$subtract(10)
    +#> [1] 20
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/predefined_optimization_problem.html b/docs/reference/predefined_optimization_problem.html index 7cd15ac98..10a84e51f 100644 --- a/docs/reference/predefined_optimization_problem.html +++ b/docs/reference/predefined_optimization_problem.html @@ -1,101 +1,18 @@ - - - - - - - -Predefined optimization problem — predefined_optimization_problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Predefined optimization problem — predefined_optimization_problem • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Create a new OptimizationProblem object.

    +

    Create a new OptimizationProblem object.

    -
    predefined_optimization_problem(x)
    - -

    Arguments

    - - - - - - -
    x

    list object containing data to construct the problem.

    - -

    Details

    +
    Usage,
    predefined_optimization_problem(x)
    +
    +

    Arguments

    +
    x
    +

    list object containing data to construct the problem.

    +
    +
    +

    Details

    The argument to x must be a list that contains the following elements:

    -
    +
    modelsense
    +

    character model sense.

    + + +
    number_of_features
    +

    integer number of features in problem.

    + + +
    number_of_planning_units
    +

    integer number of planning units.

    + -
    modelsense

    character model sense.

    +
    A_i
    +

    integer row indices for problem matrix.

    -
    number_of_features

    integer number of features in problem.

    -
    number_of_planning_units

    integer number of planning units.

    +
    A_j
    +

    integer column indices for problem matrix.

    -
    A_i

    integer row indices for problem matrix.

    -
    A_j

    integer column indices for problem matrix.

    +
    A_x
    +

    numeric values for problem matrix.

    -
    A_x

    numeric values for problem matrix.

    -
    obj

    numeric objective function values.

    +
    obj
    +

    numeric objective function values.

    -
    lb

    numeric lower bound for decision values.

    -
    ub

    numeric upper bound for decision values.

    +
    lb
    +

    numeric lower bound for decision values.

    -
    rhs

    numeric right-hand side values.

    -
    sense

    numeric constraint senses.

    +
    ub
    +

    numeric upper bound for decision values.

    -
    vtype

    character variable types. These are used to specify + +

    rhs
    +

    numeric right-hand side values.

    + + +
    sense
    +

    numeric constraint senses.

    + + +
    vtype
    +

    character variable types. These are used to specify that the decision variables are binary ("B") or continuous ("C").

    -
    row_ids

    character identifiers for the rows in the problem + +

    row_ids
    +

    character identifiers for the rows in the problem matrix.

    -
    col_ids

    character identifiers for the columns in the problem + +

    col_ids
    +

    character identifiers for the columns in the problem matrix.

    -
    - - -

    Examples

    -
    # create list with problem data
    -l <- list(modelsense = "min", number_of_features = 2,
    -          number_of_planning_units = 3, number_of_zones = 1,
    -          A_i = c(0L, 1L, 0L, 1L, 0L, 1L), A_j = c(0L, 0L, 1L, 1L, 2L, 2L),
    -          A_x = c(2, 10, 1, 10, 1, 10), obj = c(1, 2, 2), lb = c(0, 1, 0),
    -          ub = c(0, 1, 1), rhs = c(2, 10), compressed_formulation = TRUE,
    -          sense = c(">=", ">="), vtype = c("B", "B", "B"),
    -          row_ids = c("spp_target", "spp_target"),
    -          col_ids = c("pu", "pu", "pu"))
    -
    -# create OptimizationProblem object
    -x <- predefined_optimization_problem(l)
    -
    -# print new object
    -print(x)
    -#> optimization problem
    -#>   model sense: min
    -#>   dimensions:  2, 3, 6 (nrow, ncol, ncell)
    -#>   variables:   3 (B)
    +
    +
    + +
    +

    Examples

    +
    # create list with problem data
    +l <- list(modelsense = "min", number_of_features = 2,
    +          number_of_planning_units = 3, number_of_zones = 1,
    +          A_i = c(0L, 1L, 0L, 1L, 0L, 1L), A_j = c(0L, 0L, 1L, 1L, 2L, 2L),
    +          A_x = c(2, 10, 1, 10, 1, 10), obj = c(1, 2, 2), lb = c(0, 1, 0),
    +          ub = c(0, 1, 1), rhs = c(2, 10), compressed_formulation = TRUE,
    +          sense = c(">=", ">="), vtype = c("B", "B", "B"),
    +          row_ids = c("spp_target", "spp_target"),
    +          col_ids = c("pu", "pu", "pu"))
    +
    +# create OptimizationProblem object
    +x <- predefined_optimization_problem(l)
    +
    +# print new object
    +print(x)
    +#> optimization problem
    +#>   model sense: min
    +#>   dimensions:  2, 3, 6 (nrow, ncol, ncell)
    +#>   variables:   3 (B)
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/presolve_check.html b/docs/reference/presolve_check.html index 40b5ca9d3..6166e7d59 100644 --- a/docs/reference/presolve_check.html +++ b/docs/reference/presolve_check.html @@ -1,108 +1,25 @@ - - - - - - - -Presolve check — presolve_check • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Presolve check — presolve_check • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Check a conservation planning problem() for potential issues +

    Check a conservation planning problem() for potential issues before trying to solve it. Specifically, problems are checked for (i) values that are likely to result in "strange" solutions and (ii) values that are likely to cause numerical instability issues and lead to unreasonably long @@ -198,37 +115,34 @@

    Presolve check

    verify if a problem has a feasible solution or not.

    -
    presolve_check(x)
    -
    -# S3 method for ConservationProblem
    -presolve_check(x)
    +    
    Usage,
    presolve_check(x)
     
    -# S3 method for OptimizationProblem
    -presolve_check(x)
    +# S3 method for ConservationProblem +presolve_check(x) -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) or -OptimizationProblem object.

    - -

    Value

    +# S3 method for OptimizationProblem +presolve_check(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) or +OptimizationProblem object.

    +
    +
    +

    Value

    logical value indicating if all checks are passed successfully.

    -

    Details

    - +
    +
    +

    Details

    This function checks for issues that are likely to result in "strange" solutions. Specifically, it checks if (i) all planning units are locked in, (ii) all planning units are locked out, and (iii) all planning units have negative cost values (after applying penalties if any were specified). Although such conservation planning problems are mathematically valid, they are generally the result of a coding mistake -when building the problem (e.g. using an absurdly high +when building the problem (e.g., using an absurdly high penalty value or using the wrong dataset to lock in planning units). Thus such issues, if they are indeed issues and not false positives, can be fixed by carefully checking the code, data, and parameters used to build @@ -236,22 +150,22 @@

    Details

    This function then checks for values that may lead to numerical instability issues when solving the problem. Specifically, it checks if the range of values in certain components of the optimization problem are over a -certain threshold (i.e. \(1 \times 10 ^9\)) or if the values +certain threshold (i.e., \(1 \times 10 ^9\)) or if the values themselves exceed a certain threshold -(i.e. \(1 \times 10^{10}\)). +(i.e., \(1 \times 10^{10}\)). In most cases, such issues will simply cause an exact algorithm solver to take a very long time to generate a solution. In rare cases, such issues can cause incorrect calculations which can lead to exact algorithm solvers returning infeasible solutions -(e.g. a solution to the minimum set problem where not all targets are met) -or solutions that exceed the specified optimality gap (e.g. a suboptimal +(e.g., a solution to the minimum set problem where not all targets are met) +or solutions that exceed the specified optimality gap (e.g., a suboptimal solution when a zero optimality gap is specified).

    What can you do if a conservation planning problem fails to pass these checks? Well, this function will have thrown some warning messages describing the source of these issues, so read them carefully. For instance, a common issue is when a relatively large penalty value is -specified for boundary (add_boundary_penalties()) or -connectivity penalties (add_connectivity_penalties()). This +specified for boundary (add_boundary_penalties()) or +connectivity penalties (add_connectivity_penalties()). This can be fixed by trying a smaller penalty value. In such cases, the original penalty value supplied was so high that the optimal solution would just have selected every single planning unit in the solution---and @@ -261,7 +175,7 @@

    Details costs of the planning units in terms of USD then you might have some planning units that cost over one billion dollars in large-scale planning exercises. This can be fixed by rescaling the values so that they -are smaller (e.g. multiplying the values by a number smaller than one, or +are smaller (e.g., multiplying the values by a number smaller than one, or expressing them as a fraction of the maximum cost). Let's consider another common issue, let's pretend that you used habitat suitability models to predict the amount of suitable habitat @@ -287,7 +201,7 @@

    Details solving the problem, you will need to manually recalculate the cost of the solutions but at least now you can be confident that you have the optimal solution. Now let's pretend that you are using the maximum features -objective (i.e. add_max_features_objective()) and assigned some +objective (i.e., add_max_features_objective()) and assigned some really high weights to the targets for some features to ensure that their targets were met in the optimal solution. If you set the weights for these features to one billion then you will probably run into numerical @@ -295,133 +209,132 @@

    Details guarantee that these features will be represented in the optimal solution and use this value instead of one billion. This minimum weight value can be calculated as the sum of the weight values for the other features -and adding a small number to it (e.g. 1). Finally, if you're running out +and adding a small number to it (e.g., 1). Finally, if you're running out of ideas for addressing numerical stability issues you have one remaining option: you can use the numeric_focus argument in the -add_gurobi_solver() function to tell the solver to pay extra +add_gurobi_solver() function to tell the solver to pay extra attention to numerical instability issues. This is not a free lunch, however, because telling the solver to pay extra attention to numerical issues can substantially increase run time. So, if you have problems that are already taking an unreasonable time to solve, then this will not help at all.

    -

    See also

    - - +
    + -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create minimal problem with no issues
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions()
    -
    -# run presolve checks
    -# note that no warning is thrown which suggests that we should not
    -# encounter any numerical stability issues when trying to solve the problem
    -print(presolve_check(p1))
    -#> [1] TRUE
    -
    -# create a minimal problem, containing cost values that are really
    -# high so that they could cause numerical instability issues when trying
    -# to solve it
    -sim_pu_raster2 <- sim_pu_raster
    -sim_pu_raster2[1] <- 1e+15
    -p2 <- problem(sim_pu_raster2, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions()
    -
    -# run presolve checks
    -# note that a warning is thrown which suggests that we might encounter
    -# some issues, such as long solve time or suboptimal solutions, when
    -# trying to solve the problem
    -print(presolve_check(p2))
    -#> Warning: planning units with very high (> 1e+6) or very low (< 1e-6) non-zero cost values note this may be a false positive
    -#> [1] FALSE
    -
    -# create a minimal problem with connectivity penalties values that have
    -# a really high penalty value that is likely to cause numerical instability
    -# issues when trying to solve the it
    -cm <- adjacency_matrix(sim_pu_raster)
    -p3 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_connectivity_penalties(1e+15, data = cm) %>%
    -      add_binary_decisions()
    -
    -# run presolve checks
    -# note that a warning is thrown which suggests that we might encounter
    -# some numerical instability issues when trying to solve the problem
    -print(presolve_check(p3))
    -#> Warning: penalty multiplied connectivity values are very high
    -#> [1] FALSE
    -# \dontrun{
    -# let's forcibly solve the problem using Gurobi and tell it to
    -# be extra careful about numerical instability problems
    -s3 <- p3 %>%
    -      add_gurobi_solver(numeric_focus = TRUE) %>%
    -      solve(force = TRUE)
    -#> Warning: penalty multiplied connectivity values are very high
    -#> Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
    -#> Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    -#> Optimize a model with 581 rows, 378 columns and 1602 nonzeros
    -#> Model fingerprint: 0xaaf590f0
    -#> Variable types: 0 continuous, 378 integer (378 binary)
    -#> Coefficient statistics:
    -#>   Matrix range     [2e-01, 1e+00]
    -#>   Objective range  [2e+02, 1e+15]
    -#>   Bounds range     [1e+00, 1e+00]
    -#>   RHS range        [3e+00, 8e+00]
    -#> Warning: Model contains large objective coefficients
    -#> Found heuristic solution: objective -2.88000e+17
    -#> 
    -#> Explored 0 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)
    -#> Thread count was 1 (of 8 available processors)
    -#> 
    -#> Solution count 1: -2.88e+17 
    -#> No other solutions better than -2.88e+17
    -#> 
    -#> Optimal solution found (tolerance 1.00e-01)
    -#> Best objective -2.880000000000e+17, best bound -2.880000000000e+17, gap 0.0000%
    -
    -# plot solution
    -# we can see that all planning units were selected because the connectivity
    -# penalty is so high that cost becomes irrelevant, so we should try using
    -# a much lower penalty value
    -plot(s3, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create minimal problem with no issues
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions()
    +
    +# run presolve checks
    +# note that no warning is thrown which suggests that we should not
    +# encounter any numerical stability issues when trying to solve the problem
    +print(presolve_check(p1))
    +#> [1] TRUE
    +
    +# create a minimal problem, containing cost values that are really
    +# high so that they could cause numerical instability issues when trying
    +# to solve it
    +sim_pu_raster2 <- sim_pu_raster
    +sim_pu_raster2[1] <- 1e+15
    +p2 <- problem(sim_pu_raster2, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions()
    +
    +# run presolve checks
    +# note that a warning is thrown which suggests that we might encounter
    +# some issues, such as long solve time or suboptimal solutions, when
    +# trying to solve the problem
    +print(presolve_check(p2))
    +#> Warning: planning units with very high (> 1e+6) or very low (< 1e-6) non-zero cost values note this may be a false positive
    +#> [1] FALSE
    +
    +# create a minimal problem with connectivity penalties values that have
    +# a really high penalty value that is likely to cause numerical instability
    +# issues when trying to solve the it
    +cm <- adjacency_matrix(sim_pu_raster)
    +p3 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_connectivity_penalties(1e+15, data = cm) %>%
    +      add_binary_decisions()
    +
    +# run presolve checks
    +# note that a warning is thrown which suggests that we might encounter
    +# some numerical instability issues when trying to solve the problem
    +print(presolve_check(p3))
    +#> Warning: penalty multiplied connectivity values are very high
    +#> [1] FALSE
    +# \dontrun{
    +# let's forcibly solve the problem using Gurobi and tell it to
    +# be extra careful about numerical instability problems
    +s3 <- p3 %>%
    +      add_gurobi_solver(numeric_focus = TRUE) %>%
    +      solve(force = TRUE)
    +#> Warning: penalty multiplied connectivity values are very high
    +#> Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
    +#> Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    +#> Optimize a model with 581 rows, 378 columns and 1602 nonzeros
    +#> Model fingerprint: 0xaaf590f0
    +#> Variable types: 0 continuous, 378 integer (378 binary)
    +#> Coefficient statistics:
    +#>   Matrix range     [2e-01, 1e+00]
    +#>   Objective range  [2e+02, 1e+15]
    +#>   Bounds range     [1e+00, 1e+00]
    +#>   RHS range        [3e+00, 8e+00]
    +#> Warning: Model contains large objective coefficients
    +#> Found heuristic solution: objective -2.88000e+17
    +#> 
    +#> Explored 0 nodes (0 simplex iterations) in 0.00 seconds (0.00 work units)
    +#> Thread count was 1 (of 8 available processors)
    +#> 
    +#> Solution count 1: -2.88e+17 
    +#> No other solutions better than -2.88e+17
    +#> 
    +#> Optimal solution found (tolerance 1.00e-01)
    +#> Best objective -2.880000000000e+17, best bound -2.880000000000e+17, gap 0.0000%
    +
    +# plot solution
    +# we can see that all planning units were selected because the connectivity
    +# penalty is so high that cost becomes irrelevant, so we should try using
    +# a much lower penalty value
    +plot(s3, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/print.html b/docs/reference/print.html index 9697dde62..5c4d3f511 100644 --- a/docs/reference/print.html +++ b/docs/reference/print.html @@ -1,101 +1,18 @@ - - - - - - - -Print — print • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Print — print • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,85 +101,78 @@

    Print

    Display information about an object.

    -
    # S3 method for ConservationProblem
    -print(x, ...)
    +    
    Usage,
    # S3 method for ConservationProblem
    +print(x, ...)
     
    -# S3 method for ConservationModifier
    -print(x, ...)
    +# S3 method for ConservationModifier
    +print(x, ...)
     
    -# S3 method for Id
    -print(x, ...)
    +# S3 method for Id
    +print(x, ...)
     
    -# S4 method for Id
    -print(x)
    +# S4 method for Id
    +print(x)
     
    -# S3 method for OptimizationProblem
    -print(x, ...)
    +# S3 method for OptimizationProblem
    +print(x, ...)
     
    -# S3 method for ScalarParameter
    -print(x, ...)
    +# S3 method for ScalarParameter
    +print(x, ...)
     
    -# S3 method for ArrayParameter
    -print(x, ...)
    +# S3 method for ArrayParameter
    +print(x, ...)
     
    -# S3 method for Solver
    -print(x, ...)
    +# S3 method for Solver
    +print(x, ...)
     
    -# S3 method for Zones
    -print(x, ...)
    +# S3 method for Zones
    +print(x, ...)
     
    -# S4 method for tbl_df
    -print(x)
    - -

    Arguments

    - - - - - - - - - - -
    x

    Any object.

    ...

    not used.

    - -

    Value

    +# S4 method for tbl_df +print(x)
    +
    +

    Arguments

    +
    x
    +

    Any object.

    +
    ...
    +

    not used.

    +
    +
    +

    Value

    None.

    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    a <- 1:4
    -print(a)
    -#> [1] 1 2 3 4
    +    
    +

    Examples

    +
    a <- 1:4
    +print(a)
    +#> [1] 1 2 3 4
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/prioritizr-deprecated.html b/docs/reference/prioritizr-deprecated.html index 656e5b995..4147df6fb 100644 --- a/docs/reference/prioritizr-deprecated.html +++ b/docs/reference/prioritizr-deprecated.html @@ -1,61 +1,5 @@ - - - - - - - -Deprecation notice — prioritizr-deprecated • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Deprecation notice — prioritizr-deprecated • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -201,110 +118,120 @@

    Deprecation notice

    (where applicable). If a function is described as being renamed, then this means that only the name of the function has changed -(i.e. the inputs, outputs, and underlying code remain the same).

    +(i.e., the inputs, outputs, and underlying code remain the same).

    -
    add_connected_constraints(...)
    +    
    Usage,
    add_connected_constraints(...)
     
    -add_corridor_constraints(...)
    +add_corridor_constraints(...)
     
    -set_number_of_threads(...)
    +set_number_of_threads(...)
     
    -get_number_of_threads(...)
    +get_number_of_threads(...)
     
    -is.parallel(...)
    +is.parallel(...)
     
    -add_pool_portfolio(...)
    +add_pool_portfolio(...)
     
    -connected_matrix(...)
    +connected_matrix(...)
     
    -feature_representation(...)
    +feature_representation(...)
     
    -replacement_cost(...)
    +replacement_cost(...)
     
    -rarity_weighted_richness(...)
    +rarity_weighted_richness(...)
     
    -ferrier_score(...)
    - -

    Arguments

    - - - - - - -
    ...

    not used.

    - -

    Details

    +ferrier_score(...)
    +
    +

    Arguments

    +
    ...
    +

    not used.

    +
    +
    +

    Details

    The following functions have been deprecated:

    -
    +
    add_connected_constraints()
    +

    renamed +as the add_contiguity_constraints() function.

    + -
    add_connected_constraints()

    renamed -as the add_contiguity_constraints() function.

    +
    add_corridor_constraints()
    +

    replaced by the +add_feature_contiguity_constraints() function.

    -
    add_corridor_constraints()

    replaced by the -add_feature_contiguity_constraints() function.

    -
    set_number_of_threads()

    no longer needed used with the +

    set_number_of_threads()
    +

    no longer needed used with the implementation of superior data extraction.

    -
    get_number_of_threads()

    no longer needed used with the + +

    get_number_of_threads()
    +

    no longer needed used with the implementation of superior data extraction.

    -
    is.parallel()

    no longer needed used with the + +

    is.parallel()
    +

    no longer needed used with the implementation of superior data extraction.

    -
    add_pool_portfolio()

    replaced by the -add_extra_portfolio() and add_top_portfolio().

    -
    connected_matrix()

    renamed as -the adjacency_matrix() function.

    +
    add_pool_portfolio()
    +

    replaced by the +add_extra_portfolio() and add_top_portfolio().

    + + +
    connected_matrix()
    +

    renamed as +the adjacency_matrix() function.

    -
    feature_representation()

    replaced by -the eval_feature_representation_summary() function for consistency with + +

    feature_representation()
    +

    replaced by +the eval_feature_representation_summary() function for consistency with other functions.

    -
    replacement_cost()

    renamed as -the eval_replacement_importance() function for consistency with + +

    replacement_cost()
    +

    renamed as +the eval_replacement_importance() function for consistency with other functions for evaluating solutions.

    -
    rarity_weighted_richness()

    renamed as -the eval_rare_richness_importance() function for consistency with + +

    rarity_weighted_richness()
    +

    renamed as +the eval_rare_richness_importance() function for consistency with other functions for evaluating solutions.

    -
    ferrier_score()

    renamed as -the eval_ferrier_importance() function for consistency with + +

    ferrier_score()
    +

    renamed as +the eval_ferrier_importance() function for consistency with other functions for evaluating solutions.

    -
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/prioritizr.html b/docs/reference/prioritizr.html index 58ed38bc1..1f6c9fedf 100644 --- a/docs/reference/prioritizr.html +++ b/docs/reference/prioritizr.html @@ -1,61 +1,5 @@ - - - - - - - -prioritizr: Systematic Conservation Prioritization in R — prioritizr • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -prioritizr: Systematic Conservation Prioritization in R — prioritizr • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -218,49 +135,60 @@

    prioritizr: Systematic Conservation Prioritization in R

    conservation planning program (Ball et al. 2009), and find much cheaper solutions in a much shorter period of time than Marxan (Beyer et al. 2016). See the -online code repository +online code repository for more information.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    This package contains several vignettes that are designed to showcase its functionality. To view them, please use the code vignette("name", package = "prioritizr") where "name" is the -name of the desired vignette (e.g. "gurobi_installation").

    -
    - -
    prioritizr

    Background information on systematic conservation +name of the desired vignette (e.g., "gurobi_installation").

    +
    prioritizr
    +

    Background information on systematic conservation planning and a comprehensive overview of the package and its usage.

    -
    tasmania

    Tutorial using Tasmania, Australia + +

    tasmania
    +

    Tutorial using Tasmania, Australia as a case-study. This tutorial uses vector-based planning unit data and is written for individuals familiar with the Marxan decision support tool.

    -
    saltspring

    Tutorial using Salt Spring Island, Canada as a + +

    saltspring
    +

    Tutorial using Salt Spring Island, Canada as a case-study. This tutorial uses raster-based planning unit data.

    -
    zones

    Tutorial on using multiple management actions or zones + +

    zones
    +

    Tutorial on using multiple management actions or zones to create detailed prioritizations.

    -
    gurobi_installation

    Instructions for installing and setting up + +

    gurobi_installation
    +

    Instructions for installing and setting up the Gurobi optimization software for use with the package.

    -
    solver_benchmark

    Reports run times for solving + +

    solver_benchmark
    +

    Reports run times for solving conservation planning problems of varying size and complexity using different solvers.

    -
    publication_record

    List of publications that have cited the -package.

    +
    publication_record
    +

    List of publications that have cited the +package.

    -
    -

    References

    +
    +
    +

    References

    Ball IR, Possingham HP, and Watts M (2009) Marxan and relatives: Software for spatial conservation prioritisation in Spatial conservation prioritisation: Quantitative methods and computational tools. Eds Moilanen @@ -274,31 +202,27 @@

    R

    Rodrigues AS, Cerdeira OJ, and Gaston KJ (2000) Flexibility, efficiency, and accountability: adapting reserve selection algorithms to more complex conservation problems. Ecography, 23: 565--574.

    +

    +
    - - - - - - - - - - - + diff --git a/docs/reference/problem-1.png b/docs/reference/problem-1.png index 1f856f522fd88cc2ecac6350333803e5c9b31256..ee39cced4b73ea37ed118db03480fa459cd3bd7a 100644 GIT binary patch literal 47408 zcmdqI2UJt*);1bIMbW^=xEp(H@) zHIOJu=usiGKthj(5+H^^Lf~Jx&pF@!-SeIMm2t;^@3?CW$V%3F-@NOcb3XH#B~NY} z8-Rtxg+L$>_`2a$GZ1KJC>pp|6YYQFE8lOAj<+$bwv^|8^PZHK{^t0S`HpLE<4*^k+5PnD?-vj9AIp~9e;MY< zA&jI}5qlk0yGt~JRyByUsR|kIh30`U48tira3zNr!45g}Yjyv{f08l^C1p9 zAIRDJrPgSAQ!S^W*|#^~ipukl<5XDMcT7a^hx?F z`k(YVG{%qD(7tL(RT^fvnG>1|!pv%-JhrqfaeB-VTHy${ZZ4aa3x;4rsqc6)#ZIQk z&zf96nz+yOPr62G2^v*rVUp?Yq()dKH?)u&tjIh`KG16Cyiu>Q@AoS0=80JcYfAa+M9SWh`;bNYQMwM?V zAzVW9aUaYI>73+d{S3>_bfZ4g{AR`q%93QFU?am+y6RB-ISI{Z?;I`*EHy=9axETRT$+Wz?9QzCra> z<2lJD`T$9k0loK)2W=6!T2{j(lyj zCD3S|z03`br1fTC(Q1UvDyd4D?5T_w^v_z@T@veE8su^(8eD6T?GiMv7hYP+)!+xs zzaNeydnBiqq%1>cwO6lJ=Q%B!YkOJuY}xT!AZ8M3sul7jjvr$kAu+Wr?{iF6KK~yjXpv%oX(2!S)=y_ zwnA|-{YopVT^_wv{Y$LMAKm717>usfSeOTnS^|S;5Nt_Rp0(8AxIyQ0?A(s}cllE- zqe^CzCsoLUbnL)t8N;fKWZWwNDo-Dv*EJWlB5S!;f}XV|$u>C)Dv{-!j~x}BR>Yj) z&DN~!sIY^JB0WA$tnLa%&PecbcWrpDBsZUqon;s-P9sa`*8u&?*qtm}>V?!Gbg?fcH;nfbHZtKzOzw3rgQSnt^~~1|3>GN78rdv0$0WnS zEg`3-&Un`5^{3$cO9l~+ME$|KS)7%YjtB_vl(|snNH*eXn`94rhTVuM?C}X77pG+? zd~zbCT*D^{VQIZS;otgehxzAKJbE>>j!fJHrm}FMtZoUPHz zL_`vcS;M+hcINW8s1EFeSguO<)Sq-rL2eYgpYzsZl~bSLF zo{4k5^-=Vg5v2`@f338ZJE$~1RQ^>8k&dPoOE)Es4yq@$wEMkjUCUwDjfu-!m zd);i7r#H6NDts1B>2Cyp?19n)vBPA$oDY^Ji|xY1$Ka@m7I^Dmy$9}|+MJkpOXttg zr1>GFZ7Xyoud1d%+NB%!+V4JC?L3b`&G&WJYshh=BBV;0Akchm4JW#ai7LBQN-h5o znboYDtoOZrlk!%4*I{jUdQG0)RQ-?0=}vL7_tf#Z42E~@8rB?u*}aA_Vo6YwEIV9& z*sa#PgI(gYq?|&PwM@E!IPB5#yV!M{709Mfa0alvx|s zrEEu=^Eq!-ld={KD5x4-Vy&qjqMKdgBpTcVc#6Z%`4uH`1%;Q^{Nb$bRSfm=Kn)3F z0aa#leB_Y9J6gkJMHI5_^w1N%*eb}g3}nJWjtbFJ>+4Mt1m?eCjA390?{yPJLC%Ug z)mewH$ed?xG@H+f_4?SOFKQzND$pZgE{yl)b5KWQPd^iS?=(O0RSs&t#e2T$Yd@dC z6w){S7qXDQ8{?}ex>XFGgr=dV+UQl++UPKHv0K!8mzFU_lyM%m7Q?B{bZfacn@!9f z-mFO-Oh=JT79ioClQ+c+B5f9y^7@rtO^AXaA>>5>oq3*ty0^!?Di;#y2ogg4(Q2I!Qw!=9#8gm8sYq+h6?*|9SDYv4* z<^DG~u|?yXzPgpJ!}64A6?tB#7k%$}S>VU~wTCwX znBB2j3uUBori7(S{*liDa!U=$ldQ)44ks%h#c%^ z4)@L(moezv44U1-Pi???-2t4}O6+Er9q*N%-$KJ(@A?LY2k*0D#?;0fOM>Vi%jBvd z{6g+!%)v`nH`sJ`K`zE}d~A^mEXD7KtN>^pReBiWH?Z`g>?UeXV)jC~Qyh-7>Sx!~ z$J|L0QM+HQ*S93bd`N;CJLPSL^_Q57w^*&_b_7^k_}$}4yw4$jX+XNcXQnf5q97I~ znBL6csBV5g@ZcnZ^cg((y@zx_T<8ne9>=sJ<66+O&3}X7jDfr#Hn` z=Y5UetSYb{K@uFJLCCH5OC$7vQeR*Cg^X(sQ zt6OCUk9ZYYh|R;b#xp7^y*m_#Yqo`}{^2YGF7RtthNO2oZJ$K%e2?F|eNz7VU?|`A z$>B79#qC%8gOR#HFmN)Sv4lYxBxHnS(S=D(=og|ZFsrdE4a7 zggQ9dB7NRrgU~!M1F?^0L9=8O!;TzdRMT@wg5ngm@j2CqQ!*G`jmu6XYbyvwSIf)^ ztfvAsml9V#;7oqG+Q{c$qV7MA*XZi%N@oCTx2f0)VKa=EZY2NM(+msw$QLcO=5s~# z(9kHv5;HQV4@wio0t?({oOKpg)fo8*D`s>)s+lt-x)NFL^79AOS`TuW0yZ&i$fvncHO0 zuJt}Fz!Iu@*^bm?Cxwm}Fnj{iD%jpId^HqRCUJ~bC?SN6a^g+yMws}WeiM2B9X9;J z!B9Pj8ASlNaz=K>viW{xgb`~1SW|sIo=a)-nsNiKq$j4;Ve;|eV}^)AYeiV`M4xmk zRjt?kAV@YaRdsv+|L-JYHokNF7Mt5b+s_XI?Y$8rw*BgSNq&ic`{eBDe<|MnmSz|T zL?|NERSz0%->c@a<8K20*V8t1hy!<$5+OFcJW{^sigDiAJPYKH-H4E!vkWMYA-W=c z!8TXLjB-w{YbIsIstOfZ)adgFOH$KC1&pjkcdRJL&WyQLY9rraQ&D!so3KaKC}MJ& zfYR|0!?-5`g2LHVSZu_)Q=LH4AugN2-r5aPuaN9y#4BO^P);?2iJ;T6+xmG#^4^i< zwV0HZUcVzToDMpEHv)P)#|~N6=>7<64@sZ^Bw&0uqR=uVw*(-v3|-sE>VUQ+O6i1( z?w6&8`F<(13fdc$obWt^Nmm2wJ&V%2>?G9^>xMjKDQ1n4Ru*wK*nl5Cl`#HT_UMPb zAi3;0@m+j5=WVIQXHYzn3R*1rIoq1_5KToZ0J{B{RE3W6Ic>4nFMzIt984gH--08r zTw3iy*Cjn?SI|f4IBTN#F-8q((30_eDNRgkS&DQMMO53EpYOj89`t7=i-8CyA-2D) z@_!`Hf0*mPmfk-pMa_0ljj#AI{^8-7rj(}2%9V^NqAnNSl(KE`a^kgO$EB>@x&Qhe5qIsD@Kn0=m z(7CFYDc)!d8t}z;A7L3qPG-p32~W>L;~)O{p(XO-lmT_dA?FLQ%3q%EKLFeRhV=e5 zV*lrp6m2_*=$xUgCT_?{_V`Z0kdxcU;e1Q~Z@^LX9)JhQGs2SU{=F+JD>LC^!nJL? zDW9wUp(Ma3pz%|}Ga*+q2K7oQ)&H+RqV|ohE&$4Re6c?)Hi@L%tJdh;QCt z#nW|2vIgzpfvgAgPWlF*SH-%Twz#p~2-S!-s2MW&op!X8`8FClkGxMbA9u_=i^%{~ z^IlfjQF#WcW95q>A}{x>wSK48{B(y~p>R}ONQw8_ddEs*%1T+rSwdrM;jPkSR%XI7 zbr-(q)>mNV|DiAZ&v*7;Bkup$Ld#{7{k*ILMQSV0G&H$yZSvki;~ol%DD(pkBG?BG z`tn}&2K0ML$BG=9miRnm)qTB)-Um2k4O_{Dn_rlB&rRh_4?N!5*Un=-Nd^i3v;b=?0~#j*KP#WF~`0c z7xsBKBDq-FPO2ml@CQK>BlTuTDnJ*u8eG!mqe{yFJAOiHMng*}Dwip#XvMP4d`^St zN-w&libJM{>$lsNQNGmYb+9c$N)#mIMVtx0cuK~qISDR1z2OXCg2*pWu8%!V7nCSK z4i(!&4u)bRmDbZ;vC5SXr{uywq5JhMYJ}NADfrlr!5h z-?+eCpS@_~-fC$8w!yz!$#dVfCg30ZpBDT-8TViQ8t~YU-gm5+paA;Sn|(IV5;?aU z!OlJ_W7dp#gl#f@S%=nDyG-dwow=Ua*McG&za$n511@O1W92<5O5zy9Ds@H-(i=|E zv!#@7a~!l@UsobKGvezlHGD2pWdAL|;K#FPW@ftBjZ)#hTZ~0&M4_*G=!YY`uWvoS zmbJpFL+VBv|A7Kc`-MUCpMC*#*sb>_dnFGfY@6Y0U|+A3WFWn}DRD+Co4?$$Vref* z0Yd>0GTow%J`0|@7D8{N>p*n5Z$c?NLrY&UJB&XKWxb&U zoz`~rLpZYag;ql&0Q7?vdD!J)xj||G>?WAeuy_F=<8pR9FH;k~q@$ac_>;=LWpA+= z)E8fW7Zj0}oJvoA>+w~8mjFmE?FyLf)!;UDP#aUUYE6ayQV*201J7mZR?HIM)=2~j z3e$M3qVZj0^>V@8HJjG{gY3K#Mn4VJ$3@WSjMF58e`{p#`r1kZ5{b<5%GU5=l=$q|K~}#KpX|EMACfG$q{n7mmdA5 z3S|#?hZT2(#}$bXdUnT38odkf$}IN=Ou$IpD@*tm9q_*3yp9?Y&A`x}C$DkAj8bEu zEiomYYel*-RgH;d3;8G5j?C@CS4`q#1ZZa!6`?JI3+he_R9uwxX0t}<(s(CqdU0!v z*Cg8HEexm&CQGUA#fuk&Y~<=NhCCMx0zm>!<77W#H+@;m@Uh*||3>OUO7vc14nGr7 z>y00;64xgutqX3xf2pOd7AhwUUvtdW1R^%o)R~S%l@F8f(xi)0Z1JSL!T8K3mcg@U z!SZ6xUB6sHQU1@m(c8I_Z-3`{lJtrdG5|sDMSEIPwE*%yXpy|oOKLSRY$^$Z!}VEf zqARG#=0+4}MA!sWaUjY9S&zyq23XwWkhEEILs%E!$^h<`8BubXK}HE$3hH+X3@QJE zr|U0$G%Yt|5*J5Ct8i-c!CF|g_X2X`L62UDe@NMOIOXr7vdvomU&W2TtHJ>*L6D=W z8HLWs+A8FX?ncz*oQ2-z4edr;;VKk6X^n=OBTItR zAv<%<=q1$DQOj%=Rk30fRc3PZA5OTa^>a5tBuyuTUIV^XPP4?Fzf3`+xE6k; zk^YacaaMX3fD{&i02340(ucxGA1kFNqlwl~+p&m8*bu9{B09ssP-}jX#M5tYD4Eb1 z%%`Wwp2&6JZMyR&WT!285v^LfN@mjdq+ijG(n2yCprW?t_D59)2tCcqJFAG^1eiMM z&mVFIof1NkT_h=eLv(B3`O&bIp<o4;?x|biO-)J+&0D|DZdgG{|E75b>UZ{NBz`Uqn9#A z4W$dQZE0?0M9S#~OpTbM{f-=4 z4z_H@HHJQ_mVh~E6$j|1zAt7eub>vo^#R84ix#~Wj%sCMQ-VyqYSuK8%q8j)3zJ{f z0j#{b`h#9Vwza2;^i(};<;Wm{5K1{`&?_*c_A4!c5>`#f-gw->G1hr&M~@`+7#MP$ zSrsT@3!L)h$+u``qa~an`c2oq`E7n0@5Qz>ufdHhF(@E+G>I#N4d|fB3C~w6Vu>>) z^cdM`N7H7sc;~dD#Ib;eFpfL#JupJ2447ot5b?Ks%|-CF8kDD6Z)mq4#+W{8g=@#D z&8cqk$dWgN;^3yN z9ht6MbFFDRfrsDIvHL&G%l%`v|1A4cD`;@-@4W#3K|1Ll%rcFE0^(`UV$?Hq7a)qC ziQaWZxxiLCqBL!IJ-fZEjb9D|`Knhn61rB7I+uIpPIaLrXgV_30AD~arhdF496?yt zNy!U3U-*lc-7e5Vb23fMsap5!z&mTj5?=-*STDbRi+wYnfp~aEzq6qhC2~vJtpQa* zWdg|2_OOE4b~;5M;qrDKD;fpF)-uYBMN+f)jiNIEUl_d|-BKPPlv`+#jM9ZVssIk5 zA+-cxg4V1uI#2cvGK96!hTfTNHdf(smT_b!@jd+|IO^~b^bTYsyodt?ftE5Q6o@N; z2B){<+}kXA$HS??xlBa@>A--H?b%EyU2aZHK(g>9yO9sC>;wBh@LAV|HySM5O^-Inx%#6>sBG#4y1j!75$Pgt}hodw~SX?IzvAx+R? zC{Q=X&&n@JV<`QLaCg72DT9^f*c`JlE){G&1pFsTkOXoa$}Vuj+! zCJu-GPBp2)oW)haAX{1ofA|?}7yh33)aHfVyOQ_g2N)vMgvz&BPf%0*gHp;_?%1@p zOt->~D%Ie_c;#=unox{!fjZlJMJVpkUxLB8>5%()s{w>|SWfU+OTD@e0cXHD*wT&9 zTQ4Q5XIS8zABp;ZXa}aQmRK-+zZYQ|iIN0|{*>AvX`mrw%t!C1sg%Yc{Ld*zl9#tT zFhNDHGCG-U4au9@aAT*|^6>nLEjPv#2+Cdu{@809q0qX!- z1(<)e3P^31Qf^IVd93&WHyU`>`msIsX8|Sn@vEw2k>=X0)rMU4D&o%zyG^o&SMjR7 z%aq~;YqOe+T2_HGY6z)7*!zOCy@xsV|EA?AB*yrfg@SK9Gr}$Ke zR9)sqES;^^VS{R^<_y!vNH5G65HsB^T1PMWR|BmS{@OsQiP!BN1(a!X&F89+U)Uun z>uB>iPm8)?tFB$-A9Bf;a7V^>)Q;g$d~&sV_ER<95RKWrp1FD0j!iD@E&*XaCj%`6 zGs;@-jol-wWqW zD`!J$%AouFA1FT%8%i?qo5~nlo8$h(xx@_&dzKn+pyWc_X)7ONzwW4mt=$__3I0~E zqYkM2GOSd0W?m9=#(WOqQ%?23_z{9R9d2I~cd5q!327ffSWa7FL{UD|iZvW7Jle@= zBF?-eJv=oR;`4ops#HaEgs5+N0fQ})z3SgGP!*cr4d#GaC!vX3vOF@MCX{}wbjy)- zD3xWeHu}Xo`2O5{zjKxBgF06&VA0>j1hVl!AfNPdZ{Yy(UOk#I50>|k$wJV&f$HhH z87mQ}%j@`liqonLvSqP=ew5n8xx~$(9|(ZtXjf+$vslkFX^b2@oWBRzZs+8WtXxMD z<=|1;f_0g}_I?-O_e!3_sW3MZN$K$VhmmHjMNbVZ6kn+@;VQ%^bYq`8F99g~k$wF) z3XmU1F0QR z*7CyO=^Yh?R(fAjK7Yyc^3$K_CI8)4VGXhdZ7t5z3znY5d1k-rDk3;5dBpKYdlFUu zrTF3_ZIjs>tg_50oL?$W4_ITZ3PCS4(X9-&);N44ZY#dF59o@>F7n{!XX!-VOHA2d2+;}2}hTuu~(yZy9~HvnD%hos7^{A+&vo}Poh z5+(nIAo#!ICjY;BuU2*SuktA1P_^rjj-xHcKl;mji{~`^I~>D5=|b+C$-x6H3-9|D z247L6ziPg#;P3h?Qw_3Vr7aF$Z_d{Y=^`kwkj>b*okQsAX3}n>+71a>50*YeBi>#?^ zoo}*$T94rxTf#h9bo!K5_n5U#EvJ%>rSnKxC_Na>x5a>!0WfbGD#QY3?pWJ8YL1RF zMS8&rKmq+RDJvwy9-c@4N=ml?Iu$TZsTrwv6_4(&8#M~ z=89S!&A^7RVAi^7Af_m{vS$<(u6FRq;60`{VE!s0P*y7m?mZoe;>=1_ug8Etf2|qW z@FtzB%?!6=eWb^#25#kZUICZTC>*q~2X{NCre`$EQqfk9eTAeqc449zdCkQ8P8y+( z-9?U&0oi+O=cVvZel(%P{3?l6=sb$+VhGy_3MQxRL=mMQ#{D?D)R?SVCK@X9e*!*%t>)|S>sSe`d!Woja zbyVf&QAMxQZ}PAobTMd!yWX+|>EdNnH^>l;6Pq&3a0p2*WT8n@b^er^Qj42X^buH} zBKwCFjnBsw@k{=A(-0b6jyA+tC#~CeN4jKDv*`+?s4>^YW|Yh;HyqF+GX(p*b(i_I zj9nb*{Hy_L%HwLRhI?IciEz3}>LS=L*rx>94%DI#5T{14>Nc&` ztS!>vK(=aBz#_G}hI$=W)AQ`1C0AnhUC!VznE9c1ssorZ%$11@TCD{?CLzWeIG#a^ z-05@E14|9vQwrd|bKP?-;Gk2)?K{UhH`%szV3`G&Z)tl=!wcyP_oVTf2SZ&V^lW*y zS7^cek};bF6Q@Mn>Y=SY$J;pGD?Q>Zvp@6nJJB2#eJde@ogJI?*e666W19@Ck7Wkm zhA?-6A+4bw4G~hK(!vzDsV2ZV#e+gtX9Wv6iW0N7O$+gvAUtKb)A`ZZ2;0Vs0INmhiV~V~nnnjA*?Nb| zm{t_3`htzECp|Vt{oO3Tq|{~wkWWxw#(Jl(BBAiHY;I&32d_}$R=3IvWj%51blRGQ zYIX6*bh=|Fr3YCTm|BxlH+5Tvdbus)WyrAu&fs6Mj9jj@^&qi=3MQdzp96{VR~l^I z!u3sTWYhZ7$UzHS)8kPS>nLqn3$EeuXz6@ZIp^WI=}jb4Oto%g*1ETWDu;&n9%DO_ z1Px%VR%~2-7~z#YY(Nl=t_`L1U_Ga`qg8+9U9v+dP~LY*ZgY7U`*0PmkkfsN8-WTN zI2=T^=OF7bJJxOCcez$JT6Mi=U82= z>ksXnD-560w}^9%v1)!+@3x-NX?4%-S(UNe*FLkw;3!aiu^8OQ{!FDG%|o zhUf!*$(BUF1=H#{ahUYR6>>YUSM9OKSL`VEI&T_%B!;(&CuT9;hxTB+?=npMWZ4Xf zMbm&~O)_<0DOa~Cah_2U8Ia?fhuOTs(yL41MoBCVR!}a0qU?~qbB8Vr)|u&4a1!CQ zP+OhS(!_ZuRzYmIRzz6N0tVfZ4}4HIL1P7!LNi*h9Ba~JiGs7N%5%9P$TQ8gP^D`E z{`Y<0=K=l%N)mxwNC@VY#!e0jNWcQQy|EY;hG)6Dj%^OR$eNDL+L=dHqAuE_w#EjY zC85hoH<7G4Fk~HhvGw!-fa3#7uJESKl17u((?msduXhG+m574+w#c6w2()A^khTW> z&Bp|9IfWCZYn}yvnH_|T{#+|2rK^qlu#w=$-PURcb?c@svPwx?_fGGp`mKzRC3JDg zi?4fdB!8nk2c7zfQ!R{Q%8(Mg-D;MSv)U0m#~q5*`#qv#x~9$=b2qmk6-tr$x!^c- zW6LMY*9KlR6X_gx6cqH!@2OWbH;lArRV$VsA&kNghd)8MGkI&mc|6c^>@E_S?NiSf1!CVp$;U**JSPuUaCiO)7PViOtt z;b!o-SWJXtLkEKBy(UEYEHOWLg7InqSNsvBn0t0BIF$#p1$qvqPH}Svzw|zn7-`7O zvZP%YEpmOUF#sd`ODX!+C(UWRZ***Rd;`&BY{nATom*+x-1qd+j!22b)9MWqw57(_ zyw*gZ-JHfq&6;OF2KR9s`e&I$WVwqmHZf9|4>Ufs`qOyvwn=er-xr)y7tM|iv7;Ms z9!vEY?JM^c!YbiHtl498`gs2OM$UuSk3fFnY~%afZ(?D@h&xLy?dwPDV-thYqB={jYv#G(T?n-0&KZ^9)StXlyZ z-x8~}y16Ea$n%{NZDae^KU;=cQ!B4iGQqN;5DC6Uj%87}U*IAVoY$ob13xd;J1xYh*TtP{Z>l>r#QCh_br0PN}11;)^Tb+r!_XEU4(jq zQ61}z9nLA)s&2Gom8&U^Mq*v2w<}@gUE6t1=a`5VPL{^1ea_2?H}qE7?2sDuHIbm) zD3!d7vx-1*38;|MQ&tLiMpNC;8cm&#aT0}z=)Us~nr0N_sji2zGmfgZP7pIEDTU6c z*ZI*vN(Ce-@Sl|Z@PVwyBpvZB_NOdsk-PP_^BKmdmLg5uGCATIeUsFq-#cbKhHkt$ zMg6&<2KLo;n;tb6UpyK>Yx83+NWfwvM%o(+jRP4)UCs@lWtz)}M40FvJ`g z1;566M$*vgZh0L>J;9Yq9c5c2kGd?DZpwtSrR0E;P1J^}{av$bmv&zJq@Z>-{$z0C z)3*IL&+hs7;OnO&MhW^+rnmYtZuA%Jes(UH*9Mz!-fJUzO*F0G;QQ-iV_fc35TP`P zp%X+@YMSS+hjzO?(%|M+h3GdkGl@Ea^{U-j3BIrmus`^RQ039_6Z7h4L8n}^#*+kB zjZ^ko-Niz>xxcMRW{5akYn=4hbtKmJ)P`zJP~@SZlh+h%L^-0)dsZ8fF}_0^g0Ho& z!S?tID;>xHVshZL}$qcdPg&v+00y<6fNu3 z6V5#Cz?(6G#{<#PqKMx0+sQ9abF2ncG~wdUe2z zS$-hCM|(y`Y8K2)3%Q;V(o~4mX~oLtuHd6$*i%Z1zO-utzAltIxht`;2x3?#`9gOV zLteyU{E(=qEjf)J^PA(Np2I7hl2uV@zeDiNsdeCi>*DF$eT%e|Lb>+RU90}x3x>V| zhW!h=1c&mn{`%1u^&y{w`m<%%UFbBSf@n7maFUPj_$$p=b5e8x7st!zs-(K%QLnfBT{u{e5Gd3 zvC{-sCt&iMgZB>rvrMWdxnCOssgsnsRgW5E)Mtf5GME#>4e$HaygrRN5c8vGN}lPH z0fOUAj~YbOd)NPn)9_n(-lRKVnSElAr5%3jF~Tmrw8E?K^e?HoIPT;hwdEWRR0=UI5}|3v`FJweD&@>mY&9MAJw`TjRa(? z%e*vASPfxS}OBa$o7FaA7|d8y3sfmSR;XXes^LY=}K>{>_gxq2Ud(K{9yA!UWw z1FhI_?HSe{X3J8V9I!<1Lf`T?_Rpu(M23zL=OwmH5sH{ydYd@>ARN0+W%g@JlRk_4$B7r44mRAeA^e0~P{0ctrtB?0Q0s7IY}BtpVRj{L7ZIeY?}>*D2iX~W2QD>geUUkH zpY!L3r|sb>W$j~WNqA|S4|V-+uss95&2nEpyD40xDTM@8Uw9v1&EN7=?s(~k>-b}P z+N6?mc8nz>TXSxT%)7IZZy-8DB4-Zcc1K7n!=#+gjLW`(I1C*=(;0d7zG>~jrW1ae zU$cx$ME12QOgyun^O5jrEXElgrsAQQ?=moleJa;mY?b$md{A%9&7Ygc?cghCJYF6-BB5LE#dxLi4SnjLEWr? zQBO~OFRT~~%c)aa}OM0czhk?qj zJ0bJ%NQom-%EBc#xo4bBKV>Z9p%w6+-sBDed&aH577G0TB97I)3#Qz!-i6t=-uQUK zGx?v}{`Mfib-UAtLjNClS5)b0a?$-<|3Hva+!AFFhZ$b4VW*`pXIt+cZn$&Ypn==P3b2fi1!Qcij}Qx=g$YJQ!d}zWLo}Kh8!7E?IVt!tljI%0KubK-ysK7w5&se;@yCUgx|> zs6-a?L0W@O1-lJ4v_Tb+7IeCixvl;^UjpZ^-m^DBq_$$av}eX7XPW^mDN<5g*kkUf zI?*#|xodB^hV+6w&NE5BT+Y0bA8Cq+gGD(0Y&mvGV;|Aw}W# zi8*i6W;2_0mS6`O_?8Zr!*d(%;1AiD)&ZHYaqX2Yvna^;lw?q(=$Wj_DIPgCE{ojzFx9{3evl-OlytN(5PexG}1Dg6jo<0nKwZB3<@%sd-@{_ z?uIrU{hoc?9+2~^eA}U|@7%D?@YCH|J?4~k7s?S6MED~F1JU_#B=sazs%}_BEF5sQ zQnQZZ@{DOEhKmUT`4Ov;yYhPVgf>-V&o(w}I9%-8iEgaB$!uTX>rU**NrYc&Z2aRx zcGL?4m`ur#_zSN5x?_OqsSnvEJ+dKMuM~O*n~ipKN(HId2!@PiC%3!xhC3Sxm^LVm zQ@)$>Yad9pe+WO(qSr$iYtmWFq zdi}G5u6-PpuRny<0;%lx$QxJp6czQW?s)Z=8O;Ul>lm0*6i5nc_nlJOJTa=9n)}>8 zB79VV02Q12cI#u!kI8qBrW!T6`ARa~oj+ET1sQ8b$~w!$jpi7(TrkK79P;Y5OJ?B$ z%{s(OS3ha}-45+!(vPQi4J{b9e4W#@`B3^B)Bk8wRqK$`(EfvuGYsS9Vsb_D}AU_=EBP=5%+&RUD9Ni+ohN zkoC!ITqSP)a?iR{T!l&ncv?s~`Ep{;juQVP(4wjNS94}cz6t;a;O86)GSaG(B2|Qc0bn}f^QXWpzOUqA(O2bp0$S_kMehF!q&y$y|17 zQXU_6{yA6L%4bzWaBx{>$|@fDVp>A1sn>Q}-W6U$bv;O(nXyB&>vq*!|K3#_S8u!O zq2EUcfA0lA=yXd=%lrMqYrm6;o&33n`DbpzDP_6t(Z7J@;2@$uH)N*KG0gxLGLFs| zJ$f-+yK*bdxz}?xg)CunBvtEZU5!sw*-a(d0cQJ{kd1UI_i(LgE_&Ycc@g;-VtP@? z;`6j_B9|7uZwZ$%dhbuaTh@ay$_;!l=!5w9&~#$d_iJ|O%@b#S3l?IS@Xs0~%soBS zb&4>}+RG3DY3e6<@AWx4Yk4i9Yg3pPGa)*4e(2QEzH)_lhswwEEVmVqTC2k6n_;$O z|2K*~^cX2dv9iW+qEgaFz(Wmuh|IgLOw8&Tdib5%uy1Hr#^s;ypEf;aXMLU)o$dLc z8$JC^2g}I#-OsHg$iu3;UU-5hkAIUjLs8{GX9#Dg1L@{0-NRY1%lm%H{yMo{SMo(h;{Jyna>gXqgS(j@+3s>5uL?m~OJ~~8_ zXp~aV+87wu*;Bu3&Ferik^hf&jQou4_>pn_R}^Q4{&*c?`e}UV6k5ypooYrBT>q&d z)(JoFYH}G+hW*3))P;{kANpO6U&I+|R%m*7R`{dPbMx}aMS&Cc_l|X{;gicXtX;g{ zQ%pE#GDi62KurQ!uIWE<`?N1}jH-kAcL`{@eg3`&-~3L`lO7ERF_Ll~8u2^fh048X z+P|$GQ)53K%4VEuOgZ_a*XB~N^=r8$zJUg=Yo|lB3`kHw%Q^Anu!e!E;fb$OSxkkC z5TA}Ga?ud8*{AXE3ivUGfpX4rLIK8~8gDT8US3U@i-9k-nEZS)xM?HCU|tPDl|6JU zV2OU6D`pWiP2^l|icOn6%{gd97&zJWUU1;3mdM=x_)74=2Q66-hijqGqbg;P;J`K0 z?)tao2;E!v+5V9EtD6FEw4_$?NmCN|IMJyQKC-Kf$4mVL^1it*o06dlt`57xjb?x2 zh2Q^VtefsCS7oY3&zW+7qc!v{K+l>9Zn zh}}J)JF;i!Nik!R|h{%1k+ew~s zNjr@vgiMOh&8RMC7H&beq4^**)@4IDG&Wl5z-IT8wDU18^Oo&Tp2Qn2e-~(qkJ96_e+n`b|Qoi@gzz?~&pd zr!<%W{$YYYjQ{(t`MvWy(h5U2V{gPPMDv!(85c9^^ad_Y1TpdhONQO_=Ia6GwHF9E z*3!?R)=wMrpWV$!M|9fv{|+gAwG^3Oh<*9HeZSK|AhMnJo|{wcXkiPPZU_Gr0F=i2jU6-&CgpX}+vShRUBpR+8#!VUFWz2FEO zpA(4kVRpmh8*Hm}j?#O)ANVb31s`JCBRVVEJ_anIJ+QZ4qFy1{ zp`3p_&gMf6ETw**`pF2NE+C;2y|tG{DmNSzJ9C=Wl-K$J&$Th|cl3>(z=vOCv_HCM ze7yBALN^M@ha3MngdRx}IOQt!4PGPA3znJ7B~*ogocI(zXqL~7?BPCe36*Ye&gyBt zG{v6R#D8`PZl&FNBVqM?3!IofK^6#=;D#7peRucoCg+9hj3LJ_hAtdV3RsBHK6+%o z%IvLi8^WltO*)JgudEYe?@tA-iQ&I0yD9Js1F4qSLyd1&Y2O7BPBx;~2}wkW_u%&_ zQXLZfFRm&Q$`AP)NfVJLua*fo?4NBs`~yOG$~09?;=@}eHGr%I$okq&NpM$or>y(66-MO;Yf!{gH2b2Sq4`Wl%d;QZ8~-_|T21T2&bL&d~WeNyM3&Nm}Y)PVi%9t1c> z@0AR^b`@*(k95S*X{CWXj{S+zM*EY-_SYq%gj8vz*DHm~jz*s=ecmr!OxR3=SKQeZ z@$`?t!*q3q@k&j)M~qHW ztyFNJyiuBg_UfM6mjU}9YxbH)By%&;k+GgR1iwC63Q2u=I_%N)bjY6MS1*mm*I)!YoFXOAU(qj=BrlVW2vSS@ZITb zhv0}m`TbOv3a*rOF=#8f2Sk@UXZ{pS8CXP)s);_otSR)}6u%J{)3CD}+Ej{vI>3CY zvn{!f;vJ#FLlBQq2|UHdOeU$~4{v!SlTA6JcnKu?Ymu(=<`%TBH<70|(V0U7_1Czz5^PCC`!iNd)imBp~;xfDakUmLc@Y zWUWa12fN>h4c~)36pDNMbnBY-<~ggS$mmfT86zMzy*z5|Dp4f$a%y=7e;fD`XY1$S z4?;5wK?w-)wNy6+{I#Lkuy9&v#2$a~KNg=FiWWb#{)F?w7Ysev=+mAa+nN0c`8IFP zK3I$f+Tt(Uv!ziQW8+D%uV(~j-CTWyedHiVyD}WVzidbFla7&n|BI@xj*F`MzGjA! z?hd891qK*FrCUG=X@(G~fe{#_2I-cr0Sp9DB&2)jh6hnXYG{=jKuYSpJn{W~-uoZ$ zVeXl8&)IwJwbnlOm)HtUp;FZ0Tpn}y(8sVl+Xzz;cF-AWu&IXi#4g|pCw}4U^-V@^ zu5eM6Ww(Nk?1g{DgRb$4g`$IXVu`UO>YPzYzLC0~kDgVLK;-vMO#ak2yj{F@LL04= zS&9=89PFu*+y;KV9mgA^jpyD;9dW2xhr+Cnl?QVN@aCHK*PIxYs?EO?BSViFB#9bc z{OF}-)+Uk3xf^us(?TBVD4NC3+$eR_xD@@hz;KM9yeEVy>z(kgF%0R?4U^{B8Pcsg zdm?j_PM%K?Wh+N`udGk}C&JqC<*Db*>E6*RU8xW&iQDZ~mvpuxEV3+sy3ZFEr#19! z;&}0k=0~jWma(EqSLs~$CShLIHOv_LOfO=UAJ0&(Q+a4%tz?(C{}5?O?1swLXj*og ztjECZ)Uquj890N7Ps4DNBuboxbnRyMXc1N1&q++bT1ENh^UI{NM(ycJx7=+S+cSB; zzDv?KikH+Cmv>PqQnOtCM~53vBf8$VYWdh@)mE~6Q^q~^rF7Y}5|v7R?Kg}{rzO=^ zd}L{Ze|>xmy_)v_0G~ROopsE_o{8Y6O(pl>hc|oXo4{pHp!in^8*orW;j*W-tT~Nz zfVaFGdj&0Sh<>a!hBaxqxPNUb*Emruc|gS{mf+z>kFqUeoaoja`c!+YXp=KlV$;`S`6k zD+~Yr3J}|n|Kq!Kedu;UDvpB9#+}#L)a#XuWEQG49jBsmD)Bbw(Qs)T0OtO~3fy04 z=ev=1K~*IHzU_Gj+{ut(tHb|yF$Io8z*{9${3!jGLSEhQYRNM~MsM=@h6L}RG~okg zgSTXrXpSGlehE&-A6k~kodZtBJRqrgNXVlCFPATgjqIu}c3dT2@-`bYGlXbm*hhXo z!_S1@+8o!lX8aqaaC4`oPhuJNHhm^vC-}S)3B_7{5A2_lZrROObR{I7$Ool^H$O}9 zp;Lt`UKBjr5FJDr)bL>6d_|C-Fm0p!?vlJk>HX3xH1#H(|Dh`MS3XE|<>kWw)tSgc zNV@SWk@?bPQHg`!6>dmdo39rCOkj-Goofh4|5wFm;rHqY3TRRJ6L_IU`}0uJY3QTJ zBzeZ|czNPuU-MU-IrkST4#qx~+WUN{(F=!VANh;#p*j_V!gJHAD~biIJmL;4=KL$WZ`MUSBEnz*A4D zoz)*EB4P?;{r97|BmZV}uAlT~9UG3$4zDg@Q4Pz9Jxp)h#Otd1IQ|Dz2(AEU;4bQ# zPQMdFIHy_OgZc6N@pg-bx8p_vsO(Jaq;b-4nMz@&qYljYG&&2k5@y-8@gzwalY*ha zBw@JSM^{i|CeB_F92@f}P7E<-V){&6E5}+eIPCQ_x*4)8pVVK z&WB_BwLA8i3MYbnj1Afsai;`ds$N_$5a9`c+wlx~;v&nxgWjSFqaNJ>1r!?@|+Fk>PbU~*QZY?0aQVN<5hld&wQCN)&c7uui2 zCAly~!-j0TPn9ZP!~9;O$Lmv(a<<{MSliTOWB z|A#%&x21RBWpi4!5WtjmY>zkiKIKeB@@7v(%c6JNy+2TGPqb z><5Kk@ozvkKiIjhuCNs2x4Vc^2@DQ73FZqfnM$)ycYA;Ms2{%39!DxZCRN9NhJC2I zQ@hC;QmH7u%nbhea!JBEx$DFs`FcRY8J9b(XW+f>3@Fjq)qXuUFQ~A<1R5?)v4$-!7xPv)aF9D2++i z6MAkSo4jpp@?5iNM~RaLR_H%dP{ghodc*tDXP^V~VL3p-qb8zEA;w z8b~^p^t}f{`Av`MposuMjjPctw_{6-fDGnOH$(cS?!L`-Qu5R_b3p0}hR1bH7xCi{ zY5R><-Z*oBl#*cVDPd{PiO;}F12foXc44YVYIeyHFzatbylsg z`;h&7giQlRzR1SSy--sWBAenc1b?ho0%PM(c|CRY+e(;+Egu7?y!9L~sljT0Xo^ZR$3 zW9UxnD3qUwzA;%n2@!R1>^uxggJc~IC!g$p6bpU5voiu>`WzR!!ES3n@{wnKDs$!i z8J8foy==Kep|4)Rj(o^R*#?5BJY1LKKFvh{U;j%BMPDX&ybmQj5$5TF4StWXnVWV2 z1m$vF#S6+4-QnyB)GP-g5bvggnf%&5r6jfac6KlW{N8YO2L_hZ7(@#@Z@^&ny zs~*!qdtx|U>?qV{0~~12Q6DI`cM^=TXkhXOiT5_eNxa?r;$;i#d!xYM-;uOpg}u+p zB-h>@mvd@$Ax9!Yz6f@o?_QaT8k?~KuzLkQF^r=rPYzi|(#Vp8g1P)us5Fs-x)>lY_1( z6fCNpa=~9IGg;fh_7~meNeClSLfw<=PO?W+FmI*EUle(p6Btc(bXLrb|K(@5V}?nO z&A|ByJfv{TsT_Vo6<*^vbMIzqj#`tjO|qAkS~sKR7ftN~6X$NqSF%{diRCc0x);P~ z(LDJ#YyAxa7J#1$t>n|DxV)x(IK%OE} zJ-0ov^Q>GwUtPC0H^q_pKIW1D+HdhpFN3L4y;a3>si9E_v_llKAte(y_wX;kYI1$K zY5=HXPpB(l_e#Nz!NYiaIqzF-NATqf#RD1J;z1Gn;wh+!e-g#PK~-RWsAnH(onc0% zeg5-M${%QHFO6`OGe}fx7+qL3)X^y;`nYLd6$ng|AthCmKt)sSYPnTPKfjkQ9YMi- z-|V+jJ7D1`qWZ-hauNFtdXI%qbj>yaRvJB=jN(B3EKSovzK9IE?^5R=g!tLNUAnI! z6HnI=K8o(tF`bBNe^cvvo+js9ujP?0t-;0y9maa6qoH^UK#%k8DVNLlW&-_!5fKjlRMq zz6Iqg9@vz>Lz~<{W(hCo5a= zbT+Vzz8+q+#Y`$_{N}b3luReJDUPacj6I&jl24&YpM$SnMWwkmS-irB-vSYTDbRgy z$D3hg=qC6)KIS&;@tb?y^-sYc;_k%WkR>Q1`cS%;*~t2SA8BmHu<)4bs^XfXiRm7A z(4kIn4s6`{sH4{rQ!Jw-k!W)xa+nL&152N3O;X2+^Ac`|N4VPl)d-Nby#G;NMTezW zFqU*KtM;iOW}bZGJ$U#7WYmsK8cOVr?lUaxr`>TV`b7p*25R?MI{ZZjr3fN~QbLJ_iEQxX3{xz@ z=d9*pq%J0m z>a>(C-dr-n)WJ8#6PZeCBt-+&Y*7Fa-2F|2@hCx^wp4UkJjRWbQ}QnH~DVT86X8~ z!dnTm1;3-<+{A~Hob!)us1PGb&%V4Vr`^(xBr)Ik^s!(%aOW9oyGuQR4MEgeiU{{| zB2dz#WV$kM8h7rJFjFgt+s!^NvFEeWC5+Ey&f@CgpN@X1u-arLZWm8f;_G7EfsJ7) zyVVt*;N-pvy_R4m%lt4NYW}iy#GXCk7gwWPfdr;s6MSeVz0UwWSR5l}KK1l#_AF{X z?2&%b4O+iC{zqNom`8xGfaD?V#Y|vunbNb+xlF+mo=Ugj?D)zzKZo>{72{hwH;&iL zu|Yl(mN=?q`ILTZKm9?|*C2H>_N=6XuMl^71cN=^s7(iqd}cZrRkF$5$9U`f7W8Rk0?hiV>wI@r#B6$3b>7uUV)&9u%wBM`sGPRL2 z5bwqBE^DK1S(2|CpRgfHU;VuI8J7L~&V;xHHo85nyFXu zP{rU17ZO#{@^0zE!7#x0j&6Bc(}pDBkCFep0LaXd#@t`oZvvy-N}EOC6z$wynhL#j ze1MQtoHh_39l+0$barV7lDA_sU;X^zUCT&WzZ1nI*=~F9nAC7NOjpMLlsd9mv1Un){xt1jqJRsS z$wxVNiKK5MkuJlZXFTtR^CR%CB6Ne)o$d!jH*9eusHS_Rs`4FYuZ*IL=0QE?CN1ws zpIixIPatNiXtXJPRrdwTgYI4V;fkgSzI$M}efQWS z&<4m0vTllB0xHx<$cgbtLZ9tlvhMf=EeMLBqPaP3W4B`^c^rm^cts}Vp2@&fyH^%L zswfGcB<8MYOcAs|DXRgdL@M%i<1;lR(y`}z(>kp0bT&rn7#!+9Oei9!!n$_?#R^QP z`f_utzy6m()Y`nwHchel^t^0z(U7nlkH{`R_Vk3TQeYBTcxaJXrjk^w4t{oM$V8k zfnlf`(0;?WX{zpw2dbNE@Ee(V&{hk@ibBE}ei{-PbZ7oB=eWGE!W!5qVN0QfS2MT` zuadgER*?)Bz@NFFAN;T~1#5zUQ{pNYLGHG3d7T@2Uo6DAjU^w4|7YTTOn5HU;@cr! zWBWtsOdosK{o+dj#;m~~U*3rYeh;}B#V|KVTEsF6THt?$hzkhPU@+rnZ)UbkT{gSx zU-qQo_P#)BplC}*^o;Gw=X*2eoQ}*?X-n@P0$5SEw^DTJM_`wwr8~z`V}r+s%pvx& zAtn6Zn1vw`o>1-_yv^)3mM2utcy|EXWQwd7$G`SoaOI6qfD-;tNs~@cCF#&szSO7-_;fcu3WG$=6n`TBGd{a2oK{Kcv`|KTNL=KrW7%*c{{9N=l$W2xUYL{xaV+1hEzA4Bh^7 znwZDF;XFZf>%kr^wv8wBPIUkew%{y zKz|#570l$wzV{mlm1W+@S4k14g@<+(iUEa1!{Ohjj(Dgn9-J*4JmMv8wn7BE=PLt) zNU7E2lL>oGz9OvyiDRYYR`uFq_u-UGBsYAyWYjC!-jF6%neq@M?;~Dc6`_jiAaK`@ zqNoBNyMt|bu=c%s1}QFpl+Fdf4;`dQZy@ulbNK(g`bM*zdT>rbGt?+yf*XO^UO@|H`~lfXsG;kWWXU z3LMSvQB(^p$ujQK9)?TBUHjO!_%aGd3w2v|CGsSvy(dtAv<(J>|Y5n z+G(tDk#dnqTXfJ3{WfSbW=crD5dao{lPYg~CGJY`j*btlNzu-)YWB*nympF^htMY1}uT^LQLZ9!-OzWUw05`evc#I5H86{2Rdow%qEJ zbjh??l7@&Mot~;}#+_t9HG<|5o{lE3z`tO3@s*Q>cJ9@9pEv#%hM>tM>~*yo8nP|s zM)uMf9UgOYH~zks>N?*}y%1dXW?BJq3^^jpQxo)!xI)hn*}BB^w4(KzT1;3rG^0r}KD? zJr5m(Lm0}K_Rd8Fm!x_nrJMnu4^SNWP{V~K@irHNh#TmT+)g`97=`Im6BVopRu*f! zsywChOAAY2=ilC)cD)FiN`^T*9$Y5pA%#1prC)&|1k;?^#0kXZ_{!S2 z0~W9+*VT(>lab^rpx!;xL3WxHH7oH}YS(Q_@R6b94%1zxt%#@g-3^DR1?pz%t8q*n zQ1c8t7Uw{Dpr~S%0lg5yjATP9I#`iZ(%}@w+xs*y)OK=k-R_1m&9EuDgm}{q28?|&5aGYg4gAF_0Gr!uW+Nl@Ettlq{)ub z!_Q>n8(G0&%~kjPRYg!_m57D9-Vzh|DJ zkWad>pITkqLK5^~OS{adHO!)?AOZ-aYJUXap2OK}sA_h^8E6+qaidX`=nuXOd$kuR zgi&T)2kX!`$5D0g_5DYy4e%`JfTNns>1G*B;TPiKSy@``Pl+EQKN8Q}URp+#Ftahn zi!^XRdMy1gctD&<(wxQfvaf)p!%)NsWyOTP0l0Yxo%?-EF%Uz%0=T&qp2QNRtQvrh z^d)!X@5*CuV(MvDnqXNEg75y89~Zz=!aH^l7;LO5sSB6?0JEot6HKa~nRhp&;sR9q z3^0hFiN1cr1R9zNLw{^vNNGbgbcJw453AG1Usi|v2V&K>TH%Ij#{BF`WaiXTwo6jc42#=Vmt3X|_ z=m=z1l0)WT(vHmJ3S=$q{J{So1*{3>{?fU5kN7F%*6yin&w5b)lpakAKd`$~=R}so z3S#$RhqVE-t zDz?ZbypE7SBy6jbuo-s=IQlS3QI%XOwj{&c2G%~mAWxP_9H4<2kb9-=A;{0{1oq{Fi{r|S3mab_MN|*{)K-D%I{5$Hy14IZD?Qvd6)HdRQ zyLkIz;Ob){o-TKZ>e7!13J*|ox&3-KcPXwA;(Em~T08*s0Ot6*(dwACl?y)U;NJDQ z=tCGAT~aG2;8*r~TRKw3nn!j%#pN#qck8T@hpaMwamT)nfW2KmkBV;mT8m0N0c2@i zOi4lY+x6Vvz5$6=Y-SS9tde8i2Hrt8ZGi>!e^Ol##W{CJgDt)cN3{e;!8Quun zxs5$1SQ*7Mgz3RR-u#ufoQGj*3>8BGtmaGJ2FX&X!nba9b#WqIqcwR7 znLJ~)&56(k)Q`Eu${kvv|>U(b&BM6qye2h@iN9Eg{^fjsY(Y*tL(x}Sr;McFdl4Fln*wq%Fr z`GW80hDj|r)8i@>Lz|MUbX6Og`Ag_}8h9~Gn(6NVALOO$;B zs@qiSya57y#^7+&NY(Opw<4uJvPwRl0s=~)WFTxM0fgnE{sKbsUeZ?lF#WIr^NpP% z`o_rL3V!TIy>YFrm5ps`K~fVB5eo0l>3`%t~2)g!@ZJ+EZp3bzr(D%+jo|N z0}TUabc-_s2WQ1wc6Ws;G?DGGB~X-0r3m>X--Y~-L;o%@-NrPJs7Hs+50{->tQIT0 zn{jP{tAg^@oq>zCWw>(b?D(aln9A5nZ48hDhW`Tl$pY z;xIG{K?_PwyL!}?X5K5ocHu!R?w}A}$sHO3j*^NhrBso+)3R0~=29JJjoLoYyK&=$ zr2h5{iv+hZ^)+wX?p_jJ;V}OQ(}VT6C^lr9E6JFT!EBHWHx+H)>L*36o`se>i!bim zd}v|id|UUUC6~28C&Q(n(`Mx()bXB22QsAu`hK1w@X@N^-|T%r?>;X`wckrSav)u* zVPNe>xf7ogJ4FjcL!IYWybbA5*R=NEe=ZG0wopS!2E6_es;GxHl&ctk|11wER=CL z`P&-Ni{q0e@0^S`cq$CPl@ajCQj9zGx*_s&9ob3DX$yW3XY&wTT7D0@a>4`4{=sK> zjRuPpnEO7aSBk-j>h#oC3TDaU!vgy&l`_!iCp=X~qAsE;0$r2Npx2kg!O+#@d6eY~KtID?a|CK`91}>J4Qn(DEF%-_hLl5uOycU@S zvw=C@FNqAErjHMc?USpk{YUH4oGxe$p2QVAoX9|({&wc%p_34!=Igql2qWzLhGFjw zY7cs>$Rp??%l1ISV?NdB2U}d7Jvp3hW{c+k*`wL zk*NI*xTwqwli<~5RZfs5p&{tFGUhD*_!;x4CjmKOQc;x6A?HUdKGyz2V@h7mQhFdG zhlM&JoLqKGUFaRbT_MT%o`*OBxIhLpVe`lts8QZLq4>u&O5@rZg#hbjgRQ98)#jJW zEr=mR4SC`%T{n&l(v>18-eZ-)3*Zn+inwOyK&*q#X}Y~t&t8r8OSC<+szuL3J9->` zS(&BCD;YQ#cZ;EA;06(IZZ#0iwf>9iTAMPdv{8VVyaReJa9+Ayx)dPHOr!9W0ACOW z#jQlNR(lM&>dj z&|5g);c1Dm<1cz2lBDex6~_vP{FZ`kvHdaLIq+e%mdpZ`xV$4dR3D!cd?%w z4BwFCVK-WhpMZ`cwGL(p|Lzh>1=V%tvgn{riAw*we7`C z5LGd{OGkRE)0{u}O8(kX9((sHeFpY^HQ{-v44R8z4lV6Z0-BX6S4W$ye-x|anPirt zVx}72Pj4lu6q&rc&xunWACUxF8zKNvG4+fXZvMiX*v`6U?A}&+gDrN~u}dzbQnvHW z@17=P$-Vrv@!mnyr>awRUw6MAMHmyfq8oagX-@1lw+n@|gx+?xk|+y5PRsTVv=^|i zpj{09BP*%ktXDNmbeR||ME&m0Yv+y5e59keeZ*n_1%3ihy=e3yeh~;On*Ei0--MBChm$ohXlTa!> z(Qi9PnB5_T?hw5s#n2>_3^Q?eTjxa>;_t>%yYc3NgUC#C*O#L)v6vN(-gyED9TG8e za1aJ_z2liuRK9=1MyM^6h7@l3?pQN74K;B?^Q?fLYoP$+8^L39oIp(#mE6L7YxE(> z6gUucR&;FUpGQYpcRcZ2F}jSCo3}1+DWx%$_mjAdDn#qmcw}oPz8CivmP3e=pFKcn z+V1oK6M{LU;L%%|=k+Z%{V^w!(`Ac__&G@fCPQmN-j=q7Wh8YDZ%X17SCj1l%04lI{O!b<)EWB0(}4V_H!~C` zj`4D7H#CpyctFk*e_r_6a_g~hYu~XCGyCTTN;gboYo$pbZ-#;gehJ#GkZ;!!E_{Kz z=1g_}dhk>QFZGA_l)XZQBR@W-Ed{pAq+nB#uLF2wduGEtzQ;7n;Cjiod+|zYXv|XI ze8z}q1D|@~k}UUqa&&A~u6LND|tS^qMg`=(o8x50gx zuk}ln5y=PKtn}*qldeazP-vk*HMKe8JNYio(#Ax#z<^mM!XZZg!!Mt<8}sWzJ>Gue zRd)(A`|`_+JX~MAT8fre&xy`}bE!3*SNmhy;0!n6U+c{2S~G;d7nS`)duKbT9ja&Sl$^J@S4bPS%lZ4rVm8NL)$ zF*S&Jh|T2pCa@M;ljtj?7fs71S}w`YZ{j{_ z?R{7?Y);D+A;{94?xE5VIh5#SNEDe}KUT%8G4&s}|WO z=>456d42y%t3lX!YfRd$R#d}=cndFxt|4DC(_AaNZRsu+qNpV3qyXo|F=Fd6Ca&mN zR0S<1`V`Stu4}CP_W3->RrWW1r83WkR`lZxg4?E_+tMrRt#~w@W&;%+hI=ADiY0&D zew@nd>wE)p=b|)$O7jopyb~ttOW}CLc~Pr!mO^Aia2N&R_ezE6l86XYCVn%ggC$)- zO4CXZ5u|v$gOhPGz$+J6bBf@(J3#!{Xx-zPO;KQk5&Ph&sEw<3rx5Qs>5rP46QT9VoSOlZ zSGShtpIs_7C9kz)kjdEIF8h)BSAIC1j`>{1?M`!S2Cy4>4+|U53nI)Wf)*y9Uk>91 z!WoYztrg>)64oBrdFD;r6(vq`p_E~(%N}{h+d}!FII@2|8B_2R+L2oHErHPHIvN;< z=8hz$2N2@gYm|i#vrN}EDXPu-S`?>q9gXNoTNe#OmvgjZ;odcyZ+i(L@q`$KpUJ$X zhQOwK=t!bj+p@p+Be!qUhn`K=wY6b{M%nw z+p&Pzkk@n3rKKdQE9oxANYk#T!SC>eq%da9%4bw1IE31%*wK>E30C7Z)HghzKPiZx%#jBG7T9&AJ2(bcOTnnN z5~r*y_WjX97g5bZZ$YOBnQkg?$L*tAOJtZ!3CGNDOg171%Cu409RZ>){2REt#SMMc zjJl9Wkg`?|$)mlfj1$p`MvfB#L_>@Xlm6n+bJ4Xf-WDoz#5=OTGQuJxq{A(R4y4v` zWY6baw1s1i%6|$S)~n!E{PGoej_tx^@pQA`&r!Owa51uzyVLR+2lM%DxO@IWUi@rf zXb`lCemX~ZUgtJ`@SGhf^bE-x2TcF?h$AW>i1h@?<|f9!bZ6KzMU`_zgazw@S0WR28z$C35P%1Yhm>G{Fw4Q4Hxpb zRIeFl-o?nrPaW4gZ#1f)uiCj>&gu1FIe_&hm-1amT)o;R_vZJA?TT;X>zgIsg|80B z+x)|s4uJI&EKKj;PTcZwM7Gal{VIDjc6iCs*OI;Kqu`#d-AEm`0Q(@Me4W{5M3k|1 zrXt80e#=R`_+st?Uf?WZu)Zj^uVakh>=5ZWmarqU`}jM=h6!(%U?G2DY?Bvz8=Lp@ zBIUg?E5|FNy+Bq8t|fB2DaNWvr=ENZGFKbCQ1FR!SCL|K1j}$ycM7kBUcAv5Ynl0P zoBM+6^2XqDdlW4DTP0qb0$c*Fvdum-KBpSNBQB@A3oMJ$f}cUO**QG-NgyZ%tH#di zKAeNjoZ6;P1FOgZS-~>3xcl>4I?#y#g{5U53(EZ`tKfAw#9-={HbnqIMUg}9kTnJ* zHmkq!*sg+!GYd)Qg77$2JbEEreaed)DE?G@a1%rIvEY;iagyxmrw80n2i(GOVra+I zNqM9~Ln+#T3R16)#b}~}QJ=z;!rV|2c*(0Vd3c)OT|7Vjt(JBc2vq|_bMCyj3jfo7 z`y>&nS|KN^9KQNAR;J%%j!+BolB9*WDC9NQe_jBHk{PLW&dK}f zufzypLE=~P-~{13IEsF$i^r8zaPCT_rLqyru%WPtuy+bDU1RZWs}s%Z2vhUNJRUxR zx``hRPP^f<$xa@0vw^_8q-)v>-;;;F+fRFCO9K+VwTt26=(WDt0ERFj=_-XJS&%aJ zNmFD`{Qtbjrd8HO%Qy+_k?#gCjgN2XZxqhN(JJ?x*)+UF9P4e|P1Tx`)INdASWZgR z@ob?_pp`f6EoU+-50(}0FSVvEj^Hij@|w{V&m&2eI=jyBZ3#WK3e$=Ts{TcDKix2v zxuPoB@MjLqb;KceSC(;qFv(Ba1vBxgZ)m#6?HhE`bbee&alc!}?at@WbNAcDIsYE? zOg&+$KajikxdK(CW!l=x4Z_PTd2?m0Y`GO2rOg=x`;e6>+E#m7Te>NHI+Z^1pKS3< zB@kamx<(0%^vJ7f*WV<2nutI8L}n)}f+yTEK*j!7)FZdsUcBqf&BG!&qeZ{RJI0s& z1Qz0)rQ>jCaRRR2H?F`oxm<(tu3pMm3KGrTy3aC8Pkgn1k^F}*7p664C#UW(*D>OL zC3qZ1*A?h-=T zymRc3m&S_$SA3<|EUACX-7_oQnpnFL;-f#;!cSfP@_aF6nW5tzrv25=NY3Y_pSM3T zNv%ix_LKY@Dk;g=jvuYdeP19H9h~+9;q2I|n!jzBuWKe}OBVJ(Ny*idXZ6>_gBcTj z-DaN%cq;va!;OQqM?7pYUOX;qpX$h+ZLy(r*PIg}GV4Ae-+kX?`)1gh#-WXJ`o-44 zkZfkzUn+L*`tsGS_vE5Hy8$%adccECA>ZaCZky7Wqg}dhYJnz7pN$C?pNZ=`Ko!=| z$R~lyli0nbbQ?3sSl$fRroe3J6c*w}ow!IslUb|heji+D^a{2|)&23k1GkRyg*;pDLeUW>kZ4UrUxKS#{b!V} zIT+dj&OF$@VI(qZ(_Eq@XTqWHJcsR7on02H+l4b4BW}W#adaU=jnKJG zO6}C5wmpPhXy;dx+8tsWDy!*xbMG*2VHOmbg$59w4{02I8+64(AN-px^CM)+%C~NL zhA>GJ1j+iNm-biSpr9B9Oj;w1SY=IVjz!26riunZnS{P8puc@ahDhT=evN$#WT+&A z+Y(m<6Wc$nd#-7uqCai4A4|EcV|=@6{l?$;j$zD;#Kd!3Ul2t>oz&Kv8PwBQf`Wxf z;0J;vK$qGOpL&~njT~9{vyyWgYY{SaHgej(M5XEu**X6Oivo~`*+=rU0b`Y`xsql~&eA>wV$&=|~LH`P@BpK)r(xr(>jzq})e@a>faZkI(Hh?pc}Qn#5A zEpVdsBD~D|gBY1aP{HvKk8L8#lz@p!gPeQABSa|UBDye2rh{xj3t#n^$e5T;;hvaTD^nCM0UYA+e~7F}K& z{y84GA?*AaJrk4wsskCky@U=Ah1r@o3zN;H%$$3F^D|D38&deEO!$=~UEN6EkY1YL zgc=DO(K6TR4C42`DI?@de`>`^qzrUazMV<9$8YQa4i~kPI^GK|h&xeU&hEJYn#hFo zT)6S|9Aj#kvGy8>n~Uo?HvUGbae_a6iHpFeitr~v<4w{S))6A{W zMd{y|y;uEKNrR@YYb|2CGKYes(7M_KWQXXK^S-$h^U`^Th?2{l4nPQn99@d(Icl*K z%vkS&wJ;hBWZ99;BrHZd-JA)TAO)pO;9uSH0RxINA*oj;6Bkq`c&sh+a6N)>cEQSZ6$?pF8PSzUUV4SJx z=xH?SW7tipq-urMPc1+?vl+vi(1M_(>7Hhe;fGM7N7*mOy{}euvPWO@>b)D@4BHbC z6+=kiWCnxMs%1D{N^L>-KPMcg;GFGdbKmLSV1k`-rkxn|Y0Bvu-vme-C;43XLr0Nz zLVnO04Q-tUtsVO(i6l5HTyp7Qv8mZhYZ=v+(ujCzG;vU|`t06qmFG;@42(J;1I#|I z;n0M|cL=prZRj!eT{j^fq~rDwOPmL6ndZ^;yxvrF$58)KJ)Q{gY;X z9Q%k$RxD;0b&6>dTTax5iMfsMJhlUltLhwI9T9zXO4FOl>*^~T-X(M(+c~5C398mf zoIR4E->-XU+&%R9))VV|2LLpeM`H*8@{w z;FV^E7xBb3AAJ<3d01@QGX{+^;(@#?*=kJ-P$|t8hb3jTxnJ<2N}P~r$Zn&wSeH!7 zvRFk^;-OM0{n{=c{WpM@iUP_E;!|NK15el|eW3#$=SsTjQ^-ffT{07Xv~~`_u4zw$ z8`4>V(r1E7?VDY<$j&1qYt0z(d57&N{5AwB*VsAx=Ss|r5BRas5v8l2ne}th0+Q}1 z#K|KllP5iVA#}`r$te8qKSq+$Pw^)pxsB(q5)XMxB`AJ3;a`Wgq8EWE!&-|u4%79w zdK+(30>6HzE&)v_3W^A%pK{AU%6GoFZr$y4a+KIHosGRGzVTyNeG*b!&4e@g%;f)Z zyX#gKPI=PYb%36TWr)hPqQSC?h-5=19AC-YEcH*^zi7?tEy;vrzE-yQuSXHC=+8MF zVZ7)5qsl=^Zv#Bow)<#{-zbtm!X)3_J(77?vgb*gM66iYP&xcl&t;0CAaBr|HUw}w zd6xLup{jk#77JkJ(eN7Yqs7y~qve3MrZ3f-IH^=C!tbC#E6lk2WUt~TE$QHudbX(t z@v@#_4s~%!9wnNz1!S6QVv+j1^IuR^5sYdFP2rW$m6PF!0n>)t6zBI&ddl9TKb`0a* zxqFd?3C5%#gL$;?IR8txPJ~4`?C5Taex$Ei_;S0miCrycoBPm(X?<^3?0XAcL+T$U zm4&1SKRiN=s>fT>f_mDXo*|w+r{C2h5=1;LYIv~T{-)=tqcDs_^Io51KVC@-?n%rF z3Db=vdsfXVqj))6(CC}X@d+;iPcAF+X6xCa&B@uND)FV|Z&wrdthD2L{{x=BVG6fz z((z;bncLp)3=Xy^c-BM820rVDdDWYTzJz~uu5NV9Yt=X-g3zM^wm|N*Qu`p^joL(4 z`m;1I#n{KuZE$)&THje_N0hv*MVkb!gHcNE7JKmQ=y)A|#CGYohvbE*+@FfPt_=Ut z!W$~LCR&QwKvy#Wy=>O=>%p&Y`kDHKlS9-nL@q~wmFFMUe5CJ)hp5}0Xhi}$LS6g! z;vzq^dlGn!ijrC)`u0VT%1fz15N7B*^4mPugv)KM?Doh1_uLEp*8S%{bsUgrTQao| zzOiUI52n=Y_Wa8rT6(%{y$%4sZ|5RU_~hPxjcgb>V}%9UWO)BgJR1icsd_BcwjEM4 z;TQ1x67Y+J`%D1ucZyXEzJF)$%*q-1?$M>i>Y|*f$3K9de(_#J>`7r9{q9PH$es(QrG(@{Nzxd4!|dZi z<^xD3uXfLI)|`inF_=;9=SXGr?2e|A$Cx8mWlcV%(}SM}JSi*pzx+Ok;R-1vMrQ8| zUy1H}v3;!jhX9Y;Ww8tCMh=BF^Yn_~`_m*fC9Z{FPh<8t1r})jnt#9@6eUIW*D-a- zRMoJ6dwdmea3#G|!8IprTQ`1Wrk^>VQqLoMIpb)d6nyC$zA+?b^OkN&M(a%|<4d(& zH;?n_U`_|2<4Of88hNHj}-M7?8d8 zUTc1H&Tq{**Io`=AWE+9d(fuw{m>ZcXETcbE5|Wt^11p(sC;l_;$Z`pj7#MH74F;D z(r3KfbeBdI0;2ctKobdDlC-{72V>NsLLb`UizhPkq}1)As_JqO)*sHp@_Qn+JjV1g zTY1bgRM|+Er)7dfHd%sQ0BX(Fn^;>5#J6|9N}(QqbF0uh!i;&6AIjtQ#l0NP#_7fl zeyLzP%N`kTS6n>aA`|K^>()QksI=1jX>#gNS)Tj>fjP)D62@t5!jRl#T{3YvI1=a% zZg>M6oJmqiS4;o4GkdS2Ay_O}{L%L${d{oB#O#f>mG;nC6~UAQ{&`-7TgRKtQ$3pQ z>3zGW1xgPwvd~Vcpluu~lLXHS7M&4;lv>suS*?f_xnnnW@2Yqc%cg`{Y_NMhxW$HV z99#svEFww1geR=|4wDX>Fm4`OIN8Y~cQakKJK*tfPF0w$Lvwakw?fjCf}2N&dbd+Q z|MsoG-kPIDD>@UNI^j@-$s^t?iQ?)b8o5CbYOjF1h?h&xSoW*f;PX(dI^!e&k41!?|Nh>$rkd_^hXTKTrB+ynlM0RB3ff z*iA71`7;a6FD53HpV*_&n{~TlFa7@5o4WJq`Pg-CVt(& z1L0c|9}`79ECt^ce_|C}EdF#?xmqb5ly02KdYpCld`rzIcNf+APp4cAvz(0)`zDVL zM!Y^lY%()!IfPS?)4!ahC(=s=LcKiG7VeK=+J}dePLA@=#xvM!?)9CV7ID*aL!PWf z1*?W^UTB495)ZV$;bph>kl}PhEgkR*WWVp{P;Q<@F+K{ZRqG^vnxYrI^?4&C?*g3v zZtZb^6Ml8J>)?nScR@a${y5911#*1mcAw9!Pqj)+3H+1a4rkP<09ygAcar>x(y}UMe7R^Ni7>}J6 z@l!rsUMs|Gz%>EuT#=xjbxMYrnHoK;jqX}j)@c96z6qL4T-!g9s%qcJ7nhaI7!Fr8Ph>B$O~iwn zMcC%d?$u}ICBVxrcN7|%RhRBdt``bPT}K2p0-c0o2=B8z1z{IzYZqRob`_)m>5M5c z$CXWX+-*0JrK|0S*;!OAsIK504GTi`{%2OOJ+rArb(Kp=w*FCW%_{9r0Uf>);I2YUM6x60arzTJaf1d51r)&v7_QcTXixED@pJ5n zY5(XV^6s2}2HxeW8&3;q&66X2LX|BLoKm=^r0NK9H6B%ElLR-JY6ldhhn1?7%#V8W zj1Zmivt8K)2cgZQ!4V_H27+hj&b#?kns1JGZKHRcsHNl{C10sSLeYEU;CFt@BQfAb zOKK43&*6fAGt)@x*wMq$dq#zfh~ppW3y?t$;mwAplaw`?2{m%_UKdW)FE1i*vVU4C z&BX}y)pdFHIDTQ=Zf#5tIWC~$vCYt32r#Of7zpsq*+zyS8;$-jSebQK-FKGLiBJDR zXGyfBcK31{&yep%RqW6A!6cY2Me2Khe!WOT=X?7qbSUT83=K-joML)t|NMR&?9_rmrIl(n(E`mbpyVQgob-hQjgxhnzU<` z*hJzjb@U;Q5cWLql?=ZO32Prlm-)~*N9Oo*gBaGcRx70Xt)_JuIlkm6@vDlL+a91t zqF$IZIEbAJ>s97>=v1|_cVB@~wzHG`NX`x`^HCRG`O<e zDig)i&r#du)jQ;pS9?N<^Ow7D<-Z zoi-#L_&m_*FV43J$UX6jDOnlmAe8P6puJ*lPDkNQPkS^~*ctQ-q*%T7=|G3Bh*p=( z)t`20CT&YSUB}?-@kUwHr!S%cD)C(l#wfZa=~-bYBfKqx&#q<<(N5U1OWn>=bd9~a zm%#A)x`Q8#XG+!O_$RNhAC_kji&QtIQrLG_cXoz)k0GQLFzm@C#lFbEz#!jJH zesDr3$X#_ZFiw_dz0@xs^x(~uaG&YmTvgw;buM>gb>DKhE4>)vU)&SQnuoS`Y4kz7 z%Jzao0B)JW?mcvT?|LGkf;h@B(CpDd+waz*y*BL5G&}c(Z-5*NDr!}vrEg~3Y>jsH znjqkc<}1wy38gMg!fWQQeV@wDkG$~C4T}pJukS^i5#jh~ zO`pGwqJE-1t>>NXNzD@Ptu)&nzw}&BOa@+o8u7JNf;I28gG&x`{570MaTx!M=7v81 zNA1oI_&}?>q4={-LJ|4dGj~ffkJLu6UZEV25d&t_tasUNr2t0PMHTF(Y5A0F5V2l& z3E6go2W2+fZ5ipQ?x#C-&&!CCQ*rIM2U0628uV6f+DzrmR zt+HJ{H^s#5da~(qk_mafm{Omvo@Mg~;4)@a6n}m>VS5HHGnu%qOQt6GV(2}Ld2Y&{ z{oH#ae@Q5{Z0sF**ArK%3)sOG#9eO2>-BHw?FIphUWj%%c?*N=z(@vNgYDgF5r&tA zQ)^__x9Jyh=g4a%Y5Qz92z{TU6Vcmznus#T1%0UcIpb3m>?BKV`9S^2KM%ipxUm;) z+n6HfyEYiuXNCFpgj`z^reEJ@S;Kj1s5g4H7mDV}wM3oJ>RS1B2gOy}V3D^Qa1GX+ zzEhHgeVYhKP){K?O-p6|x*3bCzY_{>X5|muj`7C1R!<>)e@|@P*OB8bN>Up3G_GLF z7Ri2V*=Co92%UwRirnVcPOumtF=^NDv!-lkcAaEw+`d;veqS+EYR%YtZ5Gvw${MC( zPJi)-u0?b%H@~YlFFBtR-sRqAGw~VS0TozB1YOiF!>oU;vM56n(&WmbM|$o3G`A&_ z!rG0swgKdosD$!r-mH0v%{NzkAFQ|VecqL{Q8g1-;l6r67Bjv1+4CD?r9{BBVQ-lzdI}!4pGILlQ4WlEzeNBf zW%3GfTR4(tKf%;ncdZz`tqOp`)y_h9w``U+j$U-NttxdH$0(9r&3s(H`q&c3rL^CW zX-O{vHs?uzFxDzULoz~b=D_|!<@;SDUc4O1ci9jc>4*BQ6doVE*S2$0x9gr`S$c6y z*h;ZC{fw4B9Dq=W)4*aTt6lRglbYz!^3h-R-+E4HmODa3O(iPufU+xP18=(^ezmHU z_=@c;6v#;|9It?T;e!Nb<*sm;(9$(~FmqxKwcg(hIHBPdNKIra{&+=R1DdozaC!S~ zgknoUSDb1H$$h7XpC^2;cJ;4ZKIrOS@uU72iJ+?)kP^7m)Iq53x&8HHAB^t$o*$Qu zEB4*p4MN*DX-qbzDmvM`49)pg=gklTY@|}uS@7xP&ENLV7dYVYbrD6s6sy0#!E5;g32iTJ9~+TK29+1P=L`u{26Nz2pC@ zX%$cZI%qixQ}jG(GT=w{v)f&M%@AQ3LOUo1#lo-P>xJ?5E~;sM>()UoC7v4P+j5hM zZ@(>QnHG_p6=yxa8blnCcDi|FVH^GQIea_H%e)AksvlN)KF5J`a(_c^(_U)z4c8ne zg2MR*g8iIM%eu4oCOl&U1}v>)gZc(6`oK#sk}5)6e|c?i-N}#j`6c$ngZ)M6O#h@y zVX9G9bbqfyvdQz>X;k3vMi{Z}+pu`uOu@v#+{@%DU%5T=aXs+$ruJbj1tY4AZ*i}* z$ZHf);1Cv8U}4!yB1GY4VMG3kS!klagF>Xe*2yfjVynj8NQt^q@~u~_jX6|h|C2B? z+ngZH8`k~aao4L{n)@Ls#k6fHKK%hV;#OQiakwQSK=2f3=j{hNzRT_x`cknvrVaN7 zp+>%=UH3_CudiB+?hm6FrlzN$iXKUyU&F~IOla~b3|_?Ft}8~ zCXfO97|(IVPi1!wlvPAG9GoP#$CwZzSn602dTXi*SE*}`JRW@|z6K?iwqb*reEg`c zad42V+%6t+-=Tt z+sDQtZsf5hOFuigF5L32iDUDOvg=KGDwBL&0LWK*i&C7^v<<*zZ1%@y0p~bBHo17- zMk9_|9fniogzT>QjclOvbH6JepA?@fqgN5v8 zZ|l93?{9A&b@^n4`r4d;sya}ZKY$*(X~xn5|Qs)d<$l zK25MhxXQzI{E6()h~`^A)k<|Mr_Nxh;#KBiaLwx4?TnYJ)(-e=0CCzedH`j(`s|j8 zK?ndjQT1J530Np)d&U4IR1z2b3Z-GDf>J6k_q7P6Peoz#`%DGY$JN z5+*I%_(OL@6()u^=xv}LbW-A>>sLkUTX;kzqK*Mbg%W9_m+1~Z_6Ze9*c|o9@*^VmE^SiWXRW{033<}e{3w9~rMbeSe zf+qV;p$QU?svSgRoC4rn6Y9W$1^&1Ik)addQ3$($#lBJSaC~qZQI7)}$~e2kZfM9X zG&QLKZil<|@Sk3x|C0;+|KXR9JO%O>R475vV4`iV^2Ggf^zeJLf99q#%R!veI+>LkDn zU^-}ig0EKh46!ie8BQZdClP5$+ybW_IuLZ| za{Pv9OPaz47=AInR%7z@lj2x^+14CxtoUJ;Xd!9RktHf_X(*S#a;&5I<^xWk4&;(& zQ%>quVDxMQ>MJ;P;z_0BW#g+I{BsA&FU7I0U7Tw3(ej9^h-L0l!ZBcZ&a*;{{53C63V6AHXN~}b7xewE|77NoeuwV?;Ozq{sDI;c z|L+(spcw}r|IS>?nH)R%SCtLWh0WF9`Eej*{v1e!{&k(zc_1Y8mkRxV>G<**fEKVh zTgIh$>u(<_qeV3;)aNO6#x#Y3+Pi5}0Vt9}+LZ06w$-QZz1AApHnFFql!pg`7^|m; zd=As0Y#Ep1?H&T^ozNFqt1;WKl|{W`#g1};pF8$QaJSdk-h5x*0!$I88+7~xFZ@~L z(WLAfx76dD^5v6`GriGeMpGZBBddf~@Si=g-J^+-W z9H633k<8PkB%ZcM9J9iHI7PZzfB@!m7!aBIUr^SaxHRfAg`Clf#zouHc#UiZ` zP~?&XJz!79jh68{B7Yo`Cb`iew@51>wPPOohj+Q33XtHXh_}&-uQ;fBL)3zt{7DLs zG#LZ7;Qb-^t8D%3-+>vxZ>93&c+zA1BF?;VF;33JFLA5N#o&9x9xS{R%RwWtiu38Sn?2+YI<+*ox*IAm1$N@NnR@pE0IkkQt^DXd zIF-#JfuCu89LwfpV%#dsQ}he z2sd~yfj@}HPzt=5z|S`!KTq*6G+1%SS&E@4xt)(g9h?E4Gm6*Zz$!MzgvKG% zuo7oQm6)Ch2B`D=T;vAl#1seZ*TXI5GM&J%(n*EJe1eP{OR&sR83!#86w0mw;Z5PE zD&rn-6gQhE@drUC6k6P*hx!VE18*9TG=Z0l``uk(Z_j=-MP5MW#1ds%^>~53P}y%N z-!<9(K#@|{qJm{78yC|^K#04|{qGzP5EuHLeE9EY=KnFwM1P41RZu$zF65vMnFSTI zL>R@_!oq=FNsSz|P?I1+;(?&MtD#4%ufuv~)(YoJ6@E*WlJfY{4uZvgnxU;YE6RiqC8 zA7MGJ0SPOcb3yDGM)4ZDjl_MKg3=`VJf#Tm`#GfYeUV2Ev0d{4hR=3^Gx11)9ZTE& zp2XGlt8ObCw09Ok1lhibX#4PnLrdKpv}1Z(9=jGEJCcB_56Ys3wnu6itT>yH&kg*t z1TD=XN*FDcW?3nnEP-@qHJr#zDI8kO%mtUoajRDa^(eNcm-49lprz5{6U~$>0g3po1}iv zGN#Fy44?t2KiDb<@jN)ls=z%&e&?j;c>gJ$fjK!LJK&FtQo{K2R^tLW~?s4 z(jy)de+o+`C-Mull-#|Go5ZaNOr7CM^0aj8;rOz9r4Y)6G?%%k&#RsO$gEx4D}ejs zLqzF#S28ZJbxRD`k%`IJ3Y45}VDeDe-#ZWiZt>rQ>iuII@Q=yqv0v4QN?$5sGyoRJ z-55;WG8X8#oKW==Jhf9~dQQjinTIZa-Ah6)u%#jZpi(`NwfULUE28R5LW$b%_8OLc z8m4$Hx$x@(Z|R|TKVj^^M4Z$Zx843Fcc$~W&k>`M1L~1!Z4mxLY*S55(GWWh(gQqQ(ge&sp2~LBIs$ znFc*T0ej z;>9ElwEue2K;NSfdoUC)i4NKZZERrK3UMhhPNw%tCztnK!-B-iY1dv|);YRA;Hv?D za<`NBiCRaV~c5_?Fw34R)!ZA;&2dODRT$nA=L}ZDD`HNXM5;#g81ah-t!!lb?`p8?N z81f-@c)6%k z$CU%ut`He6wSa7P2-=PQ>4ZET4r& zq3PGwR={Cec2DmF!oDG^ebBD&oMdlNkXW=HCdLj5t&n;Ik6HV}w)u6S*dv71+7KCj z!a%KTp@m#Tw!{uYdeeypcz5n+vC?ktJxQn5sLk$>N$TI1@gMp*7))8tpK0E~W5s9s}UAqILBCCb% z7GlqbiP_uo*%otmMv#AeSp@sq&Oyh^Euhbg_YEpHs^( zj7yv!-v@HJ_P{Zrd=#^{Ue&%y;tY0uw{Uvufi`Gja{#^<69^zdD@s_~z3xxp)70Ra zs`^(mV)XiQ-!L8$DHEO^gzm%Sy_P)u2p+gr9vfC{3$3a0dMdk9IAgEVFrsmU+I6}d zi@0&l0l$FOEe;<J$mN-?&x!U;GSRLgjdvZ)A4=7 zf)JNsY%(e$3Ko2hRCS_lLJ_aU{yhp?c!gvgb^RPk5lw}{nXt2mxkh04c#JXiHQ`O+ zmM{P^dKd#c*o|`|rhVlq5|368V)r(J*FJzBMxYs(X@Rhs$Ro7n^Y$Pdt&m!7H17Nq zK$QG$r|7?4p#MjW{u?H~mQzR|vmZV9M9A&+@*y$I22W>1k6+_!dN&fA2wl))o5_XZ zKaiUM0khjJxME@ef$#qKf=_XFI*{5uaKcAq2wpyrj0w&NtvVKOV%s2{UroPo2KI+^0jQ zlJ*!6swj5VdCPvTK+vuA(P+qua)td>oKW1lg|O@+RwyGmS}|C@QKn|WmzbaQJ0U(ai+ z*YHx^>qj2>16s12boL1C9r;zRwX@z4%0u#l6EnLI$TB)GVA|7(P^-_f5MEIK+!Vvb zUT|i(5;G_ok}p*eSdo!!Fje|((CU@gV<;>L7S^+{50shM)W91BkD{yKGB#!XnSutT z|836yOH}zkNWS4+1Vrsc73S{8fpw@Vcc;#s)f>AnSojBe2z^ng|@`D z|GYJQTu1+C&|Y(t=9CUeL%YIn-RiC?z(16Jww%X)OZ&$&KX z{65I-*UFfPzX><5Zj~sM+QjutEpp|4Z$lw{7>6BnC>PZRdY!#w;NPPcinC`pCeC08YobY@2Pymhpn#i(@Koojd?A lrrU7-HM#%SAIRPUlf&6BVTIPpME?HbGH+1{+-vE1^lp+8C literal 47489 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 -`TL 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 GZvPKy1ljQb diff --git a/docs/reference/problem-4.png b/docs/reference/problem-4.png index 203f6da383717f359ae41129ea7b0b19ddee4212..e0ef5d151cb50074dfbe89bdd3634c1f701e648c 100644 GIT binary patch literal 13423 zcmeHudstIv*6&6|YUvQCR%k1PcC6A?Fjy532x-R)#S5qyB610W5)=Xma!Ux8+77h{ zRIDHZiFYJ{a0!HK0!RTPmq=v;At9gyq9G6d7e3cd}n6P`Qtm|`#ccX z*?ISR-@Sh8_gic2Tt4dQ{-^gpeIEdTKY4t=?>GP$Cj!9g;P=)-NBDWS=b)Fh!H3-U zL9ftI?`=wjUe-l_?;8sMRvG#qqnye4vjFfJ;IZ$!6NyFAQBuuJLcp+E)u0kxUbAYQ zN7}wsA8imFGx~G?C*ies>>piuQeOk2E5{GhiU(g&Plfsvx9vVubUbtC#6^F*n%eFR z>h*tyb4bqeF5!@Wq>?Qb#Bv*2bV!xrRCvXhY_^N5 zE@!CRRHPJfovFdn|7*YEADxPeZhK`@VtGEpl}-qW!ztz^Q75!E#qa5_5oA)1-el^D zLQWoa3Btx5g6SHU2Y=gZ1VqP=I4YnM>~D0_x@?_R&f*B`0pKKs03EyWID{&;R>|tn zs;KAj;Y~Zu^i#D;PAc~9TDr!Lp?5{j$7`vG#ite1se+$AG19-iwdzT_s#)?q-?SNx z@o5>9?wxSs+sPy9M(UgcO|rMG6-sH9pv z>$CY4X1Dg)^32+&7(@d$UG*J~uqjG>vDahD1PIz+?uCR-^^5mV)EJAc%Y{ct^}HRS zEe{CC;wG0CHiI(T25zmyajMRx|F(qECsSPoRlE77ihU?QRzqW?DYpHD85-q`U73<( znEQ*x)J+yeslm2xXz!?rRKD~^QwDW~+RzDqA5*wtb!n^1&JG9R!@&4lHL6zRP)J&D z4FKCZVbK6EQD5E|(xzVVQka61{Ce*G`9&7kYtzt+@{d_IWzN6GT`FbHxA?{^ALxeh zs$SZE2modF=@vkct^oUf#w6J_;r7sGuHW?tu2@LuhZ5d8@(HI#Kn8)lp!Dos+BqMB5}?Svk#7%iK74 z=8N%Md5h|cx+IPR9LE!ui}4d|X7F|W$B9>3O#%CC8nq6CplwHT6lA7oX@A#Qo%`h%EaMUY_n{r}Rx$@L7jyjy5cx9se4~Mlt zP>!!5etB~0`n=GTDD&i%M0Uz|iX$M*wqeG`yhwFbGUhXO;gOnK8d;Ijz{{u!5ZJn= zOyp{pX?~khy;rE3ZKi!I#p+l1-fWK6f-e*{p(&6bt(+o}6jPzr(aI~Kr`4Nio0#g8 zwFLghdKbRYitaT-R2Dzud&aa_U=qlkq?Xeh9Uby=FUpxbF}c&yhMeb1b0fa5lHap> z-RN^}(~$I%+I%#!&dl2gXx)C{=e0l@OO`D`6pI#I3TMg*T8CM!BMlRYd$>i2)jj!2 zIF)3Fl0N#&CNQV?MMx1HJ<sCh6qTVa4y}shnMC+zQGrn;;KI+YCA_OR~HMQKB;o0Y_vm2-yj>Jmdh4K%; z>W(Y!NQx%t+B?QMjEzGlpcqraQ@Z>i4NoB|5(YL$$;)$~^@$v^o+O7?!yl&H(}1NWMnS~E5;!pa5wfB&Lh?C8w>eHN8s(b6wA1UF zTW|Hz$Zm@D+a#O8+Y%4(?pE?yG%1L^)OddEw@YOYU1%u0GUO4%aJa_#C{+&@q zou*!5O<0YArh((C7#q!FceWswERQe(alr>1D-V==qZB=X@=puHuDT;7m?kl~ zoh2_G1LYK1Qv4miC{{}sau&;EG6&l(p>`>pw!Lys5sm8Psmq+wE?|5Y1oxs2%hIqN z{GW;$Z1$k05qq!b<&XH7i_|?nf&(C%1mguR74W7N8jTx2EIovO2H&fC`i)ja9 z-30%i@4>V;45&nA(6~pCzyIRwj1}gOP2i{c&f_JY`#dGdCl4_2zwfmwK*+cbr$Uvd z7RjpQDnzP!9D)IPQ&h8dSkeJz+E|{w9^j{#&lv;#gbTbx5&I`74?wtdMK2JrSoJ*b7A8T)O!VpYLMSKM|*E-5~1Y$JT3l2KJUc)J}Cb zRcATK3*O+jO}+3vJ0ouoGmGs%ikkdvn=~4YTA_^fhiqSJzl`%mItr4s$umJ@T(>0= zHN6GAzrn2f3B8ms6J9a>%eL(|2zl3I$6KFIEKepehgndlXf>N_ITf@EQ6aR4fC^M* zG*=NiAkVEtD#wl2*0wYzLUCij3hbBMkqDqGps~)(g$XNu@2?Z(Xs`0f^UQ5dFhxE! zDn+>w6F;*h-wWA&P@|Ye4Rm%MKqM`AH_a8vAHZ>wf(t9@Lc)ZimmHaMlTfrVE-1yh zqA^&Hx+CBL#m`Gakyo=qQ_mfX<3LPZNu-%PfUin>CMtorZ5rHA0uSC#y5~F2a_tW) zKsai>uq}d1so}7a7q39amZewNu^gn<*qd9On<5Xfo}KGF(DE9On31?A+Bx^TN3tgC zTc>pqRem>(g$hZvejyaRGDTT0c?A8vKZ9sLM6>lh>7|*epnTXf1qyh`lc%dBybBzl5c>;dBiP`jNBZ{LtHo$q8u2h zV@_QUOV5(L?5xVS_6yq~!xb{J9K=cSF{w)AVy9%|2D4SI=k(>Fw3%m<4!BMD7ggS{ z$`^g*@I8w4w1|+9yPLgXRWIOIZrDO^_Bo6c;V;H8VE{3|)%1f`>nu>z1l>5ckZ|St z;*&aEd=zV5BdK2OKI`@k)(z>t?UZO%(g}}SHV#lNP;L-zTwk1NnePr*x$j5oLPN3} z!e{{F*1~V0E!)PoSv_>?kEnQrJFI8SM@tt^E5v#ctveQ1!&5dyLon`tF1agL;5_#{ ze!rRD z#0X$c%wB>m(oz@NO1n;aP}tMi>Jc57ulGPKb?^Z^*mx8JKELa(ZAS=P=WS`PT5s=_ z*J>AB{X#?7u1BrNF~4q%TS;!{uM%6@I4fOUZsKbSwDJ_@Eha5u$Ug2{%KH{foFXJAo|@V zNe?BKm0`r55XhXF7F=U>fcP)&H%ZozLQT<&&J8s8!9Bg6t^?_zF8<{ZhxhbTFM-xx zu8*mW)W#mwQ;uaZ!YV0h5C5geli0=H+N+@7!fb`}+=PZ*~lpI~}(gotp4J$zt(N ze+&-@2pA32je*lNJ~OEWxNay66zylf?$G9xv+JGfI|@fnAQ_Dzn_vwU1mQX{@(v+I zkuK@Qqu{HZ_N|7Dk5&%1q|OXW4lwp^j+nf7ix=3;zmQPtfGWr2W5&FZw>8C*@>e@s z&&{pU7ZHkf>%A(3*20&r|C8UI0KV-2nZ6k;mv^NQ;zATw;Gu+Zb~@uti6VLmd6X5( zUd}JET=Q|E!8rS@!l~{qFK1)>U54sK(5FLv`9uHf|M4Ebisim!-4SoIRacG}Rz956 z5}=EY{RRS|FOs+32Tr<_Y!HM~39a8LgU z)&7swl>rST37|32xUyF~_6*_ridiZirkc229BZd`mylNnmAt1f(6lm(Q(re{Hcl>O zJ|KtC`kJ(-y>o!dyOwthPXDi#bt;MQV>*RIMQfrW&{5^=iV@Uv|5kXFKDU4d@#!f= zJBWJK_N(?)Xq(DKTry1+V^l|Wd{*f=d`X|M28oe(H6@@0EsEAxPCgoN99ilsARSPo z9@z=6#28EmTx6TUOsb7fRE3#U0xt_ZB%X#6<&`U*Yo%Wqs!^@8iQ$UY-0sM-?7pE2 zr=1f3@v7lYL{F6~g7SUM(~TV7Y+O-NPB8+u+r<;n%6v09PKYf;V23VcoCloFzJ+S^ z154K!(Xb?zHa7C2H}YW$wb-=Et28iR6s?n*joL)#>5Jn)8lw{@W6OsnAv78Cr$RQn zG0EgW{XK#>S=RxwT%swhzK7qy=~J&{(UvZsF)S-S)3K?l_;M4hLWn^&LiK#QRlV1^ zt&IVWxU5#bCy@UD&=D82 zO3a(^;3Bg|bBilhzEzAx#^|DkN1<8X)BuN*^5J#MMVtqK()A6D#H=$Cqclc4`wb+3 zAmV?=kpEase5gm$Fu5JHgM{UsSqehNaTw{b14kfAxD_(C7kOVp)Mu1|_I?7iRl&ty z!MKPf!AOM5{B<)cza)Ps?_A1fbc`mU^z7Me$7Sr#P9-UkHN)x9#6c$SK^5BU6O=;2 z&l?kDOTWh~GwLpPVNg3EIXly*zC0O8A*7)b&XIA2b1BRQ{sVXrkP<04;wWfbRtzQxB8cCAJuN{wja6-rthRqCf3*sOLe1E2n+~Vt7tIz6UaKpTdblm>E$z)WNNnnmm#cAW-=Fma>K)*s zr}Ar{6{P#o&?TsD_SnE{dw1E!U=)`_tHv+rkIjaeCn6}6#D`Gn83e(f^ro1du*NGt z6+xM~UGHjHYR_rgyrk1{9a7kKS1w!M=&h51_$l|H8l`eFI#*B`F7fTG#YE8f7@x5mNF?a88O|NqR;G@<0X90HU!6MC0z~q#&?N?OG z>u^TOFNp?_Ny&(al#OXK3|GWA%aF;i`5SM`T=IQC>5ziu*9`UZUw-BG6fd7NwrS|w(^XE9 z=vJRF04F-$d1MgQUui`tKv<{dPWwAx1F?AN3&@zi6NE5hs=K6E@0`ZV?VEdZ5m5839bz+2PKA86x)_!@r$@%SChP zDdjvkL}$>G9o3oi2^DL^%Rz!R$a)3&yvC%2P4O++m0AiWMizU{#l}=p75h;xfu@G_K zxrs$a!mH1-BT}x!2m^dkK!?utpt51|!9t^-{+b${@o zm0s!%tWaTw45bx+1}my=#)77D&>j#BiyHYC&M_>0xLX)-VbgZHu*y_r8r`Yyb1HY6 zZgU;L6&edOFgg0(Mx5?sQxnlMcDd*>nHrxCiUO9*1BM}ud!%6cCa+~ zUO9LtsB$a*$ir(Mp;L|ZxbNGDJDu!Yxi;)Td)I`k9r^7^eMl8j>Tqi?m@mv2*x-wfIo_|N$G`GiHN|IawMAnV_Ics9`T?lox^Jm|Q# z`#c4oP*bMl#naBR3eVs~pIvLLD|5eKhL5c*qwlw2a2;HPxW3ynW}eG7z+r9x@N`Q3 z1;!l)5l_yq;U4rW`kuo48pP4_Hrr401=zu^7l`?Y*yJmHX2rpQ?jC0OL)rbICLTV# z-Y!O=>D`QiKT?EGzIOM;(vaSBV?MqVcb6nriWRcww^^~LnLMyrjOv-!7Lb^{dACE& z6nyLrld*$Fx30Tvxi!%61s0C{{a#AG!i0^>b={Ge=gX=o!(N8{UMTQfAPm;6!&2w3~B(-TCQ70oE55RWb$H;@1Z^*W)!*X%% zIXg@98QHN)9%O2uZy*uHm-c>9cK~M8Y9i|2u19a$JGXiVDaa50w{TMH0`(J~?KvD1 zySVUuzp?Q^6VB;`$9EeuH<1aeT`!JcNA}v0KG!x!;sr68HwP3Qfcx!32kL z%VKUg$|u?wJ+DTW;*;V~^!@y@JkX6V)v3PgyWOjFhZ>3*ttF>!^~ycy*>FfO!apC% z_5TEV8lYriUEr+&&s_Q4j2i>2GD@=`t9!{IAzv|n+q-A_%W3&zH-02)+D0s8%w(&0 z4X-@bXb0x36d|`YYTBa@>&524jT9jX2^JUz2PW}Pdj#9_! zW$IMPHCN;tnB`A&+Zte&+Z7DK5;p6)QJ9N%J2SkpGj( z=$~AB>1aRH4eVomQE>$xd_1FD!-XQV+41qL{?&&iLB6nSOXozNZQx@E3q2xpEYAdY ziLb_nF~DkJR!B|nyj$!Qri)vSB_+e5h1+RbNa2Sd+R()+W z`SUS2M0k;tn1%xER*u)Tr8Cl9&osw!4{@G(z^#$pKbl=zI-5k&uZy^ieRYQyXVK)M9A{=^gs-`y~*Z-hfp6Jm_->r+wCGn#h@XEfIM!@U! zue}UKxc>}k^iO)@J5>qX8qmARvOCwN!I=PkLnHA=|3fin7uN$t**1yk4lN!r;m|+8 s+|Tv-B8$7w7g*kn{|7tp8ns}J2|sXcMD)S;`sqFPd+wwEHRy-`0)u5zqW}N^ literal 13323 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 - - - - - - -Conservation planning problem — problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Conservation planning problem — problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -194,99 +111,94 @@

    Conservation planning problem

    Create a systematic conservation planning problem. This function is used to specify the basic data used in a spatial prioritization problem: the spatial distribution of the planning units and their costs, as well as -the features (e.g. species, ecosystems) that need to be conserved. After +the features (e.g., species, ecosystems) that need to be conserved. After constructing this ConservationProblem-class object, it can be -customized to meet specific goals using objectives, -targets, constraints, and -penalties. After building the problem, the -solve() function can be used to identify solutions. +customized to meet specific goals using objectives, +targets, constraints, and +penalties. After building the problem, the +solve() function can be used to identify solutions. Note that problems require an objective, and failing to specify an an objective will throw an error when attempting to solve it.

    -
    problem(x, features, ...)
    +    
    Usage,
    problem(x, features, ...)
     
    -# S4 method for Raster,Raster
    -problem(x, features, run_checks, ...)
    +# S4 method for Raster,Raster
    +problem(x, features, run_checks, ...)
     
    -# S4 method for Raster,ZonesRaster
    -problem(x, features, run_checks, ...)
    +# S4 method for Raster,ZonesRaster
    +problem(x, features, run_checks, ...)
     
    -# S4 method for Spatial,Raster
    -problem(x, features, cost_column, run_checks, ...)
    +# S4 method for Spatial,Raster
    +problem(x, features, cost_column, run_checks, ...)
     
    -# S4 method for Spatial,ZonesRaster
    -problem(x, features, cost_column, run_checks, ...)
    +# S4 method for Spatial,ZonesRaster
    +problem(x, features, cost_column, run_checks, ...)
     
    -# S4 method for Spatial,character
    -problem(x, features, cost_column, ...)
    +# S4 method for Spatial,character
    +problem(x, features, cost_column, ...)
     
    -# S4 method for Spatial,ZonesCharacter
    -problem(x, features, cost_column, ...)
    +# S4 method for Spatial,ZonesCharacter
    +problem(x, features, cost_column, ...)
     
    -# S4 method for data.frame,character
    -problem(x, features, cost_column, ...)
    +# S4 method for data.frame,character
    +problem(x, features, cost_column, ...)
     
    -# S4 method for data.frame,ZonesCharacter
    -problem(x, features, cost_column, ...)
    +# S4 method for data.frame,ZonesCharacter
    +problem(x, features, cost_column, ...)
     
    -# S4 method for data.frame,data.frame
    -problem(x, features, rij, cost_column, zones, ...)
    +# S4 method for data.frame,data.frame
    +problem(x, features, rij, cost_column, zones, ...)
     
    -# S4 method for numeric,data.frame
    -problem(x, features, rij_matrix, ...)
    +# S4 method for numeric,data.frame
    +problem(x, features, rij_matrix, ...)
     
    -# S4 method for matrix,data.frame
    -problem(x, features, rij_matrix, ...)
    +# S4 method for matrix,data.frame
    +problem(x, features, rij_matrix, ...)
     
    -# S4 method for sf,Raster
    -problem(x, features, cost_column, run_checks, ...)
    +# S4 method for sf,Raster
    +problem(x, features, cost_column, run_checks, ...)
     
    -# S4 method for sf,ZonesRaster
    -problem(x, features, cost_column, run_checks, ...)
    +# S4 method for sf,ZonesRaster
    +problem(x, features, cost_column, run_checks, ...)
     
    -# S4 method for sf,character
    -problem(x, features, cost_column, ...)
    +# S4 method for sf,character
    +problem(x, features, cost_column, ...)
     
    -# S4 method for sf,ZonesCharacter
    -problem(x, features, cost_column, ...)
    +# S4 method for sf,ZonesCharacter +problem(x, features, cost_column, ...)
    -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    x

    Raster, -sf::st_sf(), -SpatialPolygonsDataFrame, -SpatialLinesDataFrame, -SpatialPointsDataFrame, -data.frame() object, -numeric() vector, or -matrix() specifying the planning units to use in the reserve +

    +

    Arguments

    +
    x
    +

    Raster, +sf::st_sf(), +SpatialPolygonsDataFrame, +SpatialLinesDataFrame, +SpatialPointsDataFrame, +data.frame() object, +numeric() vector, or +matrix() specifying the planning units to use in the reserve design exercise and their corresponding cost. It may be desirable to exclude some planning units from the analysis, for example those outside the study area. To exclude planning units, set the cost for those raster -cells to NA, or use the add_locked_out_constraint function.

    features

    The feature data can be specified in a variety of ways. -The specific formats that can be used depend on the cost data format (i.e. +cells to NA, or use the add_locked_out_constraint function.

    +
    features
    +

    The feature data can be specified in a variety of ways. +The specific formats that can be used depend on the cost data format (i.e., argument to x) and whether the problem should have a single zone or multiple zones. If the problem should have a single zone, then the feature -data can be specified following:

      -
    • x = RasterLayer-class, or -x = Spatial-class, or -x = sf::st_sf(): +data can be specified following:

      • x = RasterLayer-class, or +x = Spatial-class, or +x = sf::st_sf(): y = Raster-class object showing the distribution of conservation features. Missing -values (i.e. NA values) can be used to indicate the absence of +values (i.e., NA values) can be used to indicate the absence of a feature in a particular cell instead of explicitly setting these cells to zero. Note that this argument type for features can only be used to specify data for problems involving a single zone.

      • -
      • x = Spatial-class, or -x = sf::st_sf(), or +

      • x = Spatial-class, or +x = sf::st_sf(), or x = data.frame: y = character vector with column names that correspond to the abundance or occurrence of @@ -301,112 +213,113 @@

        Arg rij or rij_matrix must also be supplied. This type of argument should follow the conventions used by Marxan, wherein each row corresponds to a different feature. It must also contain the -following columns:

        -
        id

        integer unique identifier for each feature +following columns:

        id
        +

        integer unique identifier for each feature These identifiers are used in the argument to rij.

        -
        name

        character name for each feature.

        -
        prop

        numeric relative target for each feature + +

        name
        +

        character name for each feature.

        + +
        prop
        +

        numeric relative target for each feature (optional).

        -
        amount

        numeric absolute target for each + +

        amount
        +

        numeric absolute target for each feature (optional).

        -

      • -
      -

      If the problem should have multiple zones, then the feature -data can be specified following:

        -
      • x = RasterStack-class, or +

      • +

      If the problem should have multiple zones, then the feature +data can be specified following:

    ...

    not used.

    run_checks

    logical flag indicating whether checks should be + +

    ...
    +

    not used.

    +
    run_checks
    +

    logical flag indicating whether checks should be run to ensure the integrity of the input data. These checks are run by default; however, for large datasets they may increase run time. If it is taking a prohibitively long time to create the prioritization problem, it -is suggested to try setting run_checks to FALSE.

    cost_column

    character name or integer indicating the +is suggested to try setting run_checks to FALSE.

    +
    cost_column
    +

    character name or integer indicating the column(s) with the cost data. This argument must be supplied when the -argument to x is a Spatial or +argument to x is a Spatial or data.frame object. This argument should contain the name of each column containing cost data for each management zone when creating problems with multiple zones. To create a problem with a single zone, then -set the argument to cost_column as a single column name.

    rij

    data.frame containing information on the amount of +set the argument to cost_column as a single column name.

    +
    rij
    +

    data.frame containing information on the amount of each feature in each planning unit assuming each management zone. Similar to data.frame arguments for features, the data.frame objects must follow the conventions used by Marxan. Note that the "zone" column is not needed for problems involving a single management zone. Specifically, the argument should contain the following -columns:

    -
    pu

    integer planning unit identifier.

    -
    species

    integer feature identifier.

    -
    zone

    integer zone identifier (optional for +columns:

    pu
    +

    integer planning unit identifier.

    + +
    species
    +

    integer feature identifier.

    + +
    zone
    +

    integer zone identifier (optional for problems involving a single zone).

    -
    amount

    numeric amount of the feature in the + +

    amount
    +

    numeric amount of the feature in the planning unit.

    -
    zones

    data.frame containing information on the zones. This + + +

    zones
    +

    data.frame containing information on the zones. This argument is only used when argument to x and y are both data.frame objects and the problem being built contains multiple zones. Following conventions used in MarZone, this argument should contain the following columns: -columns:

    -
    id

    integer zone identifier.

    -
    name

    character zone name.

    - -
    rij_matrix

    list of matrix or -dgCMatrix +columns:

    id
    +

    integer zone identifier.

    + +
    name
    +

    character zone name.

    + + +
    +
    rij_matrix
    +

    list of matrix or +dgCMatrix objects specifying the amount of each feature (rows) within each planning unit (columns) for each zone. The list elements denote different zones, matrix rows denote features, and matrix columns denote planning units. For convenience, the argument to rij_matrix can be a single matrix or -dgCMatrix when specifying a problem with a +dgCMatrix when specifying a problem with a single management zone. This argument is only used when the argument -to x is a numeric or matrix object.

    - -

    Value

    - -

    ConservationProblem object containing +to x is a numeric or matrix object.

    +
    +
    +

    Value

    +

    ConservationProblem object containing data for a prioritization.

    -

    Details

    - +
    +
    +

    Details

    A systematic conservation planning exercise leverages data to help inform conservation decision making. To help ensure that the data -- and resulting prioritizations -- are relevant to the over-arching @@ -421,33 +334,33 @@

    Details (see the Management Zones vignette for details). After deciding on the management action(s), you can compile the following data.

    First, you will need to create a set of planning units -(i.e. discrete spatial areas) to inform decision making. +(i.e., discrete spatial areas) to inform decision making. Planning units are often created by subdividing a study region into a set square or hexagonal cells. They can also be created using -administrative boundaries (e.g. provinces), land management boundaries -(e.g. property boundaries derived from cadastral data), or -ecological boundaries (e.g. based on ecosystem classification data). -The size (i.e. spatial grain) of the planning units is often determined +administrative boundaries (e.g., provinces), land management boundaries +(e.g., property boundaries derived from cadastral data), or +ecological boundaries (e.g., based on ecosystem classification data). +The size (i.e., spatial grain) of the planning units is often determined based on a compromise between the scale needed to inform decision making, the spatial accuracy (resolution) of available datasets, and the computational resources available for generating prioritizations -(e.g. RAM and number of CPUs on your computer).

    +(e.g., RAM and number of CPUs on your computer).

    Second, you will need data to quantify the cost of implementing implementing each management action within each planning unit. Critically, the cost data should reflect the management action(s) considered in the exercise. For example, costs are often specified using data that reflect economic -expenditure (e.g. land acquisition cost), -socioeconomic conditions (e.g. human population density), +expenditure (e.g., land acquisition cost), +socioeconomic conditions (e.g., human population density), opportunity costs of foregone commercial activities -(e.g. logging or agriculture), or +(e.g., logging or agriculture), or opportunity costs of foregone recreational activities -(e.g. recreational fishing) activities, +(e.g., recreational fishing) activities, In some cases -- depending on the management action(s) considered -- it can make sense to use a constant cost value -(e.g. all planning units are assigned a cost value equal to one) +(e.g., all planning units are assigned a cost value equal to one) or use a cost value based on spatial extent -(e.g. each planning unit is assigned a cost value based on its total area). +(e.g., each planning unit is assigned a cost value based on its total area). Also, in most cases, you want to avoid negative cost values. This because a negative value means that a place is desirable for implementing a management action, and such places will almost @@ -457,13 +370,13 @@

    Details To achieve this, you will need to select a set of conservation features that relate to the over-arching goals of the exercise. For example, conservation features often include -species (e.g. Clouded Leopard), habitats (e.g. mangroves or +species (e.g., Clouded Leopard), habitats (e.g., mangroves or cloud forest), or ecosystems. The benefit that each feature derives from a planning unit -can take a variety of forms, but is typically occupancy (i.e. +can take a variety of forms, but is typically occupancy (i.e., presence or absence), area of occurrence within each planning unit -(e.g. based on species' geographic range data), or -a measure of habitat suitability (e.g. estimated using a statistical model). +(e.g., based on species' geographic range data), or +a measure of habitat suitability (e.g., estimated using a statistical model). After compiling these data, you have the minimal data need to generate a prioritization.

    A systematic conservation planning exercise involves prioritizing a set of @@ -479,20 +392,21 @@

    Details conservation plans. The decision variables are what we control, and usually there is one binary variable for each planning unit to specify whether that unit is selected or not (but other approaches are available, see -decisions). The constraints can be thought of as rules that must be +decisions). The constraints can be thought of as rules that must be followed. For example, constraints can be used to ensure a prioritization must stay within a certain budget. These constraints can also leverage additional data to help ensure that prioritizations meet the over-arching goals of the exercise. For example, to account for existing conservation efforts, you could obtain data delineating the extent of existing protected areas and use constraints to lock in planning units that are covered by them -(see add_locked_in_constraints).

    -

    Optimization

    - +(see add_locked_in_constraints).

    +
    +
    +

    Optimization

    The prioritizr package uses exact algorithms to solve reserve design -problems (see solvers for details). +problems (see solvers for details). To achieve this, it internally formulates mathematical optimization problems using mixed integer linear programming (MILP). The general form of such problems can be expressed in matrix notation using @@ -512,286 +426,285 @@

    < representation level of feature \(i\) in planning unit \(j\). If you wish to see exactly how a conservation planning problem is formulated as mixed integer linear programming problem, you can use -the write_problem() function to save the optimization problem +the write_problem() function to save the optimization problem to a plain-text file on your computer and then view it using a standard -text editor (e.g. Notepad).

    +text editor (e.g., Notepad).

    Please note that this function internally computes the amount of each feature in each planning unit when this data is not supplied (using the -rij_matrix function). As a consequence, it can take a while to +rij_matrix function). As a consequence, it can take a while to initialize large-scale conservation planning problems that involve millions of planning units.

    -

    See also

    - -

    See solve() for details on solving a problem to generate solutions. -Also, see objectives, penalties, targets, constraints, -decisions, portfolios, solvers for information on customizing problems. -Additionally, see summaries and importance for information on +

    +
    +

    See also

    +

    See solve() for details on solving a problem to generate solutions. +Also, see objectives, penalties, targets, constraints, +decisions, portfolios, solvers for information on customizing problems. +Additionally, see summaries and importance for information on evaluating solutions.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_polygons, sim_pu_lines, sim_pu_points,
    -     sim_pu_sf, sim_features)
    -
    -# create problem using raster planning unit data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# \dontrun{
    -# create problem using polygon (Spatial) planning unit data
    -p2 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem using line (Spatial) planning unit data
    -p3 <- problem(sim_pu_lines, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem using point (Spatial) planning unit data
    -p4 <- problem(sim_pu_points, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem using polygon (sf) planning unit data
    -p5 <- problem(sim_pu_sf, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# since geo-processing can be slow for large spatial vector datasets
    -# (e.g. polygons, lines, points), it can be worthwhile to pre-process the
    -# planning unit data so that it contains columns indicating the amount of
    -# each feature inside each planning unit
    -# (i.e. each column corresponds to a different feature)
    -
    -# calculate the amount of each species within each planning unit
    -# (i.e. SpatialPolygonsDataFrame object)
    -pre_proc_data <- rij_matrix(sim_pu_polygons, sim_features)
    -
    -# add extra columns to the polygon (Spatial) planning unit data
    -# to indicate the amount of each species within each planning unit
    -pre_proc_data <- as.data.frame(t(as.matrix(pre_proc_data)))
    -names(pre_proc_data) <- names(sim_features)
    -sim_pu_polygons@data <- cbind(sim_pu_polygons@data, pre_proc_data)
    -
    -# create problem using the polygon (Spatial) planning unit data
    -# with the pre-processed columns
    -p6 <- problem(sim_pu_polygons, features = names(pre_proc_data), "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# this strategy of pre-processing columns can be used for sf objects too
    -pre_proc_data2 <- rij_matrix(sim_pu_sf, sim_features)
    -pre_proc_data2 <- as.data.frame(t(as.matrix(pre_proc_data2)))
    -names(pre_proc_data2) <- names(sim_features)
    -sim_pu_sf <- cbind(sim_pu_sf, pre_proc_data2)
    -
    -# create problem using the polygon (sf) planning unit data
    -# with pre-processed columns
    -p7 <- problem(sim_pu_sf, features = names(pre_proc_data2), "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# in addition to spatially explicit data, pre-processed aspatial data
    -# can also be used to create a problem
    -# (e.g. data created using external spreadsheet software)
    -costs <- sim_pu_polygons$cost
    -features <- data.frame(id = seq_len(nlayers(sim_features)),
    -                       name = names(sim_features))
    -rij_mat <- rij_matrix(sim_pu_polygons, sim_features)
    -p8 <- problem(costs, features, rij_matrix = rij_mat) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve problems
    -s1 <- solve(p1)
    -s2 <- solve(p2)
    -s3 <- solve(p3)
    -s4 <- solve(p4)
    -s5 <- solve(p5)
    -s6 <- solve(p6)
    -s7 <- solve(p7)
    -s8 <- solve(p8)
    -
    -# plot solutions for problems associated with spatial data
    -par(mfrow = c(3, 2), mar = c(0, 0, 4.1, 0))
    -plot(s1, main = "raster data", axes = FALSE, box = FALSE, legend = FALSE)
    -
    -plot(s2, main = "polygon data")
    -plot(s2[s2$solution_1 > 0.5, ], col = "darkgreen", add = TRUE)
    -
    -plot(s3, main = "line data")
    -lines(s3[s3$solution_1 > 0.5, ], col = "darkgreen", lwd = 2)
    -
    -plot(s4, main = "point data", pch = 19)
    -points(s4[s4$solution_1 > 0.5, ], col = "darkgreen", cex = 2, pch = 19)
    -
    -# note that as_Spatial() is for convenience to plot all solutions together
    -plot(as_Spatial(s5), main = "sf (polygon) data", pch = 19)
    -plot(as_Spatial(s5[s5$solution_1 > 0.5, ]), col = "darkgreen", add = TRUE)
    -
    -plot(s6, main = "preprocessed data (polygon data)", pch = 19)
    -plot(s6[s6$solution_1 > 0.5, ], col = "darkgreen", add = TRUE)
    -
    -
    -# show solutions for problems associated with aspatial data
    -str(s8)
    -#>  num [1:90] 0 0 0 0 0 0 0 0 0 1 ...
    -#>  - attr(*, "objective")= Named num 3586
    -#>   ..- attr(*, "names")= chr "solution_1"
    -#>  - attr(*, "status")= Named chr "OPTIMAL"
    -#>   ..- attr(*, "names")= chr "solution_1"
    -#>  - attr(*, "runtime")= Named num 0.003
    -#>   ..- attr(*, "names")= chr "solution_1"
    -# }
    -# create some problems with multiple zones
    -
    -# first, create a matrix containing the targets for multi-zone problems
    -# here each row corresponds to a different feature, each
    -# column corresponds to a different zone, and values correspond
    -# to the total (absolute) amount of a given feature that needs to be secured
    -# in a given zone
    -targets <- matrix(rpois(15, 1),
    -                  nrow = number_of_features(sim_features_zones),
    -                  ncol = number_of_zones(sim_features_zones),
    -                  dimnames = list(feature_names(sim_features_zones),
    -                                  zone_names(sim_features_zones)))
    -
    -# print targets
    -print(targets)
    -#>           zone_1 zone_2 zone_3
    -#> feature_1      2      0      0
    -#> feature_2      1      1      2
    -#> feature_3      3      3      2
    -#> feature_4      1      2      0
    -#> feature_5      2      1      1
    -
    -# create a multi-zone problem with raster data
    -p8 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(targets) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve problem
    -s8 <- solve(p8)
    -
    -# plot solution
    -# here, each layer/panel corresponds to a different zone and pixel values
    -# indicate if a given planning unit has been allocated to a given zone
    -par(mfrow = c(1, 1))
    -plot(s8, main = c("zone 1", "zone 2", "zone 3"), axes = FALSE, box = FALSE)
    -
    -
    -# alternatively, the category_layer function can be used to create
    -# a new raster object containing the zone ids for each planning unit
    -# in the solution (note this only works for problems with binary decisions)
    -par(mfrow = c(1, 1))
    -plot(category_layer(s8), axes = FALSE, box = FALSE)
    -
    -
    -# create a multi-zone problem with polygon data
    -p9 <- problem(sim_pu_zones_polygons, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(targets) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve problem
    -s9 <- solve(p9)
    -
    -# create column containing the zone id for which each planning unit was
    -# allocated to in the solution
    -s9$solution <- category_vector(s9@data[, c("solution_1_zone_1",
    -                                           "solution_1_zone_2",
    -                                           "solution_1_zone_3")])
    -s9$solution <- factor(s9$solution)
    -
    -# plot solution
    -spplot(s9, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    -
    -
    -# create a multi-zone problem with polygon planning unit data
    -# and where fields (columns) in the attribute table correspond
    -# to feature abundances
    -
    -# first fields need to be added to the planning unit data
    -# which indicate the amount of each feature in each zone
    -# to do this, the fields will be populated with random counts
    -sim_pu_zones_polygons$spp1_z1 <- rpois(nrow(sim_pu_zones_polygons), 1)
    -sim_pu_zones_polygons$spp2_z1 <- rpois(nrow(sim_pu_zones_polygons), 1)
    -sim_pu_zones_polygons$spp3_z1 <- rpois(nrow(sim_pu_zones_polygons), 1)
    -sim_pu_zones_polygons$spp1_z2 <- rpois(nrow(sim_pu_zones_polygons), 1)
    -sim_pu_zones_polygons$spp2_z2 <- rpois(nrow(sim_pu_zones_polygons), 1)
    -sim_pu_zones_polygons$spp3_z2 <- rpois(nrow(sim_pu_zones_polygons), 1)
    -
    -# create problem with polygon planning unit data and use field names
    -# to indicate feature data
    -# additionally, to make this example slightly more interesting,
    -# the problem will have prfoportion-type decisions such that
    -# a proportion of each planning unit can be allocated to each of the
    -# two management zones
    -p10 <- problem(sim_pu_zones_polygons,
    -               zones(c("spp1_z1", "spp2_z1", "spp3_z1"),
    -                     c("spp1_z2", "spp2_z2", "spp3_z2"),
    -                     zone_names = c("z1", "z2")),
    -               cost_column = c("cost_1", "cost_2")) %>%
    -       add_min_set_objective() %>%
    -       add_absolute_targets(targets[1:3, 1:2]) %>%
    -       add_proportion_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve problem
    -s10 <- solve(p10)
    -
    -# plot solution
    -spplot(s10, zcol = c("solution_1_z1", "solution_1_z2"), main = "solution",
    -       axes = FALSE, box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_polygons, sim_pu_lines, sim_pu_points,
    +     sim_pu_sf, sim_features)
    +
    +# create problem using raster planning unit data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# \dontrun{
    +# create problem using polygon (Spatial) planning unit data
    +p2 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem using line (Spatial) planning unit data
    +p3 <- problem(sim_pu_lines, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem using point (Spatial) planning unit data
    +p4 <- problem(sim_pu_points, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem using polygon (sf) planning unit data
    +p5 <- problem(sim_pu_sf, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# since geo-processing can be slow for large spatial vector datasets
    +# (e.g., polygons, lines, points), it can be worthwhile to pre-process the
    +# planning unit data so that it contains columns indicating the amount of
    +# each feature inside each planning unit
    +# (i.e., each column corresponds to a different feature)
    +
    +# calculate the amount of each species within each planning unit
    +# (i.e., SpatialPolygonsDataFrame object)
    +pre_proc_data <- rij_matrix(sim_pu_polygons, sim_features)
    +
    +# add extra columns to the polygon (Spatial) planning unit data
    +# to indicate the amount of each species within each planning unit
    +pre_proc_data <- as.data.frame(t(as.matrix(pre_proc_data)))
    +names(pre_proc_data) <- names(sim_features)
    +sim_pu_polygons@data <- cbind(sim_pu_polygons@data, pre_proc_data)
    +
    +# create problem using the polygon (Spatial) planning unit data
    +# with the pre-processed columns
    +p6 <- problem(sim_pu_polygons, features = names(pre_proc_data), "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# this strategy of pre-processing columns can be used for sf objects too
    +pre_proc_data2 <- rij_matrix(sim_pu_sf, sim_features)
    +pre_proc_data2 <- as.data.frame(t(as.matrix(pre_proc_data2)))
    +names(pre_proc_data2) <- names(sim_features)
    +sim_pu_sf <- cbind(sim_pu_sf, pre_proc_data2)
    +
    +# create problem using the polygon (sf) planning unit data
    +# with pre-processed columns
    +p7 <- problem(sim_pu_sf, features = names(pre_proc_data2), "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# in addition to spatially explicit data, pre-processed aspatial data
    +# can also be used to create a problem
    +# (e.g., data created using external spreadsheet software)
    +costs <- sim_pu_polygons$cost
    +features <- data.frame(id = seq_len(nlayers(sim_features)),
    +                       name = names(sim_features))
    +rij_mat <- rij_matrix(sim_pu_polygons, sim_features)
    +p8 <- problem(costs, features, rij_matrix = rij_mat) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve problems
    +s1 <- solve(p1)
    +s2 <- solve(p2)
    +s3 <- solve(p3)
    +s4 <- solve(p4)
    +s5 <- solve(p5)
    +s6 <- solve(p6)
    +s7 <- solve(p7)
    +s8 <- solve(p8)
    +
    +# plot solutions for problems associated with spatial data
    +par(mfrow = c(3, 2), mar = c(0, 0, 4.1, 0))
    +plot(s1, main = "raster data", axes = FALSE, box = FALSE, legend = FALSE)
    +
    +plot(s2, main = "polygon data")
    +plot(s2[s2$solution_1 > 0.5, ], col = "darkgreen", add = TRUE)
    +
    +plot(s3, main = "line data")
    +lines(s3[s3$solution_1 > 0.5, ], col = "darkgreen", lwd = 2)
    +
    +plot(s4, main = "point data", pch = 19)
    +points(s4[s4$solution_1 > 0.5, ], col = "darkgreen", cex = 2, pch = 19)
    +
    +# note that as_Spatial() is for convenience to plot all solutions together
    +plot(as_Spatial(s5), main = "sf (polygon) data", pch = 19)
    +plot(as_Spatial(s5[s5$solution_1 > 0.5, ]), col = "darkgreen", add = TRUE)
    +
    +plot(s6, main = "preprocessed data (polygon data)", pch = 19)
    +plot(s6[s6$solution_1 > 0.5, ], col = "darkgreen", add = TRUE)
    +
    +
    +# show solutions for problems associated with aspatial data
    +str(s8)
    +#>  num [1:90] 0 0 0 0 0 0 0 0 0 1 ...
    +#>  - attr(*, "objective")= Named num 3586
    +#>   ..- attr(*, "names")= chr "solution_1"
    +#>  - attr(*, "status")= Named chr "OPTIMAL"
    +#>   ..- attr(*, "names")= chr "solution_1"
    +#>  - attr(*, "runtime")= Named num 0.004
    +#>   ..- attr(*, "names")= chr "solution_1"
    +# }
    +# create some problems with multiple zones
    +
    +# first, create a matrix containing the targets for multi-zone problems
    +# here each row corresponds to a different feature, each
    +# column corresponds to a different zone, and values correspond
    +# to the total (absolute) amount of a given feature that needs to be secured
    +# in a given zone
    +targets <- matrix(rpois(15, 1),
    +                  nrow = number_of_features(sim_features_zones),
    +                  ncol = number_of_zones(sim_features_zones),
    +                  dimnames = list(feature_names(sim_features_zones),
    +                                  zone_names(sim_features_zones)))
    +
    +# print targets
    +print(targets)
    +#>           zone_1 zone_2 zone_3
    +#> feature_1      2      0      0
    +#> feature_2      1      1      2
    +#> feature_3      3      3      2
    +#> feature_4      1      2      0
    +#> feature_5      2      1      1
    +
    +# create a multi-zone problem with raster data
    +p8 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(targets) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve problem
    +s8 <- solve(p8)
    +
    +# plot solution
    +# here, each layer/panel corresponds to a different zone and pixel values
    +# indicate if a given planning unit has been allocated to a given zone
    +par(mfrow = c(1, 1))
    +plot(s8, main = c("zone 1", "zone 2", "zone 3"), axes = FALSE, box = FALSE)
    +
    +
    +# alternatively, the category_layer function can be used to create
    +# a new raster object containing the zone ids for each planning unit
    +# in the solution (note this only works for problems with binary decisions)
    +par(mfrow = c(1, 1))
    +plot(category_layer(s8), axes = FALSE, box = FALSE)
    +
    +
    +# create a multi-zone problem with polygon data
    +p9 <- problem(sim_pu_zones_polygons, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(targets) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve problem
    +s9 <- solve(p9)
    +
    +# create column containing the zone id for which each planning unit was
    +# allocated to in the solution
    +s9$solution <- category_vector(s9@data[, c("solution_1_zone_1",
    +                                           "solution_1_zone_2",
    +                                           "solution_1_zone_3")])
    +s9$solution <- factor(s9$solution)
    +
    +# plot solution
    +spplot(s9, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    +
    +
    +# create a multi-zone problem with polygon planning unit data
    +# and where fields (columns) in the attribute table correspond
    +# to feature abundances
    +
    +# first fields need to be added to the planning unit data
    +# which indicate the amount of each feature in each zone
    +# to do this, the fields will be populated with random counts
    +sim_pu_zones_polygons$spp1_z1 <- rpois(nrow(sim_pu_zones_polygons), 1)
    +sim_pu_zones_polygons$spp2_z1 <- rpois(nrow(sim_pu_zones_polygons), 1)
    +sim_pu_zones_polygons$spp3_z1 <- rpois(nrow(sim_pu_zones_polygons), 1)
    +sim_pu_zones_polygons$spp1_z2 <- rpois(nrow(sim_pu_zones_polygons), 1)
    +sim_pu_zones_polygons$spp2_z2 <- rpois(nrow(sim_pu_zones_polygons), 1)
    +sim_pu_zones_polygons$spp3_z2 <- rpois(nrow(sim_pu_zones_polygons), 1)
    +
    +# create problem with polygon planning unit data and use field names
    +# to indicate feature data
    +# additionally, to make this example slightly more interesting,
    +# the problem will have prfoportion-type decisions such that
    +# a proportion of each planning unit can be allocated to each of the
    +# two management zones
    +p10 <- problem(sim_pu_zones_polygons,
    +               zones(c("spp1_z1", "spp2_z1", "spp3_z1"),
    +                     c("spp1_z2", "spp2_z2", "spp3_z2"),
    +                     zone_names = c("z1", "z2")),
    +               cost_column = c("cost_1", "cost_2")) %>%
    +       add_min_set_objective() %>%
    +       add_absolute_targets(targets[1:3, 1:2]) %>%
    +       add_proportion_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve problem
    +s10 <- solve(p10)
    +
    +# plot solution
    +spplot(s10, zcol = c("solution_1_z1", "solution_1_z2"), main = "solution",
    +       axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/proximity_matrix.html b/docs/reference/proximity_matrix.html index db506954e..9bdd4900d 100644 --- a/docs/reference/proximity_matrix.html +++ b/docs/reference/proximity_matrix.html @@ -1,102 +1,19 @@ - - - - - - - -Proximity matrix — proximity_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Proximity matrix — proximity_matrix • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,143 +103,132 @@

    Proximity matrix

    spatial proximity to each other.

    -
    proximity_matrix(x, distance)
    +    
    Usage,
    proximity_matrix(x, distance)
     
    -# S3 method for Raster
    -proximity_matrix(x, distance)
    +# S3 method for Raster
    +proximity_matrix(x, distance)
     
    -# S3 method for SpatialPolygons
    -proximity_matrix(x, distance)
    +# S3 method for SpatialPolygons
    +proximity_matrix(x, distance)
     
    -# S3 method for SpatialLines
    -proximity_matrix(x, distance)
    +# S3 method for SpatialLines
    +proximity_matrix(x, distance)
     
    -# S3 method for SpatialPoints
    -proximity_matrix(x, distance)
    +# S3 method for SpatialPoints
    +proximity_matrix(x, distance)
     
    -# S3 method for sf
    -proximity_matrix(x, distance)
    +# S3 method for sf
    +proximity_matrix(x, distance)
     
    -# S3 method for default
    -proximity_matrix(x, distance)
    +# S3 method for default +proximity_matrix(x, distance)
    -

    Arguments

    - - - - - - - - - - -
    x

    Raster, -Spatial, or sf::sf() object -representing planning units.

    distance

    numeric distance threshold. Planning units +

    +

    Arguments

    +
    x
    +

    Raster, +Spatial, or sf::sf() object +representing planning units.

    +
    distance
    +

    numeric distance threshold. Planning units that are further apart from each other than this threshold are -not treated as being within proximity of each other.

    - -

    Value

    - - - - -

    dsCMatrix symmetric sparse matrix object. +not treated as being within proximity of each other.

    +
    +
    +

    Value

    +

    dsCMatrix symmetric sparse matrix object. Each row and column represents a planning unit. Cells values indicate if the pair-wise distances between different planning units are within the distance threshold or not (using ones and zeros). To reduce computational burden, cells among the matrix diagonal are set to zero. Furthermore, if the argument to x is a -Raster object, then cells with NA -values are set to zero too.

    -

    Details

    - +Raster object, then cells with NAvalues are set to zero too.

    +
    +
    +

    Details

    Proximity calculations are performed using -sf::st_is_within_distance().

    +sf::st_is_within_distance().

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_sf, sim_pu_lines, sim_pu_points)
    -
    -# create proximity matrix using raster data
    -## crop raster to 9 cells to provide a small example
    -r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    -
    -## make proximity matrix using a distance threshold of 2
    -cm_raster <- proximity_matrix(r, distance = 2)
    -
    -# create proximity matrix using polygon (sf) data
    -## subset 9 polygons to provide a small example
    -ply <- sim_pu_sf[c(1:2, 10:12, 20:22), ]
    -
    -## make proximity matrix using a distance threshold of 2
    -cm_ply <- proximity_matrix(ply, distance = 2)
    -
    -# create proximity matrix using line (Spatial) data
    -## subset 9 lines to provide a small example
    -lns <- sim_pu_lines[c(1:2, 10:12, 20:22), ]
    -
    -## make proximity matrix
    -cm_lns <- proximity_matrix(lns, distance = 2)
    -
    -## create proximity matrix using point (Spatial) data
    -## subset 9 points to provide a small example
    -pts <- sim_pu_points[c(1:2, 10:12, 20:22), ]
    -
    -# make proximity matrix
    -cm_pts <- proximity_matrix(pts, distance = 2)
    -
    -# plot data and the proximity matrices
    -# \dontrun{
    -par(mfrow = c(4,2))
    -
    -## plot raster and proximity matrix
    -plot(r, main = "raster", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(cm_raster)), main = "proximity matrix", axes = FALSE,
    -     box = FALSE)
    -
    -## plot polygons and proximity matrix
    -plot(r, main = "polygons (sf)", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(cm_ply)), main = "proximity matrix", axes = FALSE,
    -    box = FALSE)
    -
    -## plot lines and proximity matrix
    -plot(r, main = "lines (Spatial)", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(cm_lns)), main = "proximity matrix", axes = FALSE,
    -     box = FALSE)
    -
    -## plot points and proximity matrix
    -plot(r, main = "points (Spatial)", axes = FALSE, box = FALSE)
    -plot(raster(as.matrix(cm_pts)), main = "proximity matrix", axes = FALSE,
    -     box = FALSE)
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_sf, sim_pu_lines, sim_pu_points)
    +
    +# create proximity matrix using raster data
    +## crop raster to 9 cells to provide a small example
    +r <- crop(sim_pu_raster, c(0, 0.3, 0, 0.3))
    +
    +## make proximity matrix using a distance threshold of 2
    +cm_raster <- proximity_matrix(r, distance = 2)
    +
    +# create proximity matrix using polygon (sf) data
    +## subset 9 polygons to provide a small example
    +ply <- sim_pu_sf[c(1:2, 10:12, 20:22), ]
    +
    +## make proximity matrix using a distance threshold of 2
    +cm_ply <- proximity_matrix(ply, distance = 2)
    +
    +# create proximity matrix using line (Spatial) data
    +## subset 9 lines to provide a small example
    +lns <- sim_pu_lines[c(1:2, 10:12, 20:22), ]
    +
    +## make proximity matrix
    +cm_lns <- proximity_matrix(lns, distance = 2)
    +
    +## create proximity matrix using point (Spatial) data
    +## subset 9 points to provide a small example
    +pts <- sim_pu_points[c(1:2, 10:12, 20:22), ]
    +
    +# make proximity matrix
    +cm_pts <- proximity_matrix(pts, distance = 2)
    +
    +# plot data and the proximity matrices
    +# \dontrun{
    +par(mfrow = c(4,2))
    +
    +## plot raster and proximity matrix
    +plot(r, main = "raster", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(cm_raster)), main = "proximity matrix", axes = FALSE,
    +     box = FALSE)
    +
    +## plot polygons and proximity matrix
    +plot(r, main = "polygons (sf)", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(cm_ply)), main = "proximity matrix", axes = FALSE,
    +    box = FALSE)
    +
    +## plot lines and proximity matrix
    +plot(r, main = "lines (Spatial)", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(cm_lns)), main = "proximity matrix", axes = FALSE,
    +     box = FALSE)
    +
    +## plot points and proximity matrix
    +plot(r, main = "points (Spatial)", axes = FALSE, box = FALSE)
    +plot(raster(as.matrix(cm_pts)), main = "proximity matrix", axes = FALSE,
    +     box = FALSE)
    +
    +# }
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/rij_matrix.html b/docs/reference/rij_matrix.html index 37696006b..d41bcb6b9 100644 --- a/docs/reference/rij_matrix.html +++ b/docs/reference/rij_matrix.html @@ -1,102 +1,19 @@ - - - - - - - -Feature by planning unit matrix — rij_matrix • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Feature by planning unit matrix — rij_matrix • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,421 +103,410 @@

    Feature by planning unit matrix

    unit (also known as an rij matrix).

    -
    rij_matrix(x, y, ...)
    -
    -# S4 method for Raster,Raster
    -rij_matrix(x, y, ...)
    -
    -# S4 method for Spatial,Raster
    -rij_matrix(x, y, fun, ...)
    -
    -# S4 method for sf,Raster
    -rij_matrix(x, y, fun, ...)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    x

    Raster, -Spatial, or sf::sf() object -representing the planning units.

    y

    Raster object representing the -features.

    ...

    not used.

    fun

    character for summarizing values inside each planning unit. +

    Usage,
    rij_matrix(x, y, ...)
    +
    +# S4 method for Raster,Raster
    +rij_matrix(x, y, ...)
    +
    +# S4 method for Spatial,Raster
    +rij_matrix(x, y, fun, ...)
    +
    +# S4 method for sf,Raster
    +rij_matrix(x, y, fun, ...)
    + +
    +

    Arguments

    +
    x
    +

    Raster, +Spatial, or sf::sf() object +representing the planning units.

    +
    y
    +

    Raster object representing the +features.

    +
    ...
    +

    not used.

    +
    fun
    +

    character for summarizing values inside each planning unit. This parameter is only used when the argument to x is a -Spatial or sf::sf() object. -Defaults to "sum".

    - -

    Value

    - -

    dgCMatrix sparse matrix object. +Spatial or sf::sf() object. +Defaults to "sum".

    +
    +
    +

    Value

    +

    dgCMatrix sparse matrix object. The sparse matrix represents the spatial intersection between the planning units and the features. Rows correspond to features, and columns correspond to planning units. Values correspond to the amount (or presence/absence) of the feature in the planning unit. For example, the amount of the third species in the second planning unit would be stored in the third column and second row.

    -

    Details

    - -

    Generally, processing vector (i.e. Spatial or -sf::sf()) data takes much -longer to process then Raster data, -so it is recommended to use Raster data +

    +
    +

    Details

    +

    Generally, processing vector (i.e., Spatial or +sf::sf()) data takes much +longer to process then Raster data, +so it is recommended to use Raster data for planning units where possible.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_pu_polygons, sim_pu_sf, sim_pu_zones_stack)
    -
    -# create rij matrix using raster layer planning units
    -rij_raster <- rij_matrix(sim_pu_raster, sim_features)
    -print(rij_raster)
    -#> 5 x 90 sparse Matrix of class "dgCMatrix"
    -#>                                                                              
    -#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7017231
    -#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4084749
    -#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.6046204
    -#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5292249
    -#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5068509
    -#>                                                                              
    -#> layer.1 0.7219284 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627
    -#> layer.2 0.4142271 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791
    -#> layer.3 0.5411391 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503
    -#> layer.4 0.5479991 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241
    -#> layer.5 0.5510725 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185
    -#>                                                                              
    -#> layer.1 0.7147517 0.7212831 0.7343615 0.7516415 0.7699543 0.7897989 0.7772875
    -#> layer.2 0.3615030 0.3765606 0.3841340 0.3836641 0.3759393 0.2716298 0.2901825
    -#> layer.3 0.7345400 0.6860553 0.6288231 0.5654560 0.5000330 0.8346555 0.8298830
    -#> layer.4 0.4618047 0.5148344 0.5541170 0.5767631 0.5815260 0.2249796 0.2776009
    -#> layer.5 0.4517499 0.4732935 0.5068285 0.5495481 0.5965013 0.5022737 0.4802429
    -#>                                                                              
    -#> layer.1 0.7661407 0.7585159 0.7561971 0.7599327 0.7690602 0.7816119 0.7948577
    -#> layer.2 0.3119889 0.3334326 0.3508138 0.3612252 0.3631965 0.3568911 0.3438730
    -#> layer.3 0.8151305 0.7903745 0.7553823 0.7102172 0.6558247 0.5945687 0.5304188
    -#> layer.4 0.3390011 0.4047008 0.4684589 0.5238040 0.5655910 0.5906737 0.5976491
    -#> layer.5 0.4646212 0.4587578 0.4649525 0.4840959 0.5153215 0.5556964 0.6003282
    -#>                                                                              
    -#> layer.1 0.8059966 0.8286461 0.8189872 0.8095434 0.8020377 0.7980313 0.7983424
    -#> layer.2 0.3265679 0.2650109 0.2854743 0.3077757 0.3280176 0.3424767 0.3485511
    -#> layer.3 0.4684656 0.8381119 0.8367193 0.8254115 0.8043530 0.7733527 0.7323690
    -#> layer.4 0.5863706 0.2330116 0.2831343 0.3416658 0.4047077 0.4665769 0.5210694
    -#> layer.5 0.6432595 0.5353515 0.5095593 0.4907792 0.4822721 0.4860960 0.5028092
    -#>                                                                              
    -#> layer.1 0.8026563 0.8095316 0.8167839 0.8220091 0.8633036 0.8564520 0.8487799
    -#> layer.2 0.3453627 0.3338433 0.3163452 0.2959188 0.2603208 0.2824472 0.3051395
    -#> layer.3 0.6821137 0.6246373 0.5636095 0.5039577 0.8370439 0.8390267 0.8310691
    -#> layer.4 0.5629582 0.5888172 0.5969403 0.5869119 0.2456164 0.2910969 0.3438600
    -#> layer.5 0.5312405 0.5683321 0.6093528 0.6487382 0.5700379 0.5418938 0.5211593
    -#>                                                                              
    -#> layer.1 0.8415392 0.8360026 0.8325552 0.8337363 0.8339533 0.8912849 0.8867860
    -#> layer.2 0.3242198 0.3359588 0.3303278 0.3143945 0.2708325 0.2575975 0.2809245
    -#> layer.3 0.8136049 0.7866027 0.7045769 0.6519655 0.5398928 0.8305082 0.8359634
    -#> layer.4 0.4008068 0.4572360 0.5471036 0.5718452 0.5700792 0.2632826 0.3020677
    -#> layer.5 0.5108757 0.5127704 0.5521633 0.5850800 0.6559584 0.6024207 0.5734186
    -#>                                                                              
    -#> layer.1 0.8807106 0.8738915 0.8672825 0.8616340 0.8571536 0.8533099 0.8488533
    -#> layer.2 0.3037493 0.3216412 0.3308881 0.3295497 0.3179543 0.2985197 0.2749763
    -#> layer.3 0.8312740 0.8171942 0.7939453 0.7616869 0.7210738 0.6737960 0.6228802
    -#> layer.4 0.3465595 0.3945501 0.4425012 0.4859430 0.5203217 0.5418532 0.5479865
    -#> layer.5 0.5520171 0.5408934 0.5413574 0.5531961 0.5746993 0.6028225 0.6335755
    -#>                                                                              
    -#> layer.1 0.8419808 0.9117805 0.9090526 0.9042812 0.8979800 0.8833313 0.8758136
    -#> layer.2 0.2512416 0.2570026 0.2809260 0.3035274 0.3201754 0.3229680 0.3083414
    -#> layer.3 0.5724977 0.8175488 0.8267526 0.8253873 0.8145533 0.7665224 0.7305762
    -#> layer.4 0.5375278 0.2860727 0.3163391 0.3506471 0.3876575 0.4590604 0.4860971
    -#> layer.5 0.6626552 0.6287730 0.6001593 0.5792111 0.5681102 0.5773113 0.5951498
    -#>                                                                              
    -#> layer.1 0.8679237 0.8586633 0.8463772 0.9251764 0.9199524 0.9141710 0.9067838
    -#> layer.2 0.2863445 0.2611997 0.2370352 0.2589605 0.3047683 0.3200938 0.3251705
    -#> layer.3 0.6886562 0.6435894 0.5991429 0.7972733 0.8129275 0.8053968 0.7890073
    -#> layer.4 0.5023710 0.5050812 0.4927354 0.3133764 0.3566663 0.3816158 0.4070643
    -#> layer.5 0.6183284 0.6433893 0.6667633 0.6461805 0.5990580 0.5886663 0.5878728
    -#>                                                                              
    -#> layer.1 0.8981802 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9159813
    -#> layer.2 0.3186414 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3253428
    -#> layer.3 0.7645150 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7768533
    -#> layer.4 0.4304725 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3908502
    -#> layer.5 0.5957192 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5991743
    -#>                                                                              
    -#> layer.1 0.9066665 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562 0.9315392
    -#> layer.2 0.3170430 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441 0.3143908
    -#> layer.3 0.7561399 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352 0.7677462
    -#> layer.4 0.4031866 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479 0.3741982
    -#> layer.5 0.6058227 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690 0.6078230
    -#>                                                                    
    -#> layer.1 0.9265291 0.9190510 0.8968893 0.8817701 0.8629508 0.8388748
    -#> layer.2 0.3265712 0.3282949 0.2992975 0.2742826 0.2483226 0.2255383
    -#> layer.3 0.7679649 0.7589920 0.7195554 0.6927858 0.6644491 0.6371886
    -#> layer.4 0.3753241 0.3774461 0.3785580 0.3729116 0.3601426 0.3391285
    -#> layer.5 0.6002171 0.6002851 0.6166497 0.6288149 0.6410043 0.6515710
    -
    -# create rij matrix using polygon (Spatial) planning units
    -rij_polygons <- rij_matrix(sim_pu_polygons, sim_features)
    -print(rij_polygons)
    -#> 5 x 90 sparse Matrix of class "dgCMatrix"
    -#>                                                                              
    -#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7017231
    -#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4084749
    -#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.6046204
    -#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5292249
    -#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5068509
    -#>                                                                              
    -#> layer.1 0.7219284 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627
    -#> layer.2 0.4142271 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791
    -#> layer.3 0.5411391 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503
    -#> layer.4 0.5479991 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241
    -#> layer.5 0.5510725 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185
    -#>                                                                              
    -#> layer.1 0.7147517 0.7212831 0.7343615 0.7516415 0.7699543 0.7897989 0.7772875
    -#> layer.2 0.3615030 0.3765606 0.3841340 0.3836641 0.3759393 0.2716298 0.2901825
    -#> layer.3 0.7345400 0.6860553 0.6288231 0.5654560 0.5000330 0.8346555 0.8298830
    -#> layer.4 0.4618047 0.5148344 0.5541170 0.5767631 0.5815260 0.2249796 0.2776009
    -#> layer.5 0.4517499 0.4732935 0.5068285 0.5495481 0.5965013 0.5022737 0.4802429
    -#>                                                                              
    -#> layer.1 0.7661407 0.7585159 0.7561971 0.7599327 0.7690602 0.7816119 0.7948577
    -#> layer.2 0.3119889 0.3334326 0.3508138 0.3612252 0.3631965 0.3568911 0.3438730
    -#> layer.3 0.8151305 0.7903745 0.7553823 0.7102172 0.6558247 0.5945687 0.5304188
    -#> layer.4 0.3390011 0.4047008 0.4684589 0.5238040 0.5655910 0.5906737 0.5976491
    -#> layer.5 0.4646212 0.4587578 0.4649525 0.4840959 0.5153215 0.5556964 0.6003282
    -#>                                                                              
    -#> layer.1 0.8059966 0.8286461 0.8189872 0.8095434 0.8020377 0.7980313 0.7983424
    -#> layer.2 0.3265679 0.2650109 0.2854743 0.3077757 0.3280176 0.3424767 0.3485511
    -#> layer.3 0.4684656 0.8381119 0.8367193 0.8254115 0.8043530 0.7733527 0.7323690
    -#> layer.4 0.5863706 0.2330116 0.2831343 0.3416658 0.4047077 0.4665769 0.5210694
    -#> layer.5 0.6432595 0.5353515 0.5095593 0.4907792 0.4822721 0.4860960 0.5028092
    -#>                                                                              
    -#> layer.1 0.8026563 0.8095316 0.8167839 0.8220091 0.8633036 0.8564520 0.8487799
    -#> layer.2 0.3453627 0.3338433 0.3163452 0.2959188 0.2603208 0.2824472 0.3051395
    -#> layer.3 0.6821137 0.6246373 0.5636095 0.5039577 0.8370439 0.8390267 0.8310691
    -#> layer.4 0.5629582 0.5888172 0.5969403 0.5869119 0.2456164 0.2910969 0.3438600
    -#> layer.5 0.5312405 0.5683321 0.6093528 0.6487382 0.5700379 0.5418938 0.5211593
    -#>                                                                              
    -#> layer.1 0.8415392 0.8360026 0.8325552 0.8337363 0.8339533 0.8912849 0.8867860
    -#> layer.2 0.3242198 0.3359588 0.3303278 0.3143945 0.2708325 0.2575975 0.2809245
    -#> layer.3 0.8136049 0.7866027 0.7045769 0.6519655 0.5398928 0.8305082 0.8359634
    -#> layer.4 0.4008068 0.4572360 0.5471036 0.5718452 0.5700792 0.2632826 0.3020677
    -#> layer.5 0.5108757 0.5127704 0.5521633 0.5850800 0.6559584 0.6024207 0.5734186
    -#>                                                                              
    -#> layer.1 0.8807106 0.8738915 0.8672825 0.8616340 0.8571536 0.8533099 0.8488533
    -#> layer.2 0.3037493 0.3216412 0.3308881 0.3295497 0.3179543 0.2985197 0.2749763
    -#> layer.3 0.8312740 0.8171942 0.7939453 0.7616869 0.7210738 0.6737960 0.6228802
    -#> layer.4 0.3465595 0.3945501 0.4425012 0.4859430 0.5203217 0.5418532 0.5479865
    -#> layer.5 0.5520171 0.5408934 0.5413574 0.5531961 0.5746993 0.6028225 0.6335755
    -#>                                                                              
    -#> layer.1 0.8419808 0.9117805 0.9090526 0.9042812 0.8979800 0.8833313 0.8758136
    -#> layer.2 0.2512416 0.2570026 0.2809260 0.3035274 0.3201754 0.3229680 0.3083414
    -#> layer.3 0.5724977 0.8175488 0.8267526 0.8253873 0.8145533 0.7665224 0.7305762
    -#> layer.4 0.5375278 0.2860727 0.3163391 0.3506471 0.3876575 0.4590604 0.4860971
    -#> layer.5 0.6626552 0.6287730 0.6001593 0.5792111 0.5681102 0.5773113 0.5951498
    -#>                                                                              
    -#> layer.1 0.8679237 0.8586633 0.8463772 0.9251764 0.9199524 0.9141710 0.9067838
    -#> layer.2 0.2863445 0.2611997 0.2370352 0.2589605 0.3047683 0.3200938 0.3251705
    -#> layer.3 0.6886562 0.6435894 0.5991429 0.7972733 0.8129275 0.8053968 0.7890073
    -#> layer.4 0.5023710 0.5050812 0.4927354 0.3133764 0.3566663 0.3816158 0.4070643
    -#> layer.5 0.6183284 0.6433893 0.6667633 0.6461805 0.5990580 0.5886663 0.5878728
    -#>                                                                              
    -#> layer.1 0.8981802 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9159813
    -#> layer.2 0.3186414 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3253428
    -#> layer.3 0.7645150 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7768533
    -#> layer.4 0.4304725 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3908502
    -#> layer.5 0.5957192 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5991743
    -#>                                                                              
    -#> layer.1 0.9066665 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562 0.9315392
    -#> layer.2 0.3170430 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441 0.3143908
    -#> layer.3 0.7561399 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352 0.7677462
    -#> layer.4 0.4031866 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479 0.3741982
    -#> layer.5 0.6058227 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690 0.6078230
    -#>                                                                    
    -#> layer.1 0.9265291 0.9190510 0.8968893 0.8817701 0.8629508 0.8388748
    -#> layer.2 0.3265712 0.3282949 0.2992975 0.2742826 0.2483226 0.2255383
    -#> layer.3 0.7679649 0.7589920 0.7195554 0.6927858 0.6644491 0.6371886
    -#> layer.4 0.3753241 0.3774461 0.3785580 0.3729116 0.3601426 0.3391285
    -#> layer.5 0.6002171 0.6002851 0.6166497 0.6288149 0.6410043 0.6515710
    -
    -# create rij matrix using polygon (sf) planning units
    -rij_sf <- rij_matrix(sim_pu_sf, sim_features)
    -print(rij_sf)
    -#> 5 x 90 sparse Matrix of class "dgCMatrix"
    -#>                                                                              
    -#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7017231
    -#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4084749
    -#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.6046204
    -#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5292249
    -#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5068509
    -#>                                                                              
    -#> layer.1 0.7219284 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627
    -#> layer.2 0.4142271 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791
    -#> layer.3 0.5411391 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503
    -#> layer.4 0.5479991 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241
    -#> layer.5 0.5510725 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185
    -#>                                                                              
    -#> layer.1 0.7147517 0.7212831 0.7343615 0.7516415 0.7699543 0.7897989 0.7772875
    -#> layer.2 0.3615030 0.3765606 0.3841340 0.3836641 0.3759393 0.2716298 0.2901825
    -#> layer.3 0.7345400 0.6860553 0.6288231 0.5654560 0.5000330 0.8346555 0.8298830
    -#> layer.4 0.4618047 0.5148344 0.5541170 0.5767631 0.5815260 0.2249796 0.2776009
    -#> layer.5 0.4517499 0.4732935 0.5068285 0.5495481 0.5965013 0.5022737 0.4802429
    -#>                                                                              
    -#> layer.1 0.7661407 0.7585159 0.7561971 0.7599327 0.7690602 0.7816119 0.7948577
    -#> layer.2 0.3119889 0.3334326 0.3508138 0.3612252 0.3631965 0.3568911 0.3438730
    -#> layer.3 0.8151305 0.7903745 0.7553823 0.7102172 0.6558247 0.5945687 0.5304188
    -#> layer.4 0.3390011 0.4047008 0.4684589 0.5238040 0.5655910 0.5906737 0.5976491
    -#> layer.5 0.4646212 0.4587578 0.4649525 0.4840959 0.5153215 0.5556964 0.6003282
    -#>                                                                              
    -#> layer.1 0.8059966 0.8286461 0.8189872 0.8095434 0.8020377 0.7980313 0.7983424
    -#> layer.2 0.3265679 0.2650109 0.2854743 0.3077757 0.3280176 0.3424767 0.3485511
    -#> layer.3 0.4684656 0.8381119 0.8367193 0.8254115 0.8043530 0.7733527 0.7323690
    -#> layer.4 0.5863706 0.2330116 0.2831343 0.3416658 0.4047077 0.4665769 0.5210694
    -#> layer.5 0.6432595 0.5353515 0.5095593 0.4907792 0.4822721 0.4860960 0.5028092
    -#>                                                                              
    -#> layer.1 0.8026563 0.8095316 0.8167839 0.8220091 0.8633036 0.8564520 0.8487799
    -#> layer.2 0.3453627 0.3338433 0.3163452 0.2959188 0.2603208 0.2824472 0.3051395
    -#> layer.3 0.6821137 0.6246373 0.5636095 0.5039577 0.8370439 0.8390267 0.8310691
    -#> layer.4 0.5629582 0.5888172 0.5969403 0.5869119 0.2456164 0.2910969 0.3438600
    -#> layer.5 0.5312405 0.5683321 0.6093528 0.6487382 0.5700379 0.5418938 0.5211593
    -#>                                                                              
    -#> layer.1 0.8415392 0.8360026 0.8325552 0.8337363 0.8339533 0.8912849 0.8867860
    -#> layer.2 0.3242198 0.3359588 0.3303278 0.3143945 0.2708325 0.2575975 0.2809245
    -#> layer.3 0.8136049 0.7866027 0.7045769 0.6519655 0.5398928 0.8305082 0.8359634
    -#> layer.4 0.4008068 0.4572360 0.5471036 0.5718452 0.5700792 0.2632826 0.3020677
    -#> layer.5 0.5108757 0.5127704 0.5521633 0.5850800 0.6559584 0.6024207 0.5734186
    -#>                                                                              
    -#> layer.1 0.8807106 0.8738915 0.8672825 0.8616340 0.8571536 0.8533099 0.8488533
    -#> layer.2 0.3037493 0.3216412 0.3308881 0.3295497 0.3179543 0.2985197 0.2749763
    -#> layer.3 0.8312740 0.8171942 0.7939453 0.7616869 0.7210738 0.6737960 0.6228802
    -#> layer.4 0.3465595 0.3945501 0.4425012 0.4859430 0.5203217 0.5418532 0.5479865
    -#> layer.5 0.5520171 0.5408934 0.5413574 0.5531961 0.5746993 0.6028225 0.6335755
    -#>                                                                              
    -#> layer.1 0.8419808 0.9117805 0.9090526 0.9042812 0.8979800 0.8833313 0.8758136
    -#> layer.2 0.2512416 0.2570026 0.2809260 0.3035274 0.3201754 0.3229680 0.3083414
    -#> layer.3 0.5724977 0.8175488 0.8267526 0.8253873 0.8145533 0.7665224 0.7305762
    -#> layer.4 0.5375278 0.2860727 0.3163391 0.3506471 0.3876575 0.4590604 0.4860971
    -#> layer.5 0.6626552 0.6287730 0.6001593 0.5792111 0.5681102 0.5773113 0.5951498
    -#>                                                                              
    -#> layer.1 0.8679237 0.8586633 0.8463772 0.9251764 0.9199524 0.9141710 0.9067838
    -#> layer.2 0.2863445 0.2611997 0.2370352 0.2589605 0.3047683 0.3200938 0.3251705
    -#> layer.3 0.6886562 0.6435894 0.5991429 0.7972733 0.8129275 0.8053968 0.7890073
    -#> layer.4 0.5023710 0.5050812 0.4927354 0.3133764 0.3566663 0.3816158 0.4070643
    -#> layer.5 0.6183284 0.6433893 0.6667633 0.6461805 0.5990580 0.5886663 0.5878728
    -#>                                                                              
    -#> layer.1 0.8981802 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9159813
    -#> layer.2 0.3186414 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3253428
    -#> layer.3 0.7645150 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7768533
    -#> layer.4 0.4304725 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3908502
    -#> layer.5 0.5957192 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5991743
    -#>                                                                              
    -#> layer.1 0.9066665 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562 0.9315392
    -#> layer.2 0.3170430 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441 0.3143908
    -#> layer.3 0.7561399 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352 0.7677462
    -#> layer.4 0.4031866 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479 0.3741982
    -#> layer.5 0.6058227 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690 0.6078230
    -#>                                                                    
    -#> layer.1 0.9265291 0.9190510 0.8968893 0.8817701 0.8629508 0.8388748
    -#> layer.2 0.3265712 0.3282949 0.2992975 0.2742826 0.2483226 0.2255383
    -#> layer.3 0.7679649 0.7589920 0.7195554 0.6927858 0.6644491 0.6371886
    -#> layer.4 0.3753241 0.3774461 0.3785580 0.3729116 0.3601426 0.3391285
    -#> layer.5 0.6002171 0.6002851 0.6166497 0.6288149 0.6410043 0.6515710
    -
    -# create rij matrix using raster stack planning units
    -rij_zones_raster <- rij_matrix(sim_pu_zones_stack, sim_features)
    -print(rij_zones_raster)
    -#> 5 x 90 sparse Matrix of class "dgCMatrix"
    -#>                                                                              
    -#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7219284
    -#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4142271
    -#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.5411391
    -#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5479991
    -#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5510725
    -#>                                                                              
    -#> layer.1 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627 0.7147517
    -#> layer.2 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791 0.3615030
    -#> layer.3 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503 0.7345400
    -#> layer.4 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241 0.4618047
    -#> layer.5 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185 0.4517499
    -#>                                                                              
    -#> layer.1 0.7212831 0.7343615 0.7699543 0.7861227 0.7897989 0.7772875 0.7561971
    -#> layer.2 0.3765606 0.3841340 0.3759393 0.3627013 0.2716298 0.2901825 0.3508138
    -#> layer.3 0.6860553 0.6288231 0.5000330 0.4375452 0.8346555 0.8298830 0.7553823
    -#> layer.4 0.5148344 0.5541170 0.5815260 0.5684388 0.2249796 0.2776009 0.4684589
    -#> layer.5 0.4732935 0.5068285 0.5965013 0.6415349 0.5022737 0.4802429 0.4649525
    -#>                                                                              
    -#> layer.1 0.7599327 0.7690602 0.7816119 0.7948577 0.8059966 0.8286461 0.8189872
    -#> layer.2 0.3612252 0.3631965 0.3568911 0.3438730 0.3265679 0.2650109 0.2854743
    -#> layer.3 0.7102172 0.6558247 0.5945687 0.5304188 0.4684656 0.8381119 0.8367193
    -#> layer.4 0.5238040 0.5655910 0.5906737 0.5976491 0.5863706 0.2330116 0.2831343
    -#> layer.5 0.4840959 0.5153215 0.5556964 0.6003282 0.6432595 0.5353515 0.5095593
    -#>                                                                              
    -#> layer.1 0.8095434 0.8020377 0.7980313 0.7983424 0.8026563 0.8095316 0.8167839
    -#> layer.2 0.3077757 0.3280176 0.3424767 0.3485511 0.3453627 0.3338433 0.3163452
    -#> layer.3 0.8254115 0.8043530 0.7733527 0.7323690 0.6821137 0.6246373 0.5636095
    -#> layer.4 0.3416658 0.4047077 0.4665769 0.5210694 0.5629582 0.5888172 0.5969403
    -#> layer.5 0.4907792 0.4822721 0.4860960 0.5028092 0.5312405 0.5683321 0.6093528
    -#>                                                                              
    -#> layer.1 0.8220091 0.8633036 0.8564520 0.8487799 0.8360026 0.8330038 0.8337363
    -#> layer.2 0.2959188 0.2603208 0.2824472 0.3051395 0.3359588 0.3380832 0.3143945
    -#> layer.3 0.5039577 0.8370439 0.8390267 0.8310691 0.7866027 0.7500573 0.6519655
    -#> layer.4 0.5869119 0.2456164 0.2910969 0.3438600 0.4572360 0.5076754 0.5718452
    -#> layer.5 0.6487382 0.5700379 0.5418938 0.5211593 0.5127704 0.5270232 0.5850800
    -#>                                                                              
    -#> layer.1 0.8348882 0.8339533 0.8912849 0.8867860 0.8807106 0.8738915 0.8672825
    -#> layer.2 0.2933767 0.2708325 0.2575975 0.2809245 0.3037493 0.3216412 0.3308881
    -#> layer.3 0.5955431 0.5398928 0.8305082 0.8359634 0.8312740 0.8171942 0.7939453
    -#> layer.4 0.5797670 0.5700792 0.2632826 0.3020677 0.3465595 0.3945501 0.4425012
    -#> layer.5 0.6213430 0.6559584 0.6024207 0.5734186 0.5520171 0.5408934 0.5413574
    -#>                                                                              
    -#> layer.1 0.8616340 0.8571536 0.8488533 0.9117805 0.9090526 0.9042812 0.8979800
    -#> layer.2 0.3295497 0.3179543 0.2749763 0.2570026 0.2809260 0.3035274 0.3201754
    -#> layer.3 0.7616869 0.7210738 0.6228802 0.8175488 0.8267526 0.8253873 0.8145533
    -#> layer.4 0.4859430 0.5203217 0.5479865 0.2860727 0.3163391 0.3506471 0.3876575
    -#> layer.5 0.5531961 0.5746993 0.6335755 0.6287730 0.6001593 0.5792111 0.5681102
    -#>                                                                              
    -#> layer.1 0.8908018 0.8833313 0.8758136 0.8679237 0.8586633 0.8463772 0.9251764
    -#> layer.2 0.3271982 0.3229680 0.3083414 0.2863445 0.2611997 0.2370352 0.2589605
    -#> layer.3 0.7947903 0.7665224 0.7305762 0.6886562 0.6435894 0.5991429 0.7972733
    -#> layer.4 0.4249398 0.4590604 0.4860971 0.5023710 0.5050812 0.4927354 0.3133764
    -#> layer.5 0.5676758 0.5773113 0.5951498 0.6183284 0.6433893 0.6667633 0.6461805
    -#>                                                                              
    -#> layer.1 0.9237208 0.9199524 0.9141710 0.9067838 0.8981802 0.8885294 0.8775561
    -#> layer.2 0.2828033 0.3047683 0.3200938 0.3251705 0.3186414 0.3017897 0.2781030
    -#> layer.3 0.8107003 0.8129275 0.8053968 0.7890073 0.7645150 0.7330004 0.6962687
    -#> layer.4 0.3337335 0.3566663 0.3816158 0.4070643 0.4304725 0.4485815 0.4580145
    -#> layer.5 0.6187615 0.5990580 0.5886663 0.5878728 0.5957192 0.6102524 0.6288607
    -#>                                                                              
    -#> layer.1 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9234146 0.9159813
    -#> layer.2 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3219892 0.3253428
    -#> layer.3 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7897301 0.7768533
    -#> layer.4 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3773969 0.3908502
    -#> layer.5 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5997976 0.5991743
    -#>                                                                              
    -#> layer.1 0.9066665 0.8955188 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562
    -#> layer.2 0.3170430 0.2986799 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441
    -#> layer.3 0.7561399 0.7289606 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352
    -#> layer.4 0.4031866 0.4117466 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479
    -#> layer.5 0.6058227 0.6177441 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690
    -#>                                                                    
    -#> layer.1 0.9315392 0.9190510 0.9091912 0.8968893 0.8629508 0.8388748
    -#> layer.2 0.3143908 0.3282949 0.3186099 0.2992975 0.2483226 0.2255383
    -#> layer.3 0.7677462 0.7589920 0.7423054 0.7195554 0.6644491 0.6371886
    -#> layer.4 0.3741982 0.3774461 0.3792402 0.3785580 0.3601426 0.3391285
    -#> layer.5 0.6078230 0.6002851 0.6064383 0.6166497 0.6410043 0.6515710
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_pu_polygons, sim_pu_sf, sim_pu_zones_stack)
    +
    +# create rij matrix using raster layer planning units
    +rij_raster <- rij_matrix(sim_pu_raster, sim_features)
    +print(rij_raster)
    +#> 5 x 90 sparse Matrix of class "dgCMatrix"
    +#>                                                                              
    +#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7017231
    +#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4084749
    +#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.6046204
    +#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5292249
    +#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5068509
    +#>                                                                              
    +#> layer.1 0.7219284 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627
    +#> layer.2 0.4142271 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791
    +#> layer.3 0.5411391 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503
    +#> layer.4 0.5479991 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241
    +#> layer.5 0.5510725 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185
    +#>                                                                              
    +#> layer.1 0.7147517 0.7212831 0.7343615 0.7516415 0.7699543 0.7897989 0.7772875
    +#> layer.2 0.3615030 0.3765606 0.3841340 0.3836641 0.3759393 0.2716298 0.2901825
    +#> layer.3 0.7345400 0.6860553 0.6288231 0.5654560 0.5000330 0.8346555 0.8298830
    +#> layer.4 0.4618047 0.5148344 0.5541170 0.5767631 0.5815260 0.2249796 0.2776009
    +#> layer.5 0.4517499 0.4732935 0.5068285 0.5495481 0.5965013 0.5022737 0.4802429
    +#>                                                                              
    +#> layer.1 0.7661407 0.7585159 0.7561971 0.7599327 0.7690602 0.7816119 0.7948577
    +#> layer.2 0.3119889 0.3334326 0.3508138 0.3612252 0.3631965 0.3568911 0.3438730
    +#> layer.3 0.8151305 0.7903745 0.7553823 0.7102172 0.6558247 0.5945687 0.5304188
    +#> layer.4 0.3390011 0.4047008 0.4684589 0.5238040 0.5655910 0.5906737 0.5976491
    +#> layer.5 0.4646212 0.4587578 0.4649525 0.4840959 0.5153215 0.5556964 0.6003282
    +#>                                                                              
    +#> layer.1 0.8059966 0.8286461 0.8189872 0.8095434 0.8020377 0.7980313 0.7983424
    +#> layer.2 0.3265679 0.2650109 0.2854743 0.3077757 0.3280176 0.3424767 0.3485511
    +#> layer.3 0.4684656 0.8381119 0.8367193 0.8254115 0.8043530 0.7733527 0.7323690
    +#> layer.4 0.5863706 0.2330116 0.2831343 0.3416658 0.4047077 0.4665769 0.5210694
    +#> layer.5 0.6432595 0.5353515 0.5095593 0.4907792 0.4822721 0.4860960 0.5028092
    +#>                                                                              
    +#> layer.1 0.8026563 0.8095316 0.8167839 0.8220091 0.8633036 0.8564520 0.8487799
    +#> layer.2 0.3453627 0.3338433 0.3163452 0.2959188 0.2603208 0.2824472 0.3051395
    +#> layer.3 0.6821137 0.6246373 0.5636095 0.5039577 0.8370439 0.8390267 0.8310691
    +#> layer.4 0.5629582 0.5888172 0.5969403 0.5869119 0.2456164 0.2910969 0.3438600
    +#> layer.5 0.5312405 0.5683321 0.6093528 0.6487382 0.5700379 0.5418938 0.5211593
    +#>                                                                              
    +#> layer.1 0.8415392 0.8360026 0.8325552 0.8337363 0.8339533 0.8912849 0.8867860
    +#> layer.2 0.3242198 0.3359588 0.3303278 0.3143945 0.2708325 0.2575975 0.2809245
    +#> layer.3 0.8136049 0.7866027 0.7045769 0.6519655 0.5398928 0.8305082 0.8359634
    +#> layer.4 0.4008068 0.4572360 0.5471036 0.5718452 0.5700792 0.2632826 0.3020677
    +#> layer.5 0.5108757 0.5127704 0.5521633 0.5850800 0.6559584 0.6024207 0.5734186
    +#>                                                                              
    +#> layer.1 0.8807106 0.8738915 0.8672825 0.8616340 0.8571536 0.8533099 0.8488533
    +#> layer.2 0.3037493 0.3216412 0.3308881 0.3295497 0.3179543 0.2985197 0.2749763
    +#> layer.3 0.8312740 0.8171942 0.7939453 0.7616869 0.7210738 0.6737960 0.6228802
    +#> layer.4 0.3465595 0.3945501 0.4425012 0.4859430 0.5203217 0.5418532 0.5479865
    +#> layer.5 0.5520171 0.5408934 0.5413574 0.5531961 0.5746993 0.6028225 0.6335755
    +#>                                                                              
    +#> layer.1 0.8419808 0.9117805 0.9090526 0.9042812 0.8979800 0.8833313 0.8758136
    +#> layer.2 0.2512416 0.2570026 0.2809260 0.3035274 0.3201754 0.3229680 0.3083414
    +#> layer.3 0.5724977 0.8175488 0.8267526 0.8253873 0.8145533 0.7665224 0.7305762
    +#> layer.4 0.5375278 0.2860727 0.3163391 0.3506471 0.3876575 0.4590604 0.4860971
    +#> layer.5 0.6626552 0.6287730 0.6001593 0.5792111 0.5681102 0.5773113 0.5951498
    +#>                                                                              
    +#> layer.1 0.8679237 0.8586633 0.8463772 0.9251764 0.9199524 0.9141710 0.9067838
    +#> layer.2 0.2863445 0.2611997 0.2370352 0.2589605 0.3047683 0.3200938 0.3251705
    +#> layer.3 0.6886562 0.6435894 0.5991429 0.7972733 0.8129275 0.8053968 0.7890073
    +#> layer.4 0.5023710 0.5050812 0.4927354 0.3133764 0.3566663 0.3816158 0.4070643
    +#> layer.5 0.6183284 0.6433893 0.6667633 0.6461805 0.5990580 0.5886663 0.5878728
    +#>                                                                              
    +#> layer.1 0.8981802 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9159813
    +#> layer.2 0.3186414 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3253428
    +#> layer.3 0.7645150 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7768533
    +#> layer.4 0.4304725 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3908502
    +#> layer.5 0.5957192 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5991743
    +#>                                                                              
    +#> layer.1 0.9066665 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562 0.9315392
    +#> layer.2 0.3170430 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441 0.3143908
    +#> layer.3 0.7561399 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352 0.7677462
    +#> layer.4 0.4031866 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479 0.3741982
    +#> layer.5 0.6058227 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690 0.6078230
    +#>                                                                    
    +#> layer.1 0.9265291 0.9190510 0.8968893 0.8817701 0.8629508 0.8388748
    +#> layer.2 0.3265712 0.3282949 0.2992975 0.2742826 0.2483226 0.2255383
    +#> layer.3 0.7679649 0.7589920 0.7195554 0.6927858 0.6644491 0.6371886
    +#> layer.4 0.3753241 0.3774461 0.3785580 0.3729116 0.3601426 0.3391285
    +#> layer.5 0.6002171 0.6002851 0.6166497 0.6288149 0.6410043 0.6515710
    +
    +# create rij matrix using polygon (Spatial) planning units
    +rij_polygons <- rij_matrix(sim_pu_polygons, sim_features)
    +print(rij_polygons)
    +#> 5 x 90 sparse Matrix of class "dgCMatrix"
    +#>                                                                              
    +#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7017231
    +#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4084749
    +#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.6046204
    +#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5292249
    +#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5068509
    +#>                                                                              
    +#> layer.1 0.7219284 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627
    +#> layer.2 0.4142271 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791
    +#> layer.3 0.5411391 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503
    +#> layer.4 0.5479991 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241
    +#> layer.5 0.5510725 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185
    +#>                                                                              
    +#> layer.1 0.7147517 0.7212831 0.7343615 0.7516415 0.7699543 0.7897989 0.7772875
    +#> layer.2 0.3615030 0.3765606 0.3841340 0.3836641 0.3759393 0.2716298 0.2901825
    +#> layer.3 0.7345400 0.6860553 0.6288231 0.5654560 0.5000330 0.8346555 0.8298830
    +#> layer.4 0.4618047 0.5148344 0.5541170 0.5767631 0.5815260 0.2249796 0.2776009
    +#> layer.5 0.4517499 0.4732935 0.5068285 0.5495481 0.5965013 0.5022737 0.4802429
    +#>                                                                              
    +#> layer.1 0.7661407 0.7585159 0.7561971 0.7599327 0.7690602 0.7816119 0.7948577
    +#> layer.2 0.3119889 0.3334326 0.3508138 0.3612252 0.3631965 0.3568911 0.3438730
    +#> layer.3 0.8151305 0.7903745 0.7553823 0.7102172 0.6558247 0.5945687 0.5304188
    +#> layer.4 0.3390011 0.4047008 0.4684589 0.5238040 0.5655910 0.5906737 0.5976491
    +#> layer.5 0.4646212 0.4587578 0.4649525 0.4840959 0.5153215 0.5556964 0.6003282
    +#>                                                                              
    +#> layer.1 0.8059966 0.8286461 0.8189872 0.8095434 0.8020377 0.7980313 0.7983424
    +#> layer.2 0.3265679 0.2650109 0.2854743 0.3077757 0.3280176 0.3424767 0.3485511
    +#> layer.3 0.4684656 0.8381119 0.8367193 0.8254115 0.8043530 0.7733527 0.7323690
    +#> layer.4 0.5863706 0.2330116 0.2831343 0.3416658 0.4047077 0.4665769 0.5210694
    +#> layer.5 0.6432595 0.5353515 0.5095593 0.4907792 0.4822721 0.4860960 0.5028092
    +#>                                                                              
    +#> layer.1 0.8026563 0.8095316 0.8167839 0.8220091 0.8633036 0.8564520 0.8487799
    +#> layer.2 0.3453627 0.3338433 0.3163452 0.2959188 0.2603208 0.2824472 0.3051395
    +#> layer.3 0.6821137 0.6246373 0.5636095 0.5039577 0.8370439 0.8390267 0.8310691
    +#> layer.4 0.5629582 0.5888172 0.5969403 0.5869119 0.2456164 0.2910969 0.3438600
    +#> layer.5 0.5312405 0.5683321 0.6093528 0.6487382 0.5700379 0.5418938 0.5211593
    +#>                                                                              
    +#> layer.1 0.8415392 0.8360026 0.8325552 0.8337363 0.8339533 0.8912849 0.8867860
    +#> layer.2 0.3242198 0.3359588 0.3303278 0.3143945 0.2708325 0.2575975 0.2809245
    +#> layer.3 0.8136049 0.7866027 0.7045769 0.6519655 0.5398928 0.8305082 0.8359634
    +#> layer.4 0.4008068 0.4572360 0.5471036 0.5718452 0.5700792 0.2632826 0.3020677
    +#> layer.5 0.5108757 0.5127704 0.5521633 0.5850800 0.6559584 0.6024207 0.5734186
    +#>                                                                              
    +#> layer.1 0.8807106 0.8738915 0.8672825 0.8616340 0.8571536 0.8533099 0.8488533
    +#> layer.2 0.3037493 0.3216412 0.3308881 0.3295497 0.3179543 0.2985197 0.2749763
    +#> layer.3 0.8312740 0.8171942 0.7939453 0.7616869 0.7210738 0.6737960 0.6228802
    +#> layer.4 0.3465595 0.3945501 0.4425012 0.4859430 0.5203217 0.5418532 0.5479865
    +#> layer.5 0.5520171 0.5408934 0.5413574 0.5531961 0.5746993 0.6028225 0.6335755
    +#>                                                                              
    +#> layer.1 0.8419808 0.9117805 0.9090526 0.9042812 0.8979800 0.8833313 0.8758136
    +#> layer.2 0.2512416 0.2570026 0.2809260 0.3035274 0.3201754 0.3229680 0.3083414
    +#> layer.3 0.5724977 0.8175488 0.8267526 0.8253873 0.8145533 0.7665224 0.7305762
    +#> layer.4 0.5375278 0.2860727 0.3163391 0.3506471 0.3876575 0.4590604 0.4860971
    +#> layer.5 0.6626552 0.6287730 0.6001593 0.5792111 0.5681102 0.5773113 0.5951498
    +#>                                                                              
    +#> layer.1 0.8679237 0.8586633 0.8463772 0.9251764 0.9199524 0.9141710 0.9067838
    +#> layer.2 0.2863445 0.2611997 0.2370352 0.2589605 0.3047683 0.3200938 0.3251705
    +#> layer.3 0.6886562 0.6435894 0.5991429 0.7972733 0.8129275 0.8053968 0.7890073
    +#> layer.4 0.5023710 0.5050812 0.4927354 0.3133764 0.3566663 0.3816158 0.4070643
    +#> layer.5 0.6183284 0.6433893 0.6667633 0.6461805 0.5990580 0.5886663 0.5878728
    +#>                                                                              
    +#> layer.1 0.8981802 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9159813
    +#> layer.2 0.3186414 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3253428
    +#> layer.3 0.7645150 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7768533
    +#> layer.4 0.4304725 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3908502
    +#> layer.5 0.5957192 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5991743
    +#>                                                                              
    +#> layer.1 0.9066665 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562 0.9315392
    +#> layer.2 0.3170430 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441 0.3143908
    +#> layer.3 0.7561399 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352 0.7677462
    +#> layer.4 0.4031866 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479 0.3741982
    +#> layer.5 0.6058227 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690 0.6078230
    +#>                                                                    
    +#> layer.1 0.9265291 0.9190510 0.8968893 0.8817701 0.8629508 0.8388748
    +#> layer.2 0.3265712 0.3282949 0.2992975 0.2742826 0.2483226 0.2255383
    +#> layer.3 0.7679649 0.7589920 0.7195554 0.6927858 0.6644491 0.6371886
    +#> layer.4 0.3753241 0.3774461 0.3785580 0.3729116 0.3601426 0.3391285
    +#> layer.5 0.6002171 0.6002851 0.6166497 0.6288149 0.6410043 0.6515710
    +
    +# create rij matrix using polygon (sf) planning units
    +rij_sf <- rij_matrix(sim_pu_sf, sim_features)
    +print(rij_sf)
    +#> 5 x 90 sparse Matrix of class "dgCMatrix"
    +#>                                                                              
    +#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7017231
    +#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4084749
    +#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.6046204
    +#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5292249
    +#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5068509
    +#>                                                                              
    +#> layer.1 0.7219284 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627
    +#> layer.2 0.4142271 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791
    +#> layer.3 0.5411391 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503
    +#> layer.4 0.5479991 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241
    +#> layer.5 0.5510725 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185
    +#>                                                                              
    +#> layer.1 0.7147517 0.7212831 0.7343615 0.7516415 0.7699543 0.7897989 0.7772875
    +#> layer.2 0.3615030 0.3765606 0.3841340 0.3836641 0.3759393 0.2716298 0.2901825
    +#> layer.3 0.7345400 0.6860553 0.6288231 0.5654560 0.5000330 0.8346555 0.8298830
    +#> layer.4 0.4618047 0.5148344 0.5541170 0.5767631 0.5815260 0.2249796 0.2776009
    +#> layer.5 0.4517499 0.4732935 0.5068285 0.5495481 0.5965013 0.5022737 0.4802429
    +#>                                                                              
    +#> layer.1 0.7661407 0.7585159 0.7561971 0.7599327 0.7690602 0.7816119 0.7948577
    +#> layer.2 0.3119889 0.3334326 0.3508138 0.3612252 0.3631965 0.3568911 0.3438730
    +#> layer.3 0.8151305 0.7903745 0.7553823 0.7102172 0.6558247 0.5945687 0.5304188
    +#> layer.4 0.3390011 0.4047008 0.4684589 0.5238040 0.5655910 0.5906737 0.5976491
    +#> layer.5 0.4646212 0.4587578 0.4649525 0.4840959 0.5153215 0.5556964 0.6003282
    +#>                                                                              
    +#> layer.1 0.8059966 0.8286461 0.8189872 0.8095434 0.8020377 0.7980313 0.7983424
    +#> layer.2 0.3265679 0.2650109 0.2854743 0.3077757 0.3280176 0.3424767 0.3485511
    +#> layer.3 0.4684656 0.8381119 0.8367193 0.8254115 0.8043530 0.7733527 0.7323690
    +#> layer.4 0.5863706 0.2330116 0.2831343 0.3416658 0.4047077 0.4665769 0.5210694
    +#> layer.5 0.6432595 0.5353515 0.5095593 0.4907792 0.4822721 0.4860960 0.5028092
    +#>                                                                              
    +#> layer.1 0.8026563 0.8095316 0.8167839 0.8220091 0.8633036 0.8564520 0.8487799
    +#> layer.2 0.3453627 0.3338433 0.3163452 0.2959188 0.2603208 0.2824472 0.3051395
    +#> layer.3 0.6821137 0.6246373 0.5636095 0.5039577 0.8370439 0.8390267 0.8310691
    +#> layer.4 0.5629582 0.5888172 0.5969403 0.5869119 0.2456164 0.2910969 0.3438600
    +#> layer.5 0.5312405 0.5683321 0.6093528 0.6487382 0.5700379 0.5418938 0.5211593
    +#>                                                                              
    +#> layer.1 0.8415392 0.8360026 0.8325552 0.8337363 0.8339533 0.8912849 0.8867860
    +#> layer.2 0.3242198 0.3359588 0.3303278 0.3143945 0.2708325 0.2575975 0.2809245
    +#> layer.3 0.8136049 0.7866027 0.7045769 0.6519655 0.5398928 0.8305082 0.8359634
    +#> layer.4 0.4008068 0.4572360 0.5471036 0.5718452 0.5700792 0.2632826 0.3020677
    +#> layer.5 0.5108757 0.5127704 0.5521633 0.5850800 0.6559584 0.6024207 0.5734186
    +#>                                                                              
    +#> layer.1 0.8807106 0.8738915 0.8672825 0.8616340 0.8571536 0.8533099 0.8488533
    +#> layer.2 0.3037493 0.3216412 0.3308881 0.3295497 0.3179543 0.2985197 0.2749763
    +#> layer.3 0.8312740 0.8171942 0.7939453 0.7616869 0.7210738 0.6737960 0.6228802
    +#> layer.4 0.3465595 0.3945501 0.4425012 0.4859430 0.5203217 0.5418532 0.5479865
    +#> layer.5 0.5520171 0.5408934 0.5413574 0.5531961 0.5746993 0.6028225 0.6335755
    +#>                                                                              
    +#> layer.1 0.8419808 0.9117805 0.9090526 0.9042812 0.8979800 0.8833313 0.8758136
    +#> layer.2 0.2512416 0.2570026 0.2809260 0.3035274 0.3201754 0.3229680 0.3083414
    +#> layer.3 0.5724977 0.8175488 0.8267526 0.8253873 0.8145533 0.7665224 0.7305762
    +#> layer.4 0.5375278 0.2860727 0.3163391 0.3506471 0.3876575 0.4590604 0.4860971
    +#> layer.5 0.6626552 0.6287730 0.6001593 0.5792111 0.5681102 0.5773113 0.5951498
    +#>                                                                              
    +#> layer.1 0.8679237 0.8586633 0.8463772 0.9251764 0.9199524 0.9141710 0.9067838
    +#> layer.2 0.2863445 0.2611997 0.2370352 0.2589605 0.3047683 0.3200938 0.3251705
    +#> layer.3 0.6886562 0.6435894 0.5991429 0.7972733 0.8129275 0.8053968 0.7890073
    +#> layer.4 0.5023710 0.5050812 0.4927354 0.3133764 0.3566663 0.3816158 0.4070643
    +#> layer.5 0.6183284 0.6433893 0.6667633 0.6461805 0.5990580 0.5886663 0.5878728
    +#>                                                                              
    +#> layer.1 0.8981802 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9159813
    +#> layer.2 0.3186414 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3253428
    +#> layer.3 0.7645150 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7768533
    +#> layer.4 0.4304725 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3908502
    +#> layer.5 0.5957192 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5991743
    +#>                                                                              
    +#> layer.1 0.9066665 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562 0.9315392
    +#> layer.2 0.3170430 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441 0.3143908
    +#> layer.3 0.7561399 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352 0.7677462
    +#> layer.4 0.4031866 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479 0.3741982
    +#> layer.5 0.6058227 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690 0.6078230
    +#>                                                                    
    +#> layer.1 0.9265291 0.9190510 0.8968893 0.8817701 0.8629508 0.8388748
    +#> layer.2 0.3265712 0.3282949 0.2992975 0.2742826 0.2483226 0.2255383
    +#> layer.3 0.7679649 0.7589920 0.7195554 0.6927858 0.6644491 0.6371886
    +#> layer.4 0.3753241 0.3774461 0.3785580 0.3729116 0.3601426 0.3391285
    +#> layer.5 0.6002171 0.6002851 0.6166497 0.6288149 0.6410043 0.6515710
    +
    +# create rij matrix using raster stack planning units
    +rij_zones_raster <- rij_matrix(sim_pu_zones_stack, sim_features)
    +print(rij_zones_raster)
    +#> 5 x 90 sparse Matrix of class "dgCMatrix"
    +#>                                                                              
    +#> layer.1 0.7150548 0.6990429 0.6859317 0.6783193 0.6782107 0.6863252 0.7219284
    +#> layer.2 0.2900901 0.3052216 0.3266036 0.3510977 0.3750470 0.3950113 0.4142271
    +#> layer.3 0.8178213 0.8064534 0.7852441 0.7541320 0.7131559 0.6628435 0.5411391
    +#> layer.4 0.2199663 0.2713800 0.3296247 0.3900085 0.4468281 0.4946333 0.5479991
    +#> layer.5 0.4533809 0.4413639 0.4343547 0.4355628 0.4474871 0.4714105 0.5510725
    +#>                                                                              
    +#> layer.1 0.7435505 0.7631509 0.7505900 0.7357791 0.7233151 0.7156627 0.7147517
    +#> layer.2 0.4123446 0.4038949 0.2800841 0.2967547 0.3181723 0.3409791 0.3615030
    +#> layer.3 0.4763017 0.4147364 0.8275983 0.8194545 0.8013062 0.7730503 0.7345400
    +#> layer.4 0.5498153 0.5348758 0.2208512 0.2738525 0.3350935 0.3998241 0.4618047
    +#> layer.5 0.5991256 0.6448475 0.4741697 0.4569167 0.4453572 0.4428185 0.4517499
    +#>                                                                              
    +#> layer.1 0.7212831 0.7343615 0.7699543 0.7861227 0.7897989 0.7772875 0.7561971
    +#> layer.2 0.3765606 0.3841340 0.3759393 0.3627013 0.2716298 0.2901825 0.3508138
    +#> layer.3 0.6860553 0.6288231 0.5000330 0.4375452 0.8346555 0.8298830 0.7553823
    +#> layer.4 0.5148344 0.5541170 0.5815260 0.5684388 0.2249796 0.2776009 0.4684589
    +#> layer.5 0.4732935 0.5068285 0.5965013 0.6415349 0.5022737 0.4802429 0.4649525
    +#>                                                                              
    +#> layer.1 0.7599327 0.7690602 0.7816119 0.7948577 0.8059966 0.8286461 0.8189872
    +#> layer.2 0.3612252 0.3631965 0.3568911 0.3438730 0.3265679 0.2650109 0.2854743
    +#> layer.3 0.7102172 0.6558247 0.5945687 0.5304188 0.4684656 0.8381119 0.8367193
    +#> layer.4 0.5238040 0.5655910 0.5906737 0.5976491 0.5863706 0.2330116 0.2831343
    +#> layer.5 0.4840959 0.5153215 0.5556964 0.6003282 0.6432595 0.5353515 0.5095593
    +#>                                                                              
    +#> layer.1 0.8095434 0.8020377 0.7980313 0.7983424 0.8026563 0.8095316 0.8167839
    +#> layer.2 0.3077757 0.3280176 0.3424767 0.3485511 0.3453627 0.3338433 0.3163452
    +#> layer.3 0.8254115 0.8043530 0.7733527 0.7323690 0.6821137 0.6246373 0.5636095
    +#> layer.4 0.3416658 0.4047077 0.4665769 0.5210694 0.5629582 0.5888172 0.5969403
    +#> layer.5 0.4907792 0.4822721 0.4860960 0.5028092 0.5312405 0.5683321 0.6093528
    +#>                                                                              
    +#> layer.1 0.8220091 0.8633036 0.8564520 0.8487799 0.8360026 0.8330038 0.8337363
    +#> layer.2 0.2959188 0.2603208 0.2824472 0.3051395 0.3359588 0.3380832 0.3143945
    +#> layer.3 0.5039577 0.8370439 0.8390267 0.8310691 0.7866027 0.7500573 0.6519655
    +#> layer.4 0.5869119 0.2456164 0.2910969 0.3438600 0.4572360 0.5076754 0.5718452
    +#> layer.5 0.6487382 0.5700379 0.5418938 0.5211593 0.5127704 0.5270232 0.5850800
    +#>                                                                              
    +#> layer.1 0.8348882 0.8339533 0.8912849 0.8867860 0.8807106 0.8738915 0.8672825
    +#> layer.2 0.2933767 0.2708325 0.2575975 0.2809245 0.3037493 0.3216412 0.3308881
    +#> layer.3 0.5955431 0.5398928 0.8305082 0.8359634 0.8312740 0.8171942 0.7939453
    +#> layer.4 0.5797670 0.5700792 0.2632826 0.3020677 0.3465595 0.3945501 0.4425012
    +#> layer.5 0.6213430 0.6559584 0.6024207 0.5734186 0.5520171 0.5408934 0.5413574
    +#>                                                                              
    +#> layer.1 0.8616340 0.8571536 0.8488533 0.9117805 0.9090526 0.9042812 0.8979800
    +#> layer.2 0.3295497 0.3179543 0.2749763 0.2570026 0.2809260 0.3035274 0.3201754
    +#> layer.3 0.7616869 0.7210738 0.6228802 0.8175488 0.8267526 0.8253873 0.8145533
    +#> layer.4 0.4859430 0.5203217 0.5479865 0.2860727 0.3163391 0.3506471 0.3876575
    +#> layer.5 0.5531961 0.5746993 0.6335755 0.6287730 0.6001593 0.5792111 0.5681102
    +#>                                                                              
    +#> layer.1 0.8908018 0.8833313 0.8758136 0.8679237 0.8586633 0.8463772 0.9251764
    +#> layer.2 0.3271982 0.3229680 0.3083414 0.2863445 0.2611997 0.2370352 0.2589605
    +#> layer.3 0.7947903 0.7665224 0.7305762 0.6886562 0.6435894 0.5991429 0.7972733
    +#> layer.4 0.4249398 0.4590604 0.4860971 0.5023710 0.5050812 0.4927354 0.3133764
    +#> layer.5 0.5676758 0.5773113 0.5951498 0.6183284 0.6433893 0.6667633 0.6461805
    +#>                                                                              
    +#> layer.1 0.9237208 0.9199524 0.9141710 0.9067838 0.8981802 0.8885294 0.8775561
    +#> layer.2 0.2828033 0.3047683 0.3200938 0.3251705 0.3186414 0.3017897 0.2781030
    +#> layer.3 0.8107003 0.8129275 0.8053968 0.7890073 0.7645150 0.7330004 0.6962687
    +#> layer.4 0.3337335 0.3566663 0.3816158 0.4070643 0.4304725 0.4485815 0.4580145
    +#> layer.5 0.6187615 0.5990580 0.5886663 0.5878728 0.5957192 0.6102524 0.6288607
    +#>                                                                              
    +#> layer.1 0.8643680 0.8473654 0.9322748 0.9317639 0.9287528 0.9234146 0.9159813
    +#> layer.2 0.2521341 0.2280966 0.2641903 0.2872474 0.3081174 0.3219892 0.3253428
    +#> layer.3 0.6570162 0.6186335 0.7690767 0.7873422 0.7936492 0.7897301 0.7768533
    +#> layer.4 0.4559778 0.4408970 0.3437657 0.3535038 0.3646749 0.3773969 0.3908502
    +#> layer.5 0.6486412 0.6667338 0.6528235 0.6269821 0.6089827 0.5997976 0.5991743
    +#>                                                                              
    +#> layer.1 0.9066665 0.8955188 0.8822224 0.8658993 0.8449602 0.9336696 0.9339562
    +#> layer.2 0.3170430 0.2986799 0.2740393 0.2478477 0.2243090 0.2736019 0.2951441
    +#> layer.3 0.7561399 0.7289606 0.6972504 0.6635963 0.6310227 0.7330271 0.7567352
    +#> layer.4 0.4031866 0.4117466 0.4135759 0.4061247 0.3879679 0.3750617 0.3743479
    +#> layer.5 0.6058227 0.6177441 0.6325923 0.6479832 0.6617020 0.6480034 0.6238690
    +#>                                                                    
    +#> layer.1 0.9315392 0.9190510 0.9091912 0.8968893 0.8629508 0.8388748
    +#> layer.2 0.3143908 0.3282949 0.3186099 0.2992975 0.2483226 0.2255383
    +#> layer.3 0.7677462 0.7589920 0.7423054 0.7195554 0.6644491 0.6371886
    +#> layer.4 0.3741982 0.3774461 0.3792402 0.3785580 0.3601426 0.3391285
    +#> layer.5 0.6078230 0.6002851 0.6064383 0.6166497 0.6410043 0.6515710
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/run_calculations.html b/docs/reference/run_calculations.html index abe2b3d73..b7506cf06 100644 --- a/docs/reference/run_calculations.html +++ b/docs/reference/run_calculations.html @@ -1,105 +1,22 @@ - - - - - - - -Run calculations — run_calculations • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Run calculations — run_calculations • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -188,115 +105,110 @@

    Run calculations

    Execute preliminary calculations in a conservation problem and store the results for later use. This function is useful when creating slightly different versions of the same conservation planning problem that involve -the same pre-processing steps (e.g. calculating boundary data), because +the same pre-processing steps (e.g., calculating boundary data), because means that the same calculations will not be run multiple times.

    -
    run_calculations(x)
    - -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    - -

    Value

    +
    Usage,
    run_calculations(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    +
    +

    Value

    Invisible TRUE indicating success.

    -

    Details

    - +
    +
    +

    Details

    This function is used for the effect of modifying the input -ConservationProblem object. As such, it does not return -anything. To use this function with pipe() operators, use the +ConservationProblem object. As such, it does not return +anything. To use this function with pipe() operators, use the %T>% operator and not the %>% operator.

    +
    -

    Examples

    -
    # \dontrun{
    -# Let us imagine a scenario where we wanted to understand the effect of
    -# setting different targets on our solution.
    -
    -# create a conservation problem with no targets
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_boundary_penalties(10, 0.5) %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(verbose = FALSE)
    -
    -# create a copies of p and add targets
    -p1 <- p %>% add_relative_targets(0.1)
    -p2 <- p %>% add_relative_targets(0.2)
    -p3 <- p %>% add_relative_targets(0.3)
    -
    -# now solve each of the different problems and record the time spent
    -# solving them
    -s1 <- system.time({solve(p1); solve(p2); solve(p3)})
    -
    -# This approach is inefficient. Since these problems all share the same
    -# planning units it is actually performing the same calculations three times.
    -# To avoid this, we can use the "run_calculations" function before creating
    -# the copies. Normally, R runs the calculations just before solving the
    -# problem
    -
    -# recreate a conservation problem with no targets and tell R run the
    -# preliminary calculations. Note how we use the %T>% operator here.
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_boundary_penalties(10, 0.5) %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(verbose = FALSE) %T>%
    -     run_calculations()
    -
    -# create a copies of p and add targets just like before
    -p1 <- p %>% add_relative_targets(0.1)
    -p2 <- p %>% add_relative_targets(0.2)
    -p3 <- p %>% add_relative_targets(0.3)
    -
    -# solve each of the different problems and record the time spent
    -# solving them
    -s2 <- system.time({solve(p1); solve(p2); solve(p3)})
    -
    -# now lets compare the times
    -print(s1) # time spent without running preliminary calculations
    -#>    user  system elapsed 
    -#>   1.216   0.016   1.295 
    -print(s2) # time spent after running preliminary calculations
    -#>    user  system elapsed 
    -#>   1.134   0.024   1.200 
    -
    -# As we can see, we can save time by running the preliminary
    -# calculations before making copies of the problem with slightly
    -# different constraints. Although the time saved in this example
    -# is rather small, this is because the example data are very small.
    -# We would expect larger time savings for larger datasets.
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# Let us imagine a scenario where we wanted to understand the effect of
    +# setting different targets on our solution.
    +
    +# create a conservation problem with no targets
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_boundary_penalties(10, 0.5) %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(verbose = FALSE)
    +
    +# create a copies of p and add targets
    +p1 <- p %>% add_relative_targets(0.1)
    +p2 <- p %>% add_relative_targets(0.2)
    +p3 <- p %>% add_relative_targets(0.3)
    +
    +# now solve each of the different problems and record the time spent
    +# solving them
    +s1 <- system.time({solve(p1); solve(p2); solve(p3)})
    +
    +# This approach is inefficient. Since these problems all share the same
    +# planning units it is actually performing the same calculations three times.
    +# To avoid this, we can use the "run_calculations" function before creating
    +# the copies. Normally, R runs the calculations just before solving the
    +# problem
    +
    +# recreate a conservation problem with no targets and tell R run the
    +# preliminary calculations. Note how we use the %T>% operator here.
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_boundary_penalties(10, 0.5) %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(verbose = FALSE) %T>%
    +     run_calculations()
    +
    +# create a copies of p and add targets just like before
    +p1 <- p %>% add_relative_targets(0.1)
    +p2 <- p %>% add_relative_targets(0.2)
    +p3 <- p %>% add_relative_targets(0.3)
    +
    +# solve each of the different problems and record the time spent
    +# solving them
    +s2 <- system.time({solve(p1); solve(p2); solve(p3)})
    +
    +# now lets compare the times
    +print(s1) # time spent without running preliminary calculations
    +#>    user  system elapsed 
    +#>   1.260   0.026   1.369 
    +print(s2) # time spent after running preliminary calculations
    +#>    user  system elapsed 
    +#>   1.147   0.026   1.216 
    +
    +# As we can see, we can save time by running the preliminary
    +# calculations before making copies of the problem with slightly
    +# different constraints. Although the time saved in this example
    +# is rather small, this is because the example data are very small.
    +# We would expect larger time savings for larger datasets.
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/scalar_parameters.html b/docs/reference/scalar_parameters.html index b0028aeca..9e16e2aff 100644 --- a/docs/reference/scalar_parameters.html +++ b/docs/reference/scalar_parameters.html @@ -1,104 +1,21 @@ - - - - - - - -Scalar parameters — scalar_parameters • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Scalar parameters — scalar_parameters • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -190,169 +107,162 @@

    Scalar parameters

    that are unacceptable then an error is thrown.

    -
    proportion_parameter(name, value)
    -
    -binary_parameter(name, value)
    -
    -integer_parameter(
    -  name,
    -  value,
    -  lower_limit = as.integer(-.Machine$integer.max),
    -  upper_limit = as.integer(.Machine$integer.max)
    -)
    -
    -numeric_parameter(
    -  name,
    -  value,
    -  lower_limit = .Machine$double.xmin,
    -  upper_limit = .Machine$double.xmax
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - -
    name

    character name of parameter.

    value

    integer or double value depending on the -parameter.

    lower_limit

    integer or double value representing +

    Usage,
    proportion_parameter(name, value)
    +
    +binary_parameter(name, value)
    +
    +integer_parameter(
    +  name,
    +  value,
    +  lower_limit = as.integer(-.Machine$integer.max),
    +  upper_limit = as.integer(.Machine$integer.max)
    +)
    +
    +numeric_parameter(
    +  name,
    +  value,
    +  lower_limit = .Machine$double.xmin,
    +  upper_limit = .Machine$double.xmax
    +)
    + +
    +

    Arguments

    +
    name
    +

    character name of parameter.

    +
    value
    +

    integer or double value depending on the +parameter.

    +
    lower_limit
    +

    integer or double value representing the smallest acceptable value for value. Defaults to -the smallest possible number on the system.

    upper_limit

    integer or double value representing +the smallest possible number on the system.

    +
    upper_limit
    +

    integer or double value representing the largest acceptable value for value. Defaults to -the largest possible number on the system.

    - -

    Value

    - -

    ScalarParameter object.

    -

    Details

    - +the largest possible number on the system.

    +
    +
    +

    Value

    +

    ScalarParameter object.

    +
    +
    +

    Details

    Below is a list of parameter generating functions and a brief description of each.

    -
    - -
    proportion_parameter

    A parameter that is a double and bounded +

    proportion_parameter
    +

    A parameter that is a double and bounded between zero and one.

    -
    integer_parameter

    A parameter that is a integer.

    -
    numeric_parameter

    A parameter that is a double.

    +
    integer_parameter
    +

    A parameter that is a integer.

    -
    binary_parameter

    A parameter that is restricted to integer -values of zero or one.

    +
    numeric_parameter
    +

    A parameter that is a double.

    -
    + +
    binary_parameter
    +

    A parameter that is restricted to integer +values of zero or one.

    -

    Examples

    -
    # proportion parameter
    -p1 <- proportion_parameter('prop', 0.5) # create new object
    -print(p1) # print it
    -#> prop (0.5)
    -p1$get() # get value
    -#> [1] 0.5
    -p1$id # get id
    -#> id: 69135dfd-ab6e-44ba-a1b8-c6131db3faec
    -p1$validate(5) # check if 5 is a validate input
    -p1$validate(0.1) # check if 0.1 is a validate input
    -p1$set(0.1) # change value to 0.1
    -print(p1)
    -#> prop (0.1)
    -
    -# binary parameter
    -p2 <- binary_parameter('bin', 0) # create new object
    -print(p2) # print it
    -#> bin (0)
    -p2$get() # get value
    -#> [1] 0
    -p2$id # get id
    -#> id: 6bf50b76-c926-4683-8f3c-bbc9f19cb949
    -p2$validate(5) # check if 5 is a validate input
    -p2$validate(1L) # check if 1L is a validate input
    -p2$set(1L) # change value to 1L
    -print(p1) # print it again
    -#> prop (0.1)
    -
    -# integer parameter
    -p3 <- integer_parameter('int', 5L) # create new object
    -print(p3) # print it
    -#> int (5)
    -p3$get() # get value
    -#> [1] 5
    -p3$id # get id
    -#> id: 19ad0815-7343-4a4c-8e03-238890668805
    -p3$validate(5.6) # check if 5.6 is a validate input
    -p3$validate(2L) # check if 2L is a validate input
    -p3$set(2L) # change value to 2L
    -print(p3) # print it again
    -#> int (2)
    -
    -# numeric parameter
    -p4 <- numeric_parameter('dbl', -7.6) # create new object
    -print(p4) # print it
    -#> dbl (-7.6)
    -p4$get() # get value
    -#> [1] -7.6
    -p4$id # get id
    -#> id: 1337d728-1756-4f22-8a4d-90e2e19b4bbc
    -p4$validate(NA) # check if NA is a validate input
    -p4$validate(8.9) # check if 8.9 is a validate input
    -p4$set(8.9) # change value to 8.9
    -print(p4) # print it again
    -#> dbl (8.9)
    -
    -# numeric parameter with lower bounds
    -p5 <- numeric_parameter('bdbl', 6, lower_limit=0) # create new object
    -print(p5) # print it
    -#> bdbl (6)
    -p5$get() # get value
    -#> [1] 6
    -p5$id # get id
    -#> id: 29bdc6c4-bc59-4fdb-b457-703823d4340b
    -p5$validate(-10) # check if -10 is a validate input
    -p5$validate(90) # check if 90 is a validate input
    -p5$set(90) # change value to 8.9
    -print(p5) # print it again
    -#> bdbl (90)
    -
    +
    +
    + +
    +

    Examples

    +
    # proportion parameter
    +p1 <- proportion_parameter('prop', 0.5) # create new object
    +print(p1) # print it
    +#> prop (0.5)
    +p1$get() # get value
    +#> [1] 0.5
    +p1$id # get id
    +#> id: 068b405e-07c9-4fb7-a30c-fcf2313b2dca
    +p1$validate(5) # check if 5 is a validate input
    +p1$validate(0.1) # check if 0.1 is a validate input
    +p1$set(0.1) # change value to 0.1
    +print(p1)
    +#> prop (0.1)
    +
    +# binary parameter
    +p2 <- binary_parameter('bin', 0) # create new object
    +print(p2) # print it
    +#> bin (0)
    +p2$get() # get value
    +#> [1] 0
    +p2$id # get id
    +#> id: a7775ce5-14f3-43df-8a3c-4aec6734f7e9
    +p2$validate(5) # check if 5 is a validate input
    +p2$validate(1L) # check if 1L is a validate input
    +p2$set(1L) # change value to 1L
    +print(p1) # print it again
    +#> prop (0.1)
    +
    +# integer parameter
    +p3 <- integer_parameter('int', 5L) # create new object
    +print(p3) # print it
    +#> int (5)
    +p3$get() # get value
    +#> [1] 5
    +p3$id # get id
    +#> id: d632827c-e7fb-458a-9124-aa0926a7f3b1
    +p3$validate(5.6) # check if 5.6 is a validate input
    +p3$validate(2L) # check if 2L is a validate input
    +p3$set(2L) # change value to 2L
    +print(p3) # print it again
    +#> int (2)
    +
    +# numeric parameter
    +p4 <- numeric_parameter('dbl', -7.6) # create new object
    +print(p4) # print it
    +#> dbl (-7.6)
    +p4$get() # get value
    +#> [1] -7.6
    +p4$id # get id
    +#> id: 97e2b191-90d2-4477-a7a7-2216c5c5a477
    +p4$validate(NA) # check if NA is a validate input
    +p4$validate(8.9) # check if 8.9 is a validate input
    +p4$set(8.9) # change value to 8.9
    +print(p4) # print it again
    +#> dbl (8.9)
    +
    +# numeric parameter with lower bounds
    +p5 <- numeric_parameter('bdbl', 6, lower_limit=0) # create new object
    +print(p5) # print it
    +#> bdbl (6)
    +p5$get() # get value
    +#> [1] 6
    +p5$id # get id
    +#> id: 7d17fad3-226b-4ed9-9c83-227b586d3804
    +p5$validate(-10) # check if -10 is a validate input
    +p5$validate(90) # check if 90 is a validate input
    +p5$set(90) # change value to 8.9
    +print(p5) # print it again
    +#> bdbl (90)
    +
     
    +
    + - - - - - - - - - - - + diff --git a/docs/reference/show.html b/docs/reference/show.html index fc5d9a085..7c84bae3f 100644 --- a/docs/reference/show.html +++ b/docs/reference/show.html @@ -1,101 +1,18 @@ - - - - - - - -Show — show • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Show — show • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,64 +101,57 @@

    Show

    Display information about an object.

    -
    # S4 method for ConservationModifier
    -show(x)
    -
    -# S4 method for ConservationProblem
    -show(x)
    +    
    Usage,
    # S4 method for ConservationModifier
    +show(x)
     
    -# S4 method for Id
    -show(x)
    +# S4 method for ConservationProblem
    +show(x)
     
    -# S4 method for OptimizationProblem
    -show(x)
    +# S4 method for Id
    +show(x)
     
    -# S4 method for Parameter
    -show(x)
    +# S4 method for OptimizationProblem
    +show(x)
     
    -# S4 method for Solver
    -show(x)
    +# S4 method for Parameter +show(x) -

    Arguments

    - - - - - - -
    x

    Any object.

    - -

    Value

    +# S4 method for Solver +show(x)
    +
    +

    Arguments

    +
    x
    +

    Any object.

    +
    +
    +

    Value

    None.

    -

    See also

    - - +
    +
    +

    See also

    + +
    + - - - - - - - - - - - + diff --git a/docs/reference/sim_data.html b/docs/reference/sim_data.html index 53b78c2c3..bdbff2808 100644 --- a/docs/reference/sim_data.html +++ b/docs/reference/sim_data.html @@ -1,101 +1,18 @@ - - - - - - - -Simulated conservation planning data — sim_data • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Simulated conservation planning data — sim_data • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,116 +101,153 @@

    Simulated conservation planning data

    Simulated data for making spatial prioritizations.

    -
    data(sim_pu_polygons)
    +    
    Usage,
    data(sim_pu_polygons)
    +
    +data(sim_pu_zones_polygons)
     
    -data(sim_pu_zones_polygons)
    +data(sim_pu_points)
     
    -data(sim_pu_points)
    +data(sim_pu_lines)
     
    -data(sim_pu_lines)
    +data(sim_pu_sf)
     
    -data(sim_pu_sf)
    +data(sim_pu_zones_sf)
     
    -data(sim_pu_zones_sf)
    +data(sim_pu_raster)
     
    -data(sim_pu_raster)
    +data(sim_locked_in_raster)
     
    -data(sim_locked_in_raster)
    +data(sim_locked_out_raster)
     
    -data(sim_locked_out_raster)
    +data(sim_pu_zones_stack)
     
    -data(sim_pu_zones_stack)
    +data(sim_features)
     
    -data(sim_features)
    +data(sim_features_zones)
     
    -data(sim_features_zones)
    +data(sim_phylogeny)
    -data(sim_phylogeny)
    +
    +

    Format

    + +
    sim_pu_polygons
    +

    SpatialPolygonsDataFrame +object.

    -

    Format

    +
    sim_pu_zones_polygons
    +

    SpatialPolygonsDataFrame object.

    - -
    -
    sim_pu_polygons

    SpatialPolygonsDataFrame -object.

    +
    sim_pu_sf
    +

    sf::sf() object.

    + -
    sim_pu_zones_polygons

    SpatialPolygonsDataFrame object.

    +
    sim_pu_zones_sf
    +

    sf::sf() object.

    -
    sim_pu_sf

    sf::sf() object.

    -
    sim_pu_zones_sf

    sf::sf() object.

    +
    sim_pu_lines
    +

    SpatialLinesDataFrame object.

    -
    sim_pu_lines

    SpatialLinesDataFrame object.

    -
    sim_pu_points

    SpatialPointsDataFrame +

    sim_pu_points
    +

    SpatialPointsDataFrame object.

    -
    sim_pu_raster

    RasterLayer object.

    -
    sim_pu_zones_stack

    RasterStack object.

    +
    sim_pu_raster
    +

    RasterLayer object.

    -
    sim_locked_in_raster

    RasterLayer + +

    sim_pu_zones_stack
    +

    RasterStack object.

    + + +
    sim_locked_in_raster
    +

    RasterLayer object.

    -
    sim_locked_out_raster

    RasterLayer + +

    sim_locked_out_raster
    +

    RasterLayer object.

    -
    sim_features

    RasterStack object.

    -
    sim_features_zones

    ZonesRaster() object.

    +
    sim_features
    +

    RasterStack object.

    -
    sim_phylogeny

    ape::phylo() object.

    +
    sim_features_zones
    +

    ZonesRaster() object.

    -
    -

    Details

    +
    sim_phylogeny
    +

    ape::phylo() object.

    - -
    -
    sim_pu_raster

    Planning units are represented as raster data. + +

    +
    +

    Details

    + +
    sim_pu_raster
    +

    Planning units are represented as raster data. Pixel values indicate planning unit cost and NA values indicate that a pixel is not a planning unit.

    -
    sim_pu_zones_stack

    Planning units are represented as raster + +

    sim_pu_zones_stack
    +

    Planning units are represented as raster stack data. Each layer indicates the cost for a different management zone. Pixels with NA values in a given zone indicate that a planning unit cannot be allocated to that zone in a solution. Additionally, pixels with NA values in all layers are not a planning unit.

    -
    sim_locked_in_raster

    Planning units are represented as raster + +

    sim_locked_in_raster
    +

    Planning units are represented as raster data. Pixel values are binary and indicate if planning units should be locked in to the solution.

    -
    sim_locked_out_raster

    Planning units are represented as + +

    sim_locked_out_raster
    +

    Planning units are represented as raster data. Pixel values are binary and indicate if planning units should be locked out from the solution.

    -
    sim_pu_polygons

    Planning units represented as polygon data. + +

    sim_pu_polygons
    +

    Planning units represented as polygon data. The attribute table contains fields (columns) indicating the expenditure required for prioritizing each planning unit ("cost" field), if the planning units should be selected in the solution ("locked_in" field), and if the planning units should never be selected in the solution ("locked_out" field).

    -
    sim_pu_points

    Planning units represented as point data. + +

    sim_pu_points
    +

    Planning units represented as point data. The attribute table follows the same conventions as for sim_pu_polygons.

    -
    sim_pu_lines

    Planning units represented as line data. + +

    sim_pu_lines
    +

    Planning units represented as line data. The attribute table follows the same conventions as for sim_pu_polygons.

    -
    sim_pu_sf

    Planning units represented as polygon data -using the sf::sf() package. + +

    sim_pu_sf
    +

    Planning units represented as polygon data +using the sf::sf() package. The attribute table follows the same conventions as for sim_pu_polygons.

    -
    sim_pu_zones_polygons

    Planning units represented as polygon + +

    sim_pu_zones_polygons
    +

    Planning units represented as polygon data. The attribute table contains fields (columns) indicating the expenditure required for prioritizing each planning unit under different management zones ("cost_1", "cost_2", and "cost_3" fields), and a series @@ -302,94 +256,99 @@

    Details In these locked fields, planning units that should not be locked to a specific value are assigned a NA value.

    -
    sim_pu_zones_sf

    Planning units represented as polygon data -using the sf::sf() package. The attribute tables follows + +

    sim_pu_zones_sf
    +

    Planning units represented as polygon data +using the sf::sf() package. The attribute tables follows the same conventions as for sim_pu_zone_polygons.

    -
    sim_features

    The simulated distribution of ten species. + +

    sim_features
    +

    The simulated distribution of ten species. Pixel values indicate habitat suitability.

    -
    sim_features_zones

    The simulated distribution for five + +

    sim_features_zones
    +

    The simulated distribution for five species under three different management zones.

    -
    sim_phylogeny

    The phylogenetic tree for the ten species.

    - - -
    - - -

    Examples

    -
    # load data
    -data(sim_pu_polygons, sim_pu_lines, sim_pu_points, sim_pu_raster,
    -     sim_locked_in_raster, sim_locked_out_raster, sim_phylogeny,
    -     sim_features, sim_pu_sf)
    -
    -# plot example Spatial-class planning unit data
    -# \dontrun{
    -par(mfrow = c(2, 3))
    -plot(sim_pu_raster, main = "planning units (raster)")
    -plot(sim_locked_in_raster, main = "locked in units (raster)")
    -plot(sim_locked_out_raster, main = "locked out units (raster)")
    -plot(sim_pu_polygons, main = "planning units (polygons)")
    -plot(sim_pu_lines, main = "planning units (lines)")
    -plot(sim_pu_points, main = "planning units (points)")
    -
    -
    -# plot example sf-class planning unit data
    -plot(sim_pu_sf)
    -
    -
    -# plot example phylogeny data
    -par(mfrow = c(1, 1))
    -ape::plot.phylo(sim_phylogeny, main = "simulated phylogeny")
    -
    -
    -# plot example feature data
    -par(mfrow = c(1, 1))
    -plot(sim_features)
    -
    -
    -# plot example management zone cost data
    -par(mfrow = c(1, 1))
    -plot(sim_pu_zones_stack)
    -
    -
    -# plot example feature data for each management zone
    -plot(do.call(stack, sim_features_zones),
    -     main = paste0("Species ",
    -                   rep(seq_len(number_of_zones(sim_features_zones)),
    -                       number_of_features(sim_features_zones)),
    -                   " (zone ",
    -                   rep(seq_len(number_of_features(sim_features_zones)),
    -                       each = number_of_zones(sim_features_zones)),
    -                   ")"))
    -
    -# }
    +
    +
    sim_phylogeny
    +

    The phylogenetic tree for the ten species.

    + + + +
    + +
    +

    Examples

    +
    # load data
    +data(sim_pu_polygons, sim_pu_lines, sim_pu_points, sim_pu_raster,
    +     sim_locked_in_raster, sim_locked_out_raster, sim_phylogeny,
    +     sim_features, sim_pu_sf)
    +
    +# plot example Spatial-class planning unit data
    +# \dontrun{
    +par(mfrow = c(2, 3))
    +plot(sim_pu_raster, main = "planning units (raster)")
    +plot(sim_locked_in_raster, main = "locked in units (raster)")
    +plot(sim_locked_out_raster, main = "locked out units (raster)")
    +plot(sim_pu_polygons, main = "planning units (polygons)")
    +plot(sim_pu_lines, main = "planning units (lines)")
    +plot(sim_pu_points, main = "planning units (points)")
    +
    +
    +# plot example sf-class planning unit data
    +plot(sim_pu_sf)
    +
    +
    +# plot example phylogeny data
    +par(mfrow = c(1, 1))
    +ape::plot.phylo(sim_phylogeny, main = "simulated phylogeny")
    +
    +
    +# plot example feature data
    +par(mfrow = c(1, 1))
    +plot(sim_features)
    +
    +
    +# plot example management zone cost data
    +par(mfrow = c(1, 1))
    +plot(sim_pu_zones_stack)
    +
    +
    +# plot example feature data for each management zone
    +plot(do.call(stack, sim_features_zones),
    +     main = paste0("Species ",
    +                   rep(seq_len(number_of_zones(sim_features_zones)),
    +                       number_of_features(sim_features_zones)),
    +                   " (zone ",
    +                   rep(seq_len(number_of_features(sim_features_zones)),
    +                       each = number_of_zones(sim_features_zones)),
    +                   ")"))
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/simulate_cost.html b/docs/reference/simulate_cost.html index 2a9bd4f51..293a3b5d0 100644 --- a/docs/reference/simulate_cost.html +++ b/docs/reference/simulate_cost.html @@ -1,102 +1,19 @@ - - - - - - - -Simulate cost data — simulate_cost • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Simulate cost data — simulate_cost • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,91 +103,78 @@

    Simulate cost data

    it returns spatially auto-correlated integer values.

    -
    simulate_cost(
    -  x,
    -  n = 1,
    -  model = RandomFields::RPpoisson(RandomFields::RMtruncsupport(radius = raster::xres(x)
    -    * 10, RandomFields::RMgauss())),
    -  transform = identity,
    -  ...
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    x

    RasterLayer object to use as -a template.

    n

    integer number of species to simulate.

    model

    RandomFields::RP() model object -to use for simulating data.

    transform

    function to transform values output -from the random fields simulation.

    ...

    additional arguments passed to -RandomFields::RFsimulate().

    - -

    Value

    - -

    RasterStack object.

    -

    See also

    - - +
    Usage,
    simulate_cost(
    +  x,
    +  n = 1,
    +  model = RandomFields::RPpoisson(RandomFields::RMtruncsupport(radius = raster::xres(x)
    +    * 10, RandomFields::RMgauss())),
    +  transform = identity,
    +  ...
    +)
    + +
    +

    Arguments

    +
    x
    +

    RasterLayer object to use as +a template.

    +
    n
    +

    integer number of species to simulate.

    +
    model
    +

    RandomFields::RP() model object +to use for simulating data.

    +
    transform
    +

    function to transform values output +from the random fields simulation.

    +
    ...
    +

    additional arguments passed to +RandomFields::RFsimulate().

    +
    +
    +

    Value

    +

    RasterStack object.

    +
    +
    +

    See also

    + +
    -

    Examples

    -
    # \dontrun{
    -# create raster
    -r <- raster(ncol=10, nrow=10, xmn=0, xmx=1, ymn=0, ymx=1)
    -values(r) <- 1
    -
    -# simulate data
    -# (note this requires the RandomFields package to be installed)
    -cost <- simulate_cost(r)
    -
    -# plot simulated species
    -plot(cost, main = "simulated cost data")
    -
    -# }
    -
    +    
    +

    Examples

    +
    # \dontrun{
    +# create raster
    +r <- raster(ncol=10, nrow=10, xmn=0, xmx=1, ymn=0, ymx=1)
    +values(r) <- 1
    +
    +# simulate data
    +# (note this requires the RandomFields package to be installed)
    +cost <- simulate_cost(r)
    +
    +# plot simulated species
    +plot(cost, main = "simulated cost data")
    +
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/simulate_data.html b/docs/reference/simulate_data.html index 3dbfab1c3..c8a8fe155 100644 --- a/docs/reference/simulate_data.html +++ b/docs/reference/simulate_data.html @@ -1,101 +1,18 @@ - - - - - - - -Simulate data — simulate_data • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Simulate data — simulate_data • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,85 +101,72 @@

    Simulate data

    Simulate spatially auto-correlated data.

    -
    simulate_data(x, n, model, transform = identity, ...)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    x

    RasterLayer object to use as -a template.

    n

    integer number of species to simulate.

    model

    RandomFields::RP() model object -to use for simulating data.

    transform

    function to transform values output -from the random fields simulation.

    ...

    additional arguments passed to -RandomFields::RFsimulate().

    - -

    Value

    - -

    RasterStack object with a +

    Usage,
    simulate_data(x, n, model, transform = identity, ...)
    + +
    +

    Arguments

    +
    x
    +

    RasterLayer object to use as +a template.

    +
    n
    +

    integer number of species to simulate.

    +
    model
    +

    RandomFields::RP() model object +to use for simulating data.

    +
    transform
    +

    function to transform values output +from the random fields simulation.

    +
    ...
    +

    additional arguments passed to +RandomFields::RFsimulate().

    +
    +
    +

    Value

    +

    RasterStack object with a layer for each species.

    -

    See also

    - - +
    + -

    Examples

    -
    # \dontrun{
    -# create raster
    -r <- raster(ncol=10, nrow=10, xmn=0, xmx=1, ymn=0, ymx=1)
    -values(r) <- 1
    -
    -# simulate data using a Gaussian field
    -# (note this requires the RandomFields package to be installed)
    -d <- simulate_data(r, n = 1, model = RandomFields::RMgauss())
    -
    -# plot simulated data
    -plot(d, main = "random Gaussian field")
    -
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# create raster
    +r <- raster(ncol=10, nrow=10, xmn=0, xmx=1, ymn=0, ymx=1)
    +values(r) <- 1
    +
    +# simulate data using a Gaussian field
    +# (note this requires the RandomFields package to be installed)
    +d <- simulate_data(r, n = 1, model = RandomFields::RMgauss())
    +
    +# plot simulated data
    +plot(d, main = "random Gaussian field")
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/simulate_species.html b/docs/reference/simulate_species.html index 8713f4f90..815e268f0 100644 --- a/docs/reference/simulate_species.html +++ b/docs/reference/simulate_species.html @@ -1,102 +1,19 @@ - - - - - - - -Simulate species habitat suitability data — simulate_species • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Simulate species habitat suitability data — simulate_species • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,91 +103,78 @@

    Simulate species habitat suitability data

    the output will contain values between zero and one.

    -
    simulate_species(
    -  x,
    -  n = 1,
    -  model = RandomFields::RMgauss(),
    -  transform = stats::plogis,
    -  ...
    -)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    x

    RasterLayer object to use as -a template.

    n

    integer number of species to simulate.

    model

    RandomFields::RP() model object -to use for simulating data.

    transform

    function to transform values output -from the random fields simulation.

    ...

    additional arguments passed to -RandomFields::RFsimulate().

    - -

    Value

    - -

    RasterStack object.

    -

    See also

    - - +
    Usage,
    simulate_species(
    +  x,
    +  n = 1,
    +  model = RandomFields::RMgauss(),
    +  transform = stats::plogis,
    +  ...
    +)
    + +
    +

    Arguments

    +
    x
    +

    RasterLayer object to use as +a template.

    +
    n
    +

    integer number of species to simulate.

    +
    model
    +

    RandomFields::RP() model object +to use for simulating data.

    +
    transform
    +

    function to transform values output +from the random fields simulation.

    +
    ...
    +

    additional arguments passed to +RandomFields::RFsimulate().

    +
    +
    +

    Value

    +

    RasterStack object.

    +
    +
    +

    See also

    + +
    -

    Examples

    -
    # \dontrun{
    -# create raster
    -r <- raster(ncol=10, nrow=10, xmn=0, xmx=1, ymn=0, ymx=1)
    -values(r) <- 1
    -
    -# simulate data for 4 species
    -# (note this requires the RandomFields package to be installed)
    -spp <- simulate_species(r, 4)
    -#> ....
    -
    -# plot simulated species
    -plot(spp, main = "simulated species distributions")
    -
    -# }
    -
    +    
    +

    Examples

    +
    # \dontrun{
    +# create raster
    +r <- raster(ncol=10, nrow=10, xmn=0, xmx=1, ymn=0, ymx=1)
    +values(r) <- 1
    +
    +# simulate data for 4 species
    +# (note this requires the RandomFields package to be installed)
    +spp <- simulate_species(r, 4)
    +#> ....
    +
    +# plot simulated species
    +plot(spp, main = "simulated species distributions")
    +
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/solve-2.png b/docs/reference/solve-2.png index f22a803d8034533e7953c92efc1a720fd13e7dbf..dc4e80232bd8cc3945cf5b2a4bb29efb8da0458a 100644 GIT binary patch literal 16601 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
    pPnCvL$ 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 HeJ=eAy~hE% literal 16214 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 - - - - - - -Solve — solve • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Solve — solve • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Solve a conservation planning problem().

    +

    Solve a conservation planning problem().

    -
    # S4 method for OptimizationProblem,Solver
    -solve(a, b, ...)
    -
    -# S4 method for ConservationProblem,missing
    -solve(a, b, ..., run_checks = TRUE, force = FALSE)
    - -

    Arguments

    - - - - - - - - - - - - - - - - - - - - - - -
    a

    problem() (i.e. ConservationProblem) or -OptimizationProblem object.

    b

    Solver object. Not used if a is an -ConservationProblem object.

    ...

    arguments passed to compile().

    run_checks

    logical flag indicating whether presolve checks +

    Usage,
    # S4 method for OptimizationProblem,Solver
    +solve(a, b, ...)
    +
    +# S4 method for ConservationProblem,missing
    +solve(a, b, ..., run_checks = TRUE, force = FALSE)
    + +
    +

    Arguments

    +
    a
    +

    problem() (i.e., ConservationProblem) or +OptimizationProblem object.

    +
    b
    +

    Solver object. Not used if a is an +ConservationProblem object.

    +
    ...
    +

    arguments passed to compile().

    +
    run_checks
    +

    logical flag indicating whether presolve checks should be run prior solving the problem. These checks are performed using -the presolve_check() function. Defaults to TRUE. -Skipping these checks may reduce run time for large problems.

    force

    logical flag indicating if an attempt to should be +the presolve_check() function. Defaults to TRUE. +Skipping these checks may reduce run time for large problems.

    +
    force
    +

    logical flag indicating if an attempt to should be made to solve the problem even if potential issues were detected during -the presolve checks. Defaults to FALSE.

    - -

    Value

    - +the presolve checks. Defaults to FALSE.

    +
    +
    +

    Value

    A numeric, matrix, -RasterLayer, Spatial, -or sf::sf() object containing the solution to the problem. +RasterLayer, Spatial, +or sf::sf() object containing the solution to the problem. Additionally, the returned object will have the following additional attributes: "objective" containing the solution's objective, "runtime" denoting the number of seconds that elapsed while solving the problem, and "status" describing the status of the solution -(e.g. "OPTIMAL" indicates that the optimal solution was found). -In most cases, the first solution (e.g. "solution_1") +(e.g., "OPTIMAL" indicates that the optimal solution was found). +In most cases, the first solution (e.g., "solution_1") will contain the best solution found by the solver (note that this may not be an optimal solution depending on the gap used to solve the problem and noting that the default gap is 0.1).

    -

    Details

    - -

    After formulating a conservation planning problem(), -it can be solved using an exact algorithm solver (see solvers +

    +
    +

    Details

    +

    After formulating a conservation planning problem(), +it can be solved using an exact algorithm solver (see solvers for available solvers). If no solver has been explicitly specified, then the best available exact algorithm solver will be used by default -(see add_default_solver(). Although these exact algorithm +(see add_default_solver(). Although these exact algorithm solvers will often display a lot of information that isn't really that -helpful (e.g. nodes, cutting planes), they do display information -about the progress they are making on solving the problem (e.g. the +helpful (e.g., nodes, cutting planes), they do display information +about the progress they are making on solving the problem (e.g., the performance of the best solution found at a given point in time). If potential issues were detected during the -presolve checks (see presolve_check()) -and the problem is being forcibly solved (i.e. with force = TRUE), +presolve checks (see presolve_check()) +and the problem is being forcibly solved (i.e., with force = TRUE), then it is also worth checking for any warnings displayed by the solver to see if these potential issues are actually causing issues -(e.g. Gurobi can display warnings that include +(e.g., Gurobi can display warnings that include "Warning: Model contains large matrix coefficient range" and "Warning: Model contains large rhs").

    The object returned from this function depends on the argument to a. If the argument to a is an -OptimizationProblem object, then the +OptimizationProblem object, then the solution is returned as a logical vector showing the status of each planning unit in each zone. However, in most cases, the argument -to a will be a ConservationProblem object, and so +to a will be a ConservationProblem object, and so the type of object returned depends on the number of solutions generated and the data format used to specify the planning units:

    -
    - -
    a has numeric planning units

    The solution will be +

    a has numeric planning units
    +

    The solution will be returned as a numeric vector. Here, each element in the vector corresponds to a different planning unit. Note that if a portfolio is used to generate multiple solutions, then a list of such numeric vectors will be returned.

    -
    a has matrix planning units

    The solution will be + +

    a has matrix planning units
    +

    The solution will be returned as a matrix object. Here, rows correspond to different planning units, and fields (columns) correspond to different management zones. Note that if a portfolio is used to generate multiple solutions, then a list of such matrix objects will be returned.

    -
    a has Raster planning units

    The solution -will be returned as a Raster object. + +

    a has Raster planning units
    +

    The solution +will be returned as a Raster object. If the argument to x contains a single -management zone, then a RasterLayer object will be returned. +management zone, then a RasterLayer object will be returned. Otherwise, if the argument to x contains multiple zones, then a -RasterStack object +RasterStack object will be returned containing a different layer for each management zone. Note that if a portfolio is used to generate multiple solutions, -then a list of such Raster objects will be returned.

    +then a list of such Raster objects will be returned.

    -
    a has Spatial, sf::sf(), or data.frame -planning units

    The solution will be returned in the same data format as the planning + +

    a has Spatial, sf::sf(), or data.frame +planning units
    +

    The solution will be returned in the same data format as the planning units. Here, each row corresponds to a different planning unit, and fields contain solutions. @@ -297,310 +208,308 @@

    Details will contain fields (columns) that solution the values. Specifically, the field name(s) containing the solution values be will named as "solution_XXX" where "XXX" corresponds to a solution -identifier (e.g. "solution_1"). +identifier (e.g., "solution_1"). If the argument to a contains multiple zones, then the fields containing solutions will be named as "solution_XXX_YYY" where "XXX" corresponds to the solution identifier and "YYY" is the name -of the management zone (e.g. "solution_1_zone1").

    - +of the management zone (e.g., "solution_1_zone1").

    -
    -

    See also

    -

    See problem() to create conservation planning problems, and -presolve_check() to check problems for potential issues. -Also, see the category_layer() and category_vector() function to +

    +
    +

    See also

    +

    See problem() to create conservation planning problems, and +presolve_check() to check problems for potential issues. +Also, see the category_layer() and category_vector() function to reformat solutions that contain multiple zones.

    +
    -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_polygons, sim_pu_sf, sim_features,
    -     sim_pu_zones_stack, sim_pu_zones_sf, sim_features_zones)
    -
    -# build minimal conservation problem with raster data
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s1 <- solve(p1)
    -
    -# print solution
    -print(s1)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# print attributes describing the optimization process and the solution
    -print(attr(s1, "objective"))
    -#> solution_1 
    -#>   1987.399 
    -print(attr(s1, "runtime"))
    -#> solution_1 
    -#>      0.004 
    -print(attr(s1, "status"))
    -#> solution_1 
    -#>  "OPTIMAL" 
    -
    -# calculate feature representation in the solution
    -r1 <- eval_feature_representation_summary(p1, s1)
    -print(r1)
    -#> # A tibble: 5 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall layer.1         83.3          8.91         0.107
    -#> 2 overall layer.2         31.2          3.13         0.100
    -#> 3 overall layer.3         72.0          7.34         0.102
    -#> 4 overall layer.4         42.7          4.35         0.102
    -#> 5 overall layer.5         56.7          6.01         0.106
    -
    -# plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# build minimal conservation problem with polygon (Spatial) data
    -p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print first six rows of the attribute table
    -print(head(s2))
    -#>       cost locked_in locked_out solution_1
    -#> 1 215.8638     FALSE      FALSE          0
    -#> 2 212.7823     FALSE      FALSE          0
    -#> 3 207.4962     FALSE      FALSE          0
    -#> 4 208.9322     FALSE       TRUE          0
    -#> 5 214.0419     FALSE      FALSE          0
    -#> 6 213.7636     FALSE      FALSE          0
    -
    -# calculate feature representation in the solution
    -r2 <- eval_feature_representation_summary(p2, s2[, "solution_1"])
    -print(r2)
    -#> # A tibble: 5 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall layer.1         74.5          8.05         0.108
    -#> 2 overall layer.2         28.1          2.83         0.101
    -#> 3 overall layer.3         64.9          6.65         0.103
    -#> 4 overall layer.4         38.2          3.87         0.101
    -#> 5 overall layer.5         50.7          5.41         0.107
    -
    -# plot solution
    -spplot(s2, zcol = "solution_1", main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# build minimal conservation problem with polygon (sf) data
    -p3 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s3 <- solve(p3)
    -
    -# print first six rows of the attribute table
    -print(head(s3))
    -#> Simple feature collection with 6 features and 4 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>       cost locked_in locked_out solution_1                       geometry
    -#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# calculate feature representation in the solution
    -r3 <- eval_feature_representation_summary(p3, s3[, "solution_1"])
    -print(r3)
    -#> # A tibble: 5 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall layer.1         74.5          8.05         0.108
    -#> 2 overall layer.2         28.1          2.83         0.101
    -#> 3 overall layer.3         64.9          6.65         0.103
    -#> 4 overall layer.4         38.2          3.87         0.101
    -#> 5 overall layer.5         50.7          5.41         0.107
    -
    -# plot solution
    -plot(s3[, "solution_1"])
    -
    -# }
    -
    -# build multi-zone conservation problem with raster data
    -p4 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s4 <- solve(p4)
    -
    -# print solution
    -print(s4)
    -#> class      : RasterStack 
    -#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> names      : zone_1, zone_2, zone_3 
    -#> min values :      0,      0,      0 
    -#> max values :      1,      1,      1 
    -#> 
    -
    -# calculate feature representation in the solution
    -r4 <- eval_feature_representation_summary(p4, s4)
    -print(r4)
    -#> # A tibble: 20 × 5
    -#>    summary feature   total_amount absolute_held relative_held
    -#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    -#>  1 overall feature_1        250.          47.1          0.188
    -#>  2 overall feature_2         93.6         17.9          0.191
    -#>  3 overall feature_3        216.          41.6          0.193
    -#>  4 overall feature_4        128.          23.7          0.185
    -#>  5 overall feature_5        170.          31.6          0.186
    -#>  6 zone_1  feature_1         83.3         16.5          0.198
    -#>  7 zone_1  feature_2         31.2          5.65         0.181
    -#>  8 zone_1  feature_3         72.0         14.2          0.198
    -#>  9 zone_1  feature_4         42.7          7.56         0.177
    -#> 10 zone_1  feature_5         56.7         11.1          0.197
    -#> 11 zone_2  feature_1         83.3         15.7          0.189
    -#> 12 zone_2  feature_2         31.2          6.03         0.193
    -#> 13 zone_2  feature_3         72.0         14.5          0.201
    -#> 14 zone_2  feature_4         42.7          7.82         0.183
    -#> 15 zone_2  feature_5         56.7         10.3          0.181
    -#> 16 zone_3  feature_1         83.3         14.8          0.178
    -#> 17 zone_3  feature_2         31.2          6.22         0.199
    -#> 18 zone_3  feature_3         72.0         13.0          0.180
    -#> 19 zone_3  feature_4         42.7          8.27         0.194
    -#> 20 zone_3  feature_5         56.7         10.2          0.180
    -
    -# plot solution
    -plot(category_layer(s4), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# build multi-zone conservation problem with polygon (sf) data
    -p5 <- problem(sim_pu_zones_sf, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s5 <- solve(p5)
    -
    -# print first six rows of the attribute table
    -print(head(s5))
    -#> Simple feature collection with 6 features and 9 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    -#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    -#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    -#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    -#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    -#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    -#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    -#>   solution_1_zone_2 solution_1_zone_3                       geometry
    -#> 1                 1                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2                 1                 0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4                 1                 0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5                 1                 0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6                 1                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# calculate feature representation in the solution
    -r5 <- eval_feature_representation_summary(
    -  p5, s5[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -print(r5)
    -#> # A tibble: 20 × 5
    -#>    summary feature   total_amount absolute_held relative_held
    -#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    -#>  1 overall feature_1        225.          37.5          0.166
    -#>  2 overall feature_2         83.9         14.6          0.174
    -#>  3 overall feature_3        195.          34.0          0.174
    -#>  4 overall feature_4        114.          18.4          0.161
    -#>  5 overall feature_5        154.          25.1          0.163
    -#>  6 zone_1  feature_1         75.1         12.8          0.170
    -#>  7 zone_1  feature_2         28.0          4.63         0.165
    -#>  8 zone_1  feature_3         65.0         10.6          0.163
    -#>  9 zone_1  feature_4         38.0          6.29         0.165
    -#> 10 zone_1  feature_5         51.2          8.88         0.174
    -#> 11 zone_2  feature_1         75.1         11.0          0.146
    -#> 12 zone_2  feature_2         28.0          5.26         0.188
    -#> 13 zone_2  feature_3         65.0         10.7          0.165
    -#> 14 zone_2  feature_4         38.0          6.49         0.171
    -#> 15 zone_2  feature_5         51.2          7.27         0.142
    -#> 16 zone_3  feature_1         75.1         13.7          0.182
    -#> 17 zone_3  feature_2         28.0          4.75         0.170
    -#> 18 zone_3  feature_3         65.0         12.7          0.195
    -#> 19 zone_3  feature_4         38.0          5.62         0.148
    -#> 20 zone_3  feature_5         51.2          8.93         0.175
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s5$solution <- category_vector(s5[, c("solution_1_zone_1",
    -                                      "solution_1_zone_2",
    -                                      "solution_1_zone_3")])
    -s5$solution <- factor(s5$solution)
    -
    -# plot solution
    -plot(s5[, "solution"])
    -
    -# }
    +    
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_polygons, sim_pu_sf, sim_features,
    +     sim_pu_zones_stack, sim_pu_zones_sf, sim_features_zones)
    +
    +# build minimal conservation problem with raster data
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s1 <- solve(p1)
    +
    +# print solution
    +print(s1)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# print attributes describing the optimization process and the solution
    +print(attr(s1, "objective"))
    +#> solution_1 
    +#>   1987.399 
    +print(attr(s1, "runtime"))
    +#> solution_1 
    +#>      0.005 
    +print(attr(s1, "status"))
    +#> solution_1 
    +#>  "OPTIMAL" 
    +
    +# calculate feature representation in the solution
    +r1 <- eval_feature_representation_summary(p1, s1)
    +print(r1)
    +#> # A tibble: 5 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall layer.1         83.3          8.91         0.107
    +#> 2 overall layer.2         31.2          3.13         0.100
    +#> 3 overall layer.3         72.0          7.34         0.102
    +#> 4 overall layer.4         42.7          4.35         0.102
    +#> 5 overall layer.5         56.7          6.01         0.106
    +
    +# plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# build minimal conservation problem with polygon (Spatial) data
    +p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print first six rows of the attribute table
    +print(head(s2))
    +#>       cost locked_in locked_out solution_1
    +#> 1 215.8638     FALSE      FALSE          0
    +#> 2 212.7823     FALSE      FALSE          0
    +#> 3 207.4962     FALSE      FALSE          0
    +#> 4 208.9322     FALSE       TRUE          0
    +#> 5 214.0419     FALSE      FALSE          0
    +#> 6 213.7636     FALSE      FALSE          0
    +
    +# calculate feature representation in the solution
    +r2 <- eval_feature_representation_summary(p2, s2[, "solution_1"])
    +print(r2)
    +#> # A tibble: 5 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall layer.1         74.5          8.05         0.108
    +#> 2 overall layer.2         28.1          2.83         0.101
    +#> 3 overall layer.3         64.9          6.65         0.103
    +#> 4 overall layer.4         38.2          3.87         0.101
    +#> 5 overall layer.5         50.7          5.41         0.107
    +
    +# plot solution
    +spplot(s2, zcol = "solution_1", main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# build minimal conservation problem with polygon (sf) data
    +p3 <- problem(sim_pu_sf, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s3 <- solve(p3)
    +
    +# print first six rows of the attribute table
    +print(head(s3))
    +#> Simple feature collection with 6 features and 4 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>       cost locked_in locked_out solution_1                       geometry
    +#> 1 215.8638     FALSE      FALSE          0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2 212.7823     FALSE      FALSE          0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3 207.4962     FALSE      FALSE          0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4 208.9322     FALSE       TRUE          0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5 214.0419     FALSE      FALSE          0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6 213.7636     FALSE      FALSE          0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# calculate feature representation in the solution
    +r3 <- eval_feature_representation_summary(p3, s3[, "solution_1"])
    +print(r3)
    +#> # A tibble: 5 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall layer.1         74.5          8.05         0.108
    +#> 2 overall layer.2         28.1          2.83         0.101
    +#> 3 overall layer.3         64.9          6.65         0.103
    +#> 4 overall layer.4         38.2          3.87         0.101
    +#> 5 overall layer.5         50.7          5.41         0.107
    +
    +# plot solution
    +plot(s3[, "solution_1"])
    +
    +# }
    +
    +# build multi-zone conservation problem with raster data
    +p4 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s4 <- solve(p4)
    +
    +# print solution
    +print(s4)
    +#> class      : RasterStack 
    +#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> names      : zone_1, zone_2, zone_3 
    +#> min values :      0,      0,      0 
    +#> max values :      1,      1,      1 
    +#> 
    +
    +# calculate feature representation in the solution
    +r4 <- eval_feature_representation_summary(p4, s4)
    +print(r4)
    +#> # A tibble: 20 × 5
    +#>    summary feature   total_amount absolute_held relative_held
    +#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    +#>  1 overall feature_1        250.          47.1          0.188
    +#>  2 overall feature_2         93.6         17.9          0.191
    +#>  3 overall feature_3        216.          41.6          0.193
    +#>  4 overall feature_4        128.          23.7          0.185
    +#>  5 overall feature_5        170.          31.6          0.186
    +#>  6 zone_1  feature_1         83.3         16.5          0.198
    +#>  7 zone_1  feature_2         31.2          5.65         0.181
    +#>  8 zone_1  feature_3         72.0         14.2          0.198
    +#>  9 zone_1  feature_4         42.7          7.56         0.177
    +#> 10 zone_1  feature_5         56.7         11.1          0.197
    +#> 11 zone_2  feature_1         83.3         15.7          0.189
    +#> 12 zone_2  feature_2         31.2          6.03         0.193
    +#> 13 zone_2  feature_3         72.0         14.5          0.201
    +#> 14 zone_2  feature_4         42.7          7.82         0.183
    +#> 15 zone_2  feature_5         56.7         10.3          0.181
    +#> 16 zone_3  feature_1         83.3         14.8          0.178
    +#> 17 zone_3  feature_2         31.2          6.22         0.199
    +#> 18 zone_3  feature_3         72.0         13.0          0.180
    +#> 19 zone_3  feature_4         42.7          8.27         0.194
    +#> 20 zone_3  feature_5         56.7         10.2          0.180
    +
    +# plot solution
    +plot(category_layer(s4), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# build multi-zone conservation problem with polygon (sf) data
    +p5 <- problem(sim_pu_zones_sf, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s5 <- solve(p5)
    +
    +# print first six rows of the attribute table
    +print(head(s5))
    +#> Simple feature collection with 6 features and 9 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    +#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    +#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    +#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    +#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    +#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    +#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    +#>   solution_1_zone_2 solution_1_zone_3                       geometry
    +#> 1                 1                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2                 1                 0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4                 1                 0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5                 1                 0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6                 1                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# calculate feature representation in the solution
    +r5 <- eval_feature_representation_summary(
    +  p5, s5[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +print(r5)
    +#> # A tibble: 20 × 5
    +#>    summary feature   total_amount absolute_held relative_held
    +#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    +#>  1 overall feature_1        225.          37.5          0.166
    +#>  2 overall feature_2         83.9         14.6          0.174
    +#>  3 overall feature_3        195.          34.0          0.174
    +#>  4 overall feature_4        114.          18.4          0.161
    +#>  5 overall feature_5        154.          25.1          0.163
    +#>  6 zone_1  feature_1         75.1         12.8          0.170
    +#>  7 zone_1  feature_2         28.0          4.63         0.165
    +#>  8 zone_1  feature_3         65.0         10.6          0.163
    +#>  9 zone_1  feature_4         38.0          6.29         0.165
    +#> 10 zone_1  feature_5         51.2          8.88         0.174
    +#> 11 zone_2  feature_1         75.1         11.0          0.146
    +#> 12 zone_2  feature_2         28.0          5.26         0.188
    +#> 13 zone_2  feature_3         65.0         10.7          0.165
    +#> 14 zone_2  feature_4         38.0          6.49         0.171
    +#> 15 zone_2  feature_5         51.2          7.27         0.142
    +#> 16 zone_3  feature_1         75.1         13.7          0.182
    +#> 17 zone_3  feature_2         28.0          4.75         0.170
    +#> 18 zone_3  feature_3         65.0         12.7          0.195
    +#> 19 zone_3  feature_4         38.0          5.62         0.148
    +#> 20 zone_3  feature_5         51.2          8.93         0.175
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s5$solution <- category_vector(s5[, c("solution_1_zone_1",
    +                                      "solution_1_zone_2",
    +                                      "solution_1_zone_3")])
    +s5$solution <- factor(s5$solution)
    +
    +# plot solution
    +plot(s5[, "solution"])
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/solvers.html b/docs/reference/solvers.html index c5280a80e..e88ddc483 100644 --- a/docs/reference/solvers.html +++ b/docs/reference/solvers.html @@ -1,107 +1,24 @@ - - - - - - - -Problem solvers — solvers • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Problem solvers — solvers • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -

    Specify the software and configuration used to solve a conservation planning -problem(). By default, the best available +problem(). By default, the best available software currently installed on the system will be used. For information on the performance of different solvers, please see Schuster et al. (2020) for benchmarks comparing the @@ -196,18 +113,19 @@

    Problem solvers

    different sized datasets.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    The following solvers can be used to find solutions for a -conservation planning problem():

    -
    - -
    add_default_solver()

    This solver uses the best software +conservation planning problem():

    +
    add_default_solver()
    +

    This solver uses the best software currently installed on the system.

    -
    add_gurobi_solver()

    Gurobi + +

    add_gurobi_solver()
    +

    Gurobi is a state-of-the-art commercial optimization software with an R package interface. We recommend using this solver if at all possible. It is by far the fastest of the solvers available for @@ -216,16 +134,20 @@

    Details gurobi package is distributed with the Gurobi software suite. This solver uses the gurobi package to solve problems.

    -
    add_cplex_solver()

    IBM CPLEX + +

    add_cplex_solver()
    +

    IBM CPLEX is a commercial optimization software. It is faster than the open source solvers available for generating prioritizations, however, it is not freely available. -Similar to the Gurobi +Similar to the Gurobi software, licenses are available to academics at no cost. This solver uses the cplexAPI package to solve problems using IBM CPLEX.

    -
    add_cbc_solver()

    CBC is an + +

    add_cbc_solver()
    +

    CBC is an open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Preliminary benchmarks indicate that it is the fastest open source @@ -233,134 +155,137 @@

    Details We recommend using this solver if both Gurobi and IBM CPLEX are unavailable. It requires the rcbc package, which is currently only available on -GitHub.

    +GitHub.

    + -
    add_lpsymphony_solver()

    SYMPHONY is an +

    add_lpsymphony_solver()
    +

    SYMPHONY is an open-source mixed integer programming solver that is also part of the COIN-OR project. Although both SYMPHONY and CBC are part of the COIN-OR project, they are different software. The lpsymphony package provides an interface to the SYMPHONY software, and is distributed through -Bioconductor. +Bioconductor. We recommend using this solver if the CBC solver cannot be installed. This solver can use parallel processing to solve problems, so it is faster than Rsymphony package interface (see below).

    -
    add_rsymphony_solver()

    This solver provides an alternative interface to the -SYMPHONY solver using + +

    add_rsymphony_solver()
    +

    This solver provides an alternative interface to the +SYMPHONY solver using the Rsymphony package. Unlike other solvers, the Rsymphony package can be installed directly from the Comprehensive R Archive Network (CRAN). It is also the slowest of the available solvers.

    -
    - -

    References

    +
    +
    +

    References

    Schuster R, Hanson JO, Strimas-Mackey M, and Bennett JR (2020). Exact integer linear programming solvers outperform simulated annealing for solving conservation planning problems. PeerJ, 8: e9258.

    -

    See also

    - - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # \dontrun{
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create basic problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -  add_min_set_objective() %>%
    -  add_relative_targets(0.1) %>%
    -  add_proportion_decisions()
    -
    -# create vector to store plot titles
    -titles <- c()
    -
    -# create empty stack to store solutions
    -s <- stack()
    -
    -# if gurobi is installed: create problem with added gurobi solver
    -if (require("gurobi")) {
    -  titles <- c(titles, "gurobi")
    -  p3 <- p %>% add_gurobi_solver(verbose = FALSE)
    -  s <- addLayer(s, solve(p3))
    -}
    -#> Loading required package: gurobi
    -#> Loading required package: slam
    -
    -# if cplexAPI is installed: create problem with added CPLEX solver
    -if (require("cplexAPI")) {
    -  titles <- c(titles, "CPLEX")
    -  p4 <- p %>% add_cplex_solver(verbose = FALSE)
    -  s <- addLayer(s, solve(p4))
    -}
    -#> Loading required package: cplexAPI
    -
    -# if rcbc is installed: create problem with added cbc solver
    -if (require("rcbc")) {
    -  titles <- c(titles, "CBC")
    -  p6 <- p %>% add_cbc_solver(verbose = FALSE)
    -  s <- addLayer(s, solve(p6))
    -}
    -#> Loading required package: rcbc
    -
    -# create problem with added rsymphony solver
    -if (require("Rsymphony")) {
    -  titles <- c(titles, "Rsymphony")
    -  p2 <- p %>% add_rsymphony_solver(verbose = FALSE)
    -  s <- addLayer(s, solve(p2))
    -}
    -#> Loading required package: Rsymphony
    -
    -# if lpsymphony is installed: create problem with added lpsymphony solver
    -if (require("lpsymphony")) {
    -  titles <- c(titles, "lpsymphony")
    -  p5 <- p %>% add_lpsymphony_solver(verbose = FALSE)
    -  s <- addLayer(s, solve(p5))
    -}
    -#> Loading required package: lpsymphony
    -
    -# plot solutions
    -plot(s, main = titles, axes = FALSE, box = FALSE)
    -
    -# }
    -
    +    
    +

    Examples

    +
    # \dontrun{
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create basic problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +  add_min_set_objective() %>%
    +  add_relative_targets(0.1) %>%
    +  add_proportion_decisions()
    +
    +# create vector to store plot titles
    +titles <- c()
    +
    +# create empty stack to store solutions
    +s <- stack()
    +
    +# if gurobi is installed: create problem with added gurobi solver
    +if (require("gurobi")) {
    +  titles <- c(titles, "gurobi")
    +  p3 <- p %>% add_gurobi_solver(verbose = FALSE)
    +  s <- addLayer(s, solve(p3))
    +}
    +#> Loading required package: gurobi
    +#> Loading required package: slam
    +
    +# if cplexAPI is installed: create problem with added CPLEX solver
    +if (require("cplexAPI")) {
    +  titles <- c(titles, "CPLEX")
    +  p4 <- p %>% add_cplex_solver(verbose = FALSE)
    +  s <- addLayer(s, solve(p4))
    +}
    +#> Loading required package: cplexAPI
    +
    +# if rcbc is installed: create problem with added cbc solver
    +if (require("rcbc")) {
    +  titles <- c(titles, "CBC")
    +  p6 <- p %>% add_cbc_solver(verbose = FALSE)
    +  s <- addLayer(s, solve(p6))
    +}
    +#> Loading required package: rcbc
    +
    +# create problem with added rsymphony solver
    +if (require("Rsymphony")) {
    +  titles <- c(titles, "Rsymphony")
    +  p2 <- p %>% add_rsymphony_solver(verbose = FALSE)
    +  s <- addLayer(s, solve(p2))
    +}
    +#> Loading required package: Rsymphony
    +
    +# if lpsymphony is installed: create problem with added lpsymphony solver
    +if (require("lpsymphony")) {
    +  titles <- c(titles, "lpsymphony")
    +  p5 <- p %>% add_lpsymphony_solver(verbose = FALSE)
    +  s <- addLayer(s, solve(p5))
    +}
    +#> Loading required package: lpsymphony
    +
    +# plot solutions
    +plot(s, main = titles, axes = FALSE, box = FALSE)
    +
    +# }
    +
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/summaries.html b/docs/reference/summaries.html index 8475c25cd..c8cb53041 100644 --- a/docs/reference/summaries.html +++ b/docs/reference/summaries.html @@ -1,104 +1,21 @@ - - - - - - - -Evaluate solutions using summary statistics — summaries • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Evaluate solutions using summary statistics — summaries • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    After generating a solution to a conservation planning problem(), +

    After generating a solution to a conservation planning problem(), it can be useful to evaluate how well it performs. These functions can be used to evaluate a solution according to various different summary statistics.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    The following functions can be used to summarize the performance -of a solution to a conservation planning problem():

    -
    - -
    eval_n_summary()

    Calculate the number of planning units selected +of a solution to a conservation planning problem():

    +
    eval_n_summary()
    +

    Calculate the number of planning units selected within a solution.

    -
    eval_cost_summary()

    Calculate the total cost of a solution.

    -
    eval_feature_representation_summary()

    Calculate how well features +

    eval_cost_summary()
    +

    Calculate the total cost of a solution.

    + + +
    eval_feature_representation_summary()
    +

    Calculate how well features are represented by a solution. This function can be used for all problems.

    -
    eval_target_coverage_summary()

    Calculate how well feature -representation targets are met by a solution. This function can only be -used with problems contain targets.

    -
    eval_boundary_summary()

    Calculate the exposed boundary length +

    eval_target_coverage_summary()
    +

    Calculate how well feature +representation targets are met by a solution. This function can only be +used with problems contain targets.

    + + +
    eval_boundary_summary()
    +

    Calculate the exposed boundary length (perimeter) associated with a solution.

    -
    eval_connectivity_summary()

    Calculate the connectivity held within -a solution.

    +
    eval_connectivity_summary()
    +

    Calculate the connectivity held within +a solution.

    -
    -

    See also

    - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create a minimal problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.1) %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(verbose = FALSE)
    -
    -# \dontrun{
    -# solve problem
    -s <- solve(p)
    -
    -# evaluate number of selected planning units in solution
    -eval_n_summary(p, s)
    -#> # A tibble: 1 × 2
    -#>   summary  cost
    -#>   <chr>   <dbl>
    -#> 1 overall    10
    -
    -# evaluate solution cost
    -eval_cost_summary(p, s)
    -#> # A tibble: 1 × 2
    -#>   summary  cost
    -#>   <chr>   <dbl>
    -#> 1 overall 1987.
    -
    -# evaluate feature representation by solution
    -eval_feature_representation_summary(p, s)
    -#> # A tibble: 5 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall layer.1         83.3          8.91         0.107
    -#> 2 overall layer.2         31.2          3.13         0.100
    -#> 3 overall layer.3         72.0          7.34         0.102
    -#> 4 overall layer.4         42.7          4.35         0.102
    -#> 5 overall layer.5         56.7          6.01         0.106
    -
    -# evaluate target coverage by solution
    -eval_target_coverage_summary(p, s)
    -#> # A tibble: 5 × 9
    -#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -#> 1 layer.1 TRUE          83.3            8.33          8.91                  0
    -#> 2 layer.2 TRUE          31.2            3.12          3.13                  0
    -#> 3 layer.3 TRUE          72.0            7.20          7.34                  0
    -#> 4 layer.4 TRUE          42.7            4.27          4.35                  0
    -#> 5 layer.5 TRUE          56.7            5.67          6.01                  0
    -#> # … with 3 more variables: relative_target <dbl>, relative_held <dbl>,
    -#> #   relative_shortfall <dbl>
    -
    -
    -# evaluate exposed boundary (perimeter) length by solution
    -eval_boundary_summary(p, s)
    -#> # A tibble: 1 × 2
    -#>   summary boundary
    -#>   <chr>      <dbl>
    -#> 1 overall     2.25
    -
    -# create a connectivity matrix to describe pair-wise connectivity
    -# values between combinations of planning units,
    -# see ?connectivity_matrix for more information
    -
    -# for brevity, we will do this using the cost data
    -# cost valuers have high connectivity between them
    -cm <- connectivity_matrix(sim_pu_raster, sim_pu_raster)
    -
    -# evaluate connectivity of solution
    -eval_connectivity_summary(p, s, data = cm)
    -#> # A tibble: 1 × 2
    -#>   summary connectivity
    -#>   <chr>          <dbl>
    -#> 1 overall         198.
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create a minimal problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.1) %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(verbose = FALSE)
    +
    +# \dontrun{
    +# solve problem
    +s <- solve(p)
    +
    +# evaluate number of selected planning units in solution
    +eval_n_summary(p, s)
    +#> # A tibble: 1 × 2
    +#>   summary  cost
    +#>   <chr>   <dbl>
    +#> 1 overall    10
    +
    +# evaluate solution cost
    +eval_cost_summary(p, s)
    +#> # A tibble: 1 × 2
    +#>   summary  cost
    +#>   <chr>   <dbl>
    +#> 1 overall 1987.
    +
    +# evaluate feature representation by solution
    +eval_feature_representation_summary(p, s)
    +#> # A tibble: 5 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall layer.1         83.3          8.91         0.107
    +#> 2 overall layer.2         31.2          3.13         0.100
    +#> 3 overall layer.3         72.0          7.34         0.102
    +#> 4 overall layer.4         42.7          4.35         0.102
    +#> 5 overall layer.5         56.7          6.01         0.106
    +
    +# evaluate target coverage by solution
    +eval_target_coverage_summary(p, s)
    +#> # A tibble: 5 × 9
    +#>   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +#>   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +#> 1 layer.1 TRUE          83.3            8.33          8.91                  0
    +#> 2 layer.2 TRUE          31.2            3.12          3.13                  0
    +#> 3 layer.3 TRUE          72.0            7.20          7.34                  0
    +#> 4 layer.4 TRUE          42.7            4.27          4.35                  0
    +#> 5 layer.5 TRUE          56.7            5.67          6.01                  0
    +#> # … with 3 more variables: relative_target <dbl>, relative_held <dbl>,
    +#> #   relative_shortfall <dbl>
    +
    +
    +# evaluate exposed boundary (perimeter) length by solution
    +eval_boundary_summary(p, s)
    +#> # A tibble: 1 × 2
    +#>   summary boundary
    +#>   <chr>      <dbl>
    +#> 1 overall     2.25
    +
    +# create a connectivity matrix to describe pair-wise connectivity
    +# values between combinations of planning units,
    +# see ?connectivity_matrix for more information
    +
    +# for brevity, we will do this using the cost data
    +# cost valuers have high connectivity between them
    +cm <- connectivity_matrix(sim_pu_raster, sim_pu_raster)
    +
    +# evaluate connectivity of solution
    +eval_connectivity_summary(p, s, data = cm)
    +#> # A tibble: 1 × 2
    +#>   summary connectivity
    +#>   <chr>          <dbl>
    +#> 1 overall         198.
    +
    +# }
     
    +
    +
    -
    - - - - - - - - - - + diff --git a/docs/reference/targets.html b/docs/reference/targets.html index 6786b3d7a..759ea7d3d 100644 --- a/docs/reference/targets.html +++ b/docs/reference/targets.html @@ -1,102 +1,19 @@ - - - - - - - -Add representation targets — targets • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Add representation targets — targets • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -186,101 +103,104 @@

    Add representation targets

    distribution that should (ideally) be covered (represented) by a solution.

    +
    Usage,NULL
    - -

    Details

    - +
    +

    Details

    Please note that most objectives require targets, and attempting to solve a problem that requires targets will throw an error.

    The following functions can be used to specify targets for a -conservation planning problem():

    -
    - -
    add_relative_targets()

    Set targets as a proportion +conservation planning problem():

    +
    add_relative_targets()
    +

    Set targets as a proportion (between 0 and 1) of the total amount of each feature in the the study area.

    -
    add_absolute_targets()

    Set targets that denote the + +

    add_absolute_targets()
    +

    Set targets that denote the minimum amount of each feature required in the prioritization.

    -
    add_loglinear_targets()

    Set targets as a proportion + +

    add_loglinear_targets()
    +

    Set targets as a proportion (between 0 and 1) that are calculated using log-linear interpolation.

    -
    add_manual_targets()

    Set targets manually.

    +
    add_manual_targets()
    +

    Set targets manually.

    -
    -

    See also

    - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # load data
    -data(sim_pu_raster, sim_features)
    -
    -# create base problem
    -p <- problem(sim_pu_raster, sim_features) %>%
    -     add_min_set_objective() %>%
    -     add_binary_decisions() %>%
    -     add_default_solver(verbose = FALSE)
    -
    -# create problem with added relative targets
    -p1 <- p %>% add_relative_targets(0.1)
    -
    -# create problem with added absolute targets
    -p2 <- p %>% add_absolute_targets(3)
    -
    -# create problem with added loglinear targets
    -p3 <- p %>% add_loglinear_targets(10, 0.9, 100, 0.2)
    -
    -# create problem with manual targets that equate to 10% relative targets
    -p4 <- p %>% add_manual_targets(data.frame(feature = names(sim_features),
    -                                          target = 0.1,
    -                                          type = "relative"))
    -# \dontrun{
    -# solve problem
    -s <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    -
    -# plot solution
    -plot(s, axes = FALSE, box = FALSE,
    -     main = c("relative targets", "absolute targets", "loglinear targets",
    -              "manual targets"))
    -
    -# }
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create base problem
    +p <- problem(sim_pu_raster, sim_features) %>%
    +     add_min_set_objective() %>%
    +     add_binary_decisions() %>%
    +     add_default_solver(verbose = FALSE)
    +
    +# create problem with added relative targets
    +p1 <- p %>% add_relative_targets(0.1)
    +
    +# create problem with added absolute targets
    +p2 <- p %>% add_absolute_targets(3)
    +
    +# create problem with added loglinear targets
    +p3 <- p %>% add_loglinear_targets(10, 0.9, 100, 0.2)
    +
    +# create problem with manual targets that equate to 10% relative targets
    +p4 <- p %>% add_manual_targets(data.frame(feature = names(sim_features),
    +                                          target = 0.1,
    +                                          type = "relative"))
    +# \dontrun{
    +# solve problem
    +s <- stack(solve(p1), solve(p2), solve(p3), solve(p4))
    +
    +# plot solution
    +plot(s, axes = FALSE, box = FALSE,
    +     main = c("relative targets", "absolute targets", "loglinear targets",
    +              "manual targets"))
    +
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/tee.html b/docs/reference/tee.html index e3da7d8fa..1d8635e59 100644 --- a/docs/reference/tee.html +++ b/docs/reference/tee.html @@ -1,101 +1,18 @@ - - - - - - - -Tee operator — %T>% • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Tee operator — %T>% • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,71 +101,67 @@

    Tee operator

    This package uses the "tee" operator (\%T>\%) to modify objects.

    +
    Usage,NULL
    -

    Arguments

    - - - - - - -
    lhs, rhs

    An object and a function.

    - -

    Value

    - +
    +

    Arguments

    +
    lhs, rhs
    +

    An object and a function.

    +
    +
    +

    Value

    An object.

    -

    See also

    - -

    magrittr::%T>%(), pipe().

    +
    +
    +

    See also

    +

    magrittr::%T>%(), pipe().

    +
    -

    Examples

    -
    # the tee operator returns the left-hand side of the result and can be
    -# useful when dealing with mutable objects. In this example we want
    -# to use the function "f" to modify the object "e" and capture the
    -# result
    -
    -# create an empty environment
    -e <- new.env()
    -
    -# create a function to modify an environment and return NULL
    -f <- function(x) {x$a <- 5; return(NULL)}
    -
    -# if we use the pipe operator we won't capture the result since "f"()
    -# returns a NULL
    -e2 <- e %>% f()
    -print(e2)
    -#> NULL
    -
    -# but if we use the tee operator then the result contains a copy of "e"
    -e3 <- e %T>% f()
    -print(e3)
    -#> <environment: 0x56421c3d1b00>
    -
    +    
    +

    Examples

    +
    # the tee operator returns the left-hand side of the result and can be
    +# useful when dealing with mutable objects. In this example we want
    +# to use the function "f" to modify the object "e" and capture the
    +# result
    +
    +# create an empty environment
    +e <- new.env()
    +
    +# create a function to modify an environment and return NULL
    +f <- function(x) {x$a <- 5; return(NULL)}
    +
    +# if we use the pipe operator we won't capture the result since "f"()
    +# returns a NULL
    +e2 <- e %>% f()
    +print(e2)
    +#> NULL
    +
    +# but if we use the tee operator then the result contains a copy of "e"
    +e3 <- e %T>% f()
    +print(e3)
    +#> <environment: 0x55f2759882e0>
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/tibble-methods.html b/docs/reference/tibble-methods.html index f341c1572..1cbaaa700 100644 --- a/docs/reference/tibble-methods.html +++ b/docs/reference/tibble-methods.html @@ -1,101 +1,18 @@ - - - - - - - -Manipulate tibbles — tibble-methods • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Manipulate tibbles — tibble-methods • prioritizr - - - - - - - - - - - - - - +
    -
    -
    -

    Assorted functions for manipulating tibble::tibble() objects.

    +

    Assorted functions for manipulating tibble::tibble() objects.

    -
    # S4 method for tbl_df
    -nrow(x)
    +    
    Usage,
    # S4 method for tbl_df
    +nrow(x)
     
    -# S4 method for tbl_df
    -ncol(x)
    +# S4 method for tbl_df
    +ncol(x)
     
    -# S4 method for tbl_df
    -as.list(x)
    +# S4 method for tbl_df +as.list(x)
    -

    Arguments

    - - - - - - -
    x

    tibble::tibble() object.

    +
    +

    Arguments

    +
    x
    +

    tibble::tibble() object.

    +
    +
    +

    Details

    +

    The following methods are provided from manipulating +tibble::tibble() objects.

    +
    nrow
    +

    extract integer number of rows.

    -

    Details

    -

    The following methods are provided from manipulating -tibble::tibble() objects.

    -
    -
    nrow

    extract integer number of rows.

    +
    ncol
    +

    extract integer number of columns.

    -
    ncol

    extract integer number of columns.

    -
    as.list

    convert to a list.

    +
    as.list
    +

    convert to a list.

    -
    print

    print the object.

    +
    print
    +

    print the object.

    -
    -

    Examples

    -
    # load tibble package
    -require(tibble)
    -#> Loading required package: tibble
    -
    -# make tibble
    -a <- tibble(value = seq_len(5))
    -
    -# number of rows
    -nrow(a)
    -#> [1] 5
    -
    -# number of columns
    -ncol(a)
    -#> [1] 1
    -
    -# convert to list
    -as.list(a)
    -#> $value
    -#> [1] 1 2 3 4 5
    -#> 
    +
    + +
    +

    Examples

    +
    # load tibble package
    +require(tibble)
    +#> Loading required package: tibble
    +
    +# make tibble
    +a <- tibble(value = seq_len(5))
    +
    +# number of rows
    +nrow(a)
    +#> [1] 5
    +
    +# number of columns
    +ncol(a)
    +#> [1] 1
    +
    +# convert to list
    +as.list(a)
    +#> $value
    +#> [1] 1 2 3 4 5
    +#> 
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/write_problem.html b/docs/reference/write_problem.html index 5f88cb9a3..ff341500d 100644 --- a/docs/reference/write_problem.html +++ b/docs/reference/write_problem.html @@ -1,103 +1,20 @@ - - - - - - - -Write problem — write_problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Write problem — write_problem • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    -

    Save the mathematical formulation for a conservation planning problem() +

    Save the mathematical formulation for a conservation planning problem() to a file for mixed integer programming solvers. Note that this function requires the Rsymphony package to be installed.

    -
    write_problem(x, path)
    +
    Usage,
    write_problem(x, path)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    path

    character file path to save the problem formulation. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    path
    +

    character file path to save the problem formulation. The argument should contain a ".lp" or .mps" file extension to specify whether the problem formulation will be saved in the -LP or -MPS -format (respectively).

    - -

    Value

    - +LP or +MPS +format (respectively).

    +
    +
    +

    Value

    Invisible TRUE indicating success.

    +
    -

    Examples

    -
    # \dontrun{
    -# set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create minimal problem
    -p <- problem(sim_pu_sf[1:4, , drop = FALSE], sim_features,
    -             cost_column = "cost") %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(0.1) %>%
    -     add_binary_decisions()
    -
    -# specify file path to save problem formulation
    -path <- file.path(tempdir(), "model.lp")
    -
    -# save problem to file (using the Rsymphony package)
    -write_problem(p, path)
    -
    -# print model file
    -cat(readLines(path), sep = "\n")
    -#> \Problem name: 
    -#> 
    -#> Minimize
    -#> obj: 215.863839903 x0 + 212.782348080 x1 + 207.496243710 x2 + 208.932169949 x3
    -#> Subject To
    -#> cons0:  -0.277834868 x0 -0.277834868 x1 -0.277834868 x2 -0.277834868 x3 <= -0.277834868
    -#> cons1:  -0.127301291 x0 -0.127301291 x1 -0.127301291 x2 -0.127301291 x3 <= -0.127301291
    -#> cons2:  -0.316365083 x0 -0.316365083 x1 -0.316365083 x2 -0.316365083 x3 <= -0.316365083
    -#> cons3:  -0.121097958 x0 -0.121097958 x1 -0.121097958 x2 -0.121097958 x3 <= -0.121097958
    -#> cons4:  -0.176466230 x0 -0.176466230 x1 -0.176466230 x2 -0.176466230 x3 <= -0.176466230
    -#> Bounds
    -#>  0 <= x0 <= 1
    -#>  0 <= x1 <= 1
    -#>  0 <= x2 <= 1
    -#>  0 <= x3 <= 1
    -#> Integers
    -#> x0 x1 x2 x3 
    -#> End
    -# }
    -
    +    
    +

    Examples

    +
    # \dontrun{
    +# set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_features)
    +
    +# create minimal problem
    +p <- problem(sim_pu_sf[1:4, , drop = FALSE], sim_features,
    +             cost_column = "cost") %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(0.1) %>%
    +     add_binary_decisions()
    +
    +# specify file path to save problem formulation
    +path <- file.path(tempdir(), "model.lp")
    +
    +# save problem to file (using the Rsymphony package)
    +write_problem(p, path)
    +
    +# print model file
    +cat(readLines(path), sep = "\n")
    +#> \Problem name: 
    +#> 
    +#> Minimize
    +#> obj: 215.863839903 x0 + 212.782348080 x1 + 207.496243710 x2 + 208.932169949 x3
    +#> Subject To
    +#> cons0:  -0.277834868 x0 -0.277834868 x1 -0.277834868 x2 -0.277834868 x3 <= -0.277834868
    +#> cons1:  -0.127301291 x0 -0.127301291 x1 -0.127301291 x2 -0.127301291 x3 <= -0.127301291
    +#> cons2:  -0.316365083 x0 -0.316365083 x1 -0.316365083 x2 -0.316365083 x3 <= -0.316365083
    +#> cons3:  -0.121097958 x0 -0.121097958 x1 -0.121097958 x2 -0.121097958 x3 <= -0.121097958
    +#> cons4:  -0.176466230 x0 -0.176466230 x1 -0.176466230 x2 -0.176466230 x3 <= -0.176466230
    +#> Bounds
    +#>  0 <= x0 <= 1
    +#>  0 <= x1 <= 1
    +#>  0 <= x2 <= 1
    +#>  0 <= x3 <= 1
    +#> Integers
    +#> x0 x1 x2 x3 
    +#> End
    +# }
    +
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/zone_names.html b/docs/reference/zone_names.html index 0ec85aab2..e7fcaa9db 100644 --- a/docs/reference/zone_names.html +++ b/docs/reference/zone_names.html @@ -1,101 +1,18 @@ - - - - - - - -Zone names — zone_names • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Zone names — zone_names • prioritizr - - - - - - - - - - - - - - +
    -
    -
    @@ -184,71 +101,65 @@

    Zone names

    Extract the names of zones in an object.

    -
    zone_names(x)
    -
    -# S4 method for ConservationProblem
    -zone_names(x)
    -
    -# S4 method for ZonesRaster
    -zone_names(x)
    +    
    Usage,
    zone_names(x)
     
    -# S4 method for ZonesCharacter
    -zone_names(x)
    +# S4 method for ConservationProblem +zone_names(x) -

    Arguments

    - - - - - - -
    x

    problem() (i.e. ConservationProblem) or Zones()

    +# S4 method for ZonesRaster +zone_names(x) -

    Value

    +# S4 method for ZonesCharacter +zone_names(x)
    +
    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) or Zones()

    +
    +
    +

    Value

    character zone names.

    +
    -

    Examples

    -
    # load data
    -data(sim_pu_zones_stack, sim_features_zones)
    -
    -# print names of zones in a Zones object
    -print(zone_names(sim_features_zones))
    -#> [1] "zone_1" "zone_2" "zone_3"
    -# create problem with multiple zones
    -p <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -     add_min_set_objective() %>%
    -     add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    -     add_binary_decisions()
    -
    -# print zone names in problem
    -print(zone_names(p))
    -#> [1] "zone_1" "zone_2" "zone_3"
    +    
    +

    Examples

    +
    # load data
    +data(sim_pu_zones_stack, sim_features_zones)
    +
    +# print names of zones in a Zones object
    +print(zone_names(sim_features_zones))
    +#> [1] "zone_1" "zone_2" "zone_3"
    +# create problem with multiple zones
    +p <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +     add_min_set_objective() %>%
    +     add_relative_targets(matrix(0.2, ncol = 3, nrow = 5)) %>%
    +     add_binary_decisions()
    +
    +# print zone names in problem
    +print(zone_names(p))
    +#> [1] "zone_1" "zone_2" "zone_3"
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/reference/zones.html b/docs/reference/zones.html index a0b077b2f..850acf7dc 100644 --- a/docs/reference/zones.html +++ b/docs/reference/zones.html @@ -1,107 +1,24 @@ - - - - - - - -Management zones — zones • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Management zones — zones • prioritizr - - - - - - - - - - - - - - - - - - - - - - - - - - +
    -
    -
    @@ -191,152 +108,152 @@

    Management zones

    Specifically, the data should describe the expected amount of each feature within each planning unit given each management zone. For example, the data could describe the occupancy -(e.g. presence/absence), probability of occurrence, or +(e.g., presence/absence), probability of occurrence, or abundance expected for each feature when each planning unit is allocated to a different zone.

    -
    zones(..., zone_names = NULL, feature_names = NULL)
    - -

    Arguments

    - - - - - - - - - - - - - - -
    ...

    raster::raster() or character objects that -pertain to the biodiversity data. See Details for more information.

    zone_names

    character names of the management zones. Defaults -to NULL which results in sequential integers.

    feature_names

    character names of the features zones. Defaults -to NULL which results in sequential integers.

    - -

    Value

    - +
    Usage,
    zones(..., zone_names = NULL, feature_names = NULL)
    + +
    +

    Arguments

    +
    ...
    +

    raster::raster() or character objects that +pertain to the biodiversity data. See Details for more information.

    +
    zone_names
    +

    character names of the management zones. Defaults +to NULL which results in sequential integers.

    +
    feature_names
    +

    character names of the features zones. Defaults +to NULL which results in sequential integers.

    +
    +
    +

    Value

    Zones object.

    -

    Details

    - +
    +
    +

    Details

    This function is used to store and organize data for use in a -conservation planning problem() that has multiple management +conservation planning problem() that has multiple management zones. In all cases, the data for each zone is input as a separate argument. The correct arguments depends on the type of planning unit data -used when building the conservation planning problem().

    -
    -
    planning unit data are a Raster or Spatial object

    Raster object can be supplied to specify the expected amount of +used when building the conservation planning problem().

    +
    planning unit data are a Raster or Spatial object
    +

    Raster object can be supplied to specify the expected amount of each feature within each planning unit under each management zone. Data for each zone should be specified as separate arguments, and the data for each feature in a given zone are specified -in separate layers in a raster::stack() object. +in separate layers in a raster::stack() object. Note that all layers for a given zone must have NA values in exactly the same cells.

    -
    planning unit data are a Spatial or data.frame -object

    character vector containing column names can + +

    planning unit data are a Spatial or data.frame +object
    +

    character vector containing column names can be supplied to specify the expected amount of each feature under each zone. Note that these columns must not contain any NA values.

    -
    planning unit data are a Spatial, data.frame, or -matrix object

    data.frame object can be supplied to specify the + +

    planning unit data are a Spatial, data.frame, or +matrix object
    +

    data.frame object can be supplied to specify the expected amount of each feature under each zone. Following conventions used in Marxan, the -data.frame object should contain the following columns. -

    -
    pu

    integer planning unit identifier.

    -
    species

    integer feature identifier.

    -
    amount

    numeric amount of the feature in the -planning unit for a given zone.

    -Note that data for each zone are specified in a separate argument, and +data.frame object should contain the following columns.

    pu
    +

    integer planning unit identifier.

    + +
    species
    +

    integer feature identifier.

    + +
    amount
    +

    numeric amount of the feature in the +planning unit for a given zone.

    + + +

    Note that data for each zone are specified in a separate argument, and the data contained in a single data.frame object should correspond to a single zone. Also, note that data are not required for all combinations of planning units, features, and zones. The expected amount of features in planning units under management zones that are missing from the table are assumed to be zero.

    -
    - -

    See also

    - +
    +
    +

    See also

    + +
    -

    Examples

    -
    # \dontrun{
    -# load planning unit data
    -data(sim_pu_raster)
    -
    -# (note this requires the RandomFields package to be installed)
    -zone_1 <- simulate_species(sim_pu_raster, 3)
    -#> ...
    -zone_2 <- simulate_species(sim_pu_raster, 3)
    -#> ...
    -
    -# create zones using two raster stack objects
    -# each object corresponds to a different zone and each layer corresponds to
    -# a different species
    -z <- zones(zone_1, zone_2, zone_names = c("zone_1", "zone_2"),
    -           feature_names = c("feature_1", "feature_2", "feature_3"))
    -print(z)
    -#> Zones
    -#>   zones: zone_1, zone_2 (2 zones)
    -#>   features: feature_1, feature_2, feature_3 (3 features)
    -#>   data type: RasterStack
    -
    -# note that the do.call function can also be used to create a Zones object
    -# this method for creating a Zones object can be helpful when there are many
    -# management zones
    -l <- list(zone_1, zone_2, zone_names = c("zone_1", "zone_2"),
    -          feature_names = c("feature_1", "feature_2", "feature_3"))
    -z <- do.call(zones, l)
    -print(z)
    -#> Zones
    -#>   zones: zone_1, zone_2 (2 zones)
    -#>   features: feature_1, feature_2, feature_3 (3 features)
    -#>   data type: RasterStack
    -
    -# create zones using character vectors that represent the names of
    -# fields (columns) in a data.frame or Spatial object that contain the amount
    -# of each species expected different management zones
    -z <- zones(c("spp1_zone1", "spp2_zone1"),
    -           c("spp1_zone2", "spp2_zone2"),
    -           c("spp1_zone3", "spp2_zone3"),
    -           zone_names = c("zone1", "zone2", "zone3"),
    -           feature_names = c("spp1", "spp2"))
    -print(z)
    -#> Zones
    -#>   zones: zone1, zone2, zone3 (3 zones)
    -#>   features: spp1, spp2 (2 features)
    -#>   data type: character
    -# }
    +    
    +

    Examples

    +
    # \dontrun{
    +# load planning unit data
    +data(sim_pu_raster)
    +
    +# (note this requires the RandomFields package to be installed)
    +zone_1 <- simulate_species(sim_pu_raster, 3)
    +#> ...
    +zone_2 <- simulate_species(sim_pu_raster, 3)
    +#> ...
    +
    +# create zones using two raster stack objects
    +# each object corresponds to a different zone and each layer corresponds to
    +# a different species
    +z <- zones(zone_1, zone_2, zone_names = c("zone_1", "zone_2"),
    +           feature_names = c("feature_1", "feature_2", "feature_3"))
    +print(z)
    +#> Zones
    +#>   zones: zone_1, zone_2 (2 zones)
    +#>   features: feature_1, feature_2, feature_3 (3 features)
    +#>   data type: RasterStack
    +
    +# note that the do.call function can also be used to create a Zones object
    +# this method for creating a Zones object can be helpful when there are many
    +# management zones
    +l <- list(zone_1, zone_2, zone_names = c("zone_1", "zone_2"),
    +          feature_names = c("feature_1", "feature_2", "feature_3"))
    +z <- do.call(zones, l)
    +print(z)
    +#> Zones
    +#>   zones: zone_1, zone_2 (2 zones)
    +#>   features: feature_1, feature_2, feature_3 (3 features)
    +#>   data type: RasterStack
    +
    +# create zones using character vectors that represent the names of
    +# fields (columns) in a data.frame or Spatial object that contain the amount
    +# of each species expected different management zones
    +z <- zones(c("spp1_zone1", "spp2_zone1"),
    +           c("spp1_zone2", "spp2_zone2"),
    +           c("spp1_zone3", "spp2_zone3"),
    +           zone_names = c("zone1", "zone2", "zone3"),
    +           feature_names = c("spp1", "spp2"))
    +print(z)
    +#> Zones
    +#>   zones: zone1, zone2, zone3 (3 zones)
    +#>   features: spp1, spp2 (2 features)
    +#>   data type: character
    +# }
     
    +
    +
    - - - - - - - - - - - + diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 585a5522f..5f1eaf72d 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -7,28 +7,31 @@ https://prioritizr.net/CONTRIBUTING.html - https://prioritizr.net/articles/gurobi_installation.html + https://prioritizr.net/articles/calibrating_trade-offs_tutorial.html - https://prioritizr.net/articles/index.html + https://prioritizr.net/articles/connectivity_tutorial.html - https://prioritizr.net/articles/prioritizr.html + https://prioritizr.net/articles/gurobi_installation_guide.html - https://prioritizr.net/articles/publication_record.html + https://prioritizr.net/articles/index.html - https://prioritizr.net/articles/saltspring.html + https://prioritizr.net/articles/management_zones_tutorial.html - https://prioritizr.net/articles/solver_benchmark.html + https://prioritizr.net/articles/package_overview.html - https://prioritizr.net/articles/tasmania.html + https://prioritizr.net/articles/prioritizr.html + + + https://prioritizr.net/articles/publication_record.html - https://prioritizr.net/articles/zones.html + https://prioritizr.net/articles/solver_benchmarks.html https://prioritizr.net/authors.html diff --git a/inst/doc/calibrating_trade-offs_tutorial.Rmd b/inst/doc/calibrating_trade-offs_tutorial.Rmd new file mode 100644 index 000000000..22089a2c7 --- /dev/null +++ b/inst/doc/calibrating_trade-offs_tutorial.Rmd @@ -0,0 +1,678 @@ +--- +title: "Calibrating trade-offs tutorial" +output: + rmarkdown::html_vignette: + toc: true + fig_caption: true + self_contained: yes +fontsize: 11pt +documentclass: article +bibliography: references.bib +csl: reference-style.csl +vignette: > + %\VignetteIndexEntry{Calibrating trade-offs tutorial} + %\VignetteEngine{knitr::rmarkdown_notangle} +--- + +```{r, include = FALSE} +# define dummy variables so that vignette passes package checks +prelim_penalty <- rep(NA_real_, 100) +threshold <- rep(NA_real_, 100) +topsis_results <- data.frame( + alt.row = seq_len(3), score = runif(3), rank = seq_len(3) +) +``` + +```{r, include = FALSE} +# define variables for vignette figures and code execution +h <- 3.5 +w <- 3.5 +is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", + "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) +knitr::opts_chunk$set(fig.align = "center", eval = !is_check) +``` + +## Introduction + +Systematic conservation planning requires making trade-offs [@r4; @r36]. Since different criteria may conflict with one another -- or not align perfectly -- prioritizations need to make trade-offs between different criteria [@r37]. Although some criteria can easily be accounted for by using locked constraints or representation targets [e.g., @r40; @r39], this is not always the case [e.g., @r38]. For example, prioritizations often need to balance overall cost with the overall level spatial fragmentation among reserves [@r41; @r42]. Additionally, prioritizations often need to balance the overall level of connectivity among reserves against other criteria [@r43]. Since the best trade-off depends on a range of factors -- such as available budgets, species' connectivity requirements, and management capacity -- finding the best balance can be challenging. + +The _prioritizr R_ package provides multi-objective optimization methods to help identify the best trade-offs between different criteria. To achieve this, a conservation planning problem can be formulated with a primary objective (e.g., `add_min_set_objective()`) and penalties (e.g., `add_boundary_penalties()`) that relate to such criteria. When building the problem, the nature of the trade-offs can be specified using certain parameters (e.g., the `penalty` parameter of the `add_boundary_penalties()` function). To identify a prioritization that finds the best balance between different criteria, the trade-off parameters can be tuned using a calibration analysis. These analyses -- in the context of systematic conservation planning -- typically involve generating a set of candidate prioritizations based on different parameters, measuring their performance according to each of the criteria, and then selecting a prioritization (or set of prioritizations) based on how well they achieve the criteria [@r41; @r42; @r43]. For example, the _Marxan_ decision support tool has a range of parameters (e.g., species penalty factors, boundary length modifier) that are calibrated to balance cost, species' representation, and spatial fragmentation [@r45]. + +The aim of this tutorial is to provide guidance on calibrating trade-offs when using the _prioritizr R_ package. Here we will explore a couple of different approaches for generating candidate prioritizations, and methods for finding the best balance between different criteria. Specifically, we will try to generate prioritizations that strike the best balance between total cost and spatial fragmentation (measured as total boundary length). As such, the code used in this vignette will be directly applicable when performing a boundary length calibration analysis. + +## Data + +Let's load the packages and dataset used in this tutorial. Since this tutorial uses the _prioritizrdata R_ package along with several other _R_ packages (see below), please ensure that they are all installed. This particular dataset comprises two object: `tas_pu` and `tas_features`. Although we will briefly discuss this dataset below, please refer to the _Tasmania Tutorial_ vignette for further details. + +```{r, message = FALSE} +# load packages +library(prioritizrdata) +library(prioritizr) +library(dplyr) +library(tibble) +library(scales) +library(ggplot2) +library(topsis) +library(withr) + +# load planning unit data +data(tas_pu) + +# convert planning units to sf format +tas_pu <- st_as_sf(tas_pu) + +# load feature data +data(tas_features) + +# print planning unit data +print(tas_pu) + +# print feature data +print(tas_features) +``` + +The `tas_pu` object contains planning units represented as spatial polygons (i.e., converted to a `sf::st_sf()` object). This object has three columns that denote the following information for each planning unit: a unique identifier (`id`), unimproved land value (`cost`), and current conservation status (`locked_in`). Specifically, the conservation status column indicates if at least half the area planning unit is covered by existing protected areas (denoted by a value of 1) or not (denoted by a value of zero). + +```{r, fig.width = w, fig.height = h} +# plot map of planning unit costs +plot(tas_pu[, "cost"], main = "Planning unit costs") + +# plot map of planning unit statuses +plot(tas_pu[, "locked_in"], main = "Planning unit status") +``` + +The `tas_features` object describes the spatial distribution of different vegetation communities (using presence/absence data). We will use the vegetation communities as the biodiversity features for the prioritization. + +```{r, fig.width = 4.5, fig.height = 4.5} +# plot map of the first four vegetation classes +plot(tas_features[[1:4]], main = paste("Feature", 1:4)) +``` + +We can use this dataset to generate a prioritization. Specifically, we will use the minimum set objective so that the optimization process minimizes total cost. We will add representation targets to ensure that prioritizations cover 17% of each vegetation community. Additionally, we will add constraints to ensure that planning units covered by existing protected areas are selected (i.e., locked in). Finally, we will specify that the conservation planning exercise involves binary decisions (i.e., selecting or not selecting planning units for protected area establishment). + +```{r, fig.width = w, fig.height = h, results = "hide"} +# define a problem +p0 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# print problem +print(p0) + +# solve problem +s0 <- solve(p0) + +# print result +print(s0) + +# create column for making a map of the prioritization +s0$map_1 <- case_when( + s0$locked_in > 0.5 ~ "locked in", + s0$solution_1 > 0.5 ~ "priority", + TRUE ~ "other" +) + +# plot map of prioritization +plot( + s0[, "map_1"], pal = c("purple", "grey90", "darkgreen"), + main = NULL, key.pos = 1 +) +``` + +We can see that the priority areas identified by the prioritization are scattered across the study area (shown in green). Indeed, none of the priority areas are connect to existing protected areas (shown in purple), and very of them are connect with other priority areas. As such, the prioritization has a high level of spatial fragmentation. If it is important avoid such levels of spatial fragmentation, then we will need to explicitly account spatial fragmentation in the optimization process. + +## Preliminary processing + +We need to conduct some preliminary processing procedures to prepare the data for subsequent analysis. This is important to help make it easier to find suitable trade-off parameters, and avoid numerical scaling issues that can result in overly long run times (see `presolve_check()` for further information). These processing steps are akin to data scaling (or normalization) procedures that are applied in statistical analysis to improve model convergence. + +The first processing procedure involves setting the cost values for all locked in planning units to zero. This is so that the total cost estimates of the prioritization reflects the total cost of establishing new protected areas -- not just total land value. In other words, we want the total cost estimate for a prioritization to reflect the cost of implementing conservation actions. **This procedure is especially important when using the hierarchical approach described below, so that cost thresholds are based on percentage increases in the cost of establishing new protected areas.** + +```{r, fig.width = w, fig.height = h} + +# set costs for planning units covered by existing protected areas to zero +tas_pu$cost[tas_pu$locked_in > 0.5] <- 0 + +# plot map of planning unit costs +plot(tas_pu[, "cost"], main = "Planning unit cost") +``` + +The second procedure involves pre-computing the boundary length data and manually re-scaling the boundary length values. This procedure is important because boundary length values are often very large that, in turn, can cause numerical issues that result in excessive run times (see `presolve_check()` for further details). + +```{r} +# generate boundary length data for the planning units +tas_bd <- boundary_matrix(tas_pu) + +# manually re-scale the boundary length values +tas_bd@x <- rescale(tas_bd@x, to = c(0.01, 100)) +``` + +After applying these procedures, our data is ready for subsequent analysis. + +## Generating candidate prioritizations + +Here we will start the calibration analysis by generating a set of candidate prioritizations. Specifically, these prioritizations will be generated using different parameters to specify different trade-offs between the different criteria. Since this tutorial involves navigating trade-offs between the overall cost of a prioritization and the level of spatial fragmentation associated with a prioritization (as measured by total boundary length), we will generate prioritizations using different parameters related to these criteria. We will examine two approaches for generating candidate prioritizations based on multi-objective optimization procedures. +**Although we'll be examining both approaches in this tutorial, you would normally only use one of these approaches when conducting your own analysis** + +### Blended approach + +The blended approach for multi-objective optimization involves combining separate criteria (e.g., total cost and total boundary length) into a single joint criterion. To achieve this, a trade-off (or scaling) parameter is used to specify the relative importance of each criterion. This approach is the default approach provided by the _prioritizr R_ package. Specifically, each of the functions for adding a penalty to a problem formulation (e.g., `add_boundary_penalties()`) contains a parameter to control the relative importance of the penalties (i.e., the `penalty` parameter). For example, when using the `add_boundary_penalties()` function, setting a high `penalty` value will indicate that it is important to reduce the overall exposed boundary (perimeter) of the prioritization. + +The main challenge with the blended approach is identifying a range of suitable `penalty` values to generate candidate prioritizations. If we set a `penalty` value that is too low, then the penalties will have no effect (e.g., boundary length penalties would have no effect on the prioritization). If we set a `penalty` value too high, then the prioritization will effectively ignore the primary objective. In such cases, the prioritization will be overly spatially clustered -- because the planning unit cost values have no effect --- and contain a single reserve. Thus we need to find a suitable range of `penalty` values before we can generate a set of candidate prioritizations. + +We can find a suitable range of `penalty` values by generating a set of preliminary prioritizations. These preliminary prioritizations will be based on different `penalty` values -- similar to the process for generating the candidate prioritizations -- but solved using customized settings that sacrifice optimality for fast run times (see below for details). This is especially important because specifying a `penalty` value that is too high will cause the optimization process to take a very long time to generate a solutions (due to the numerical scaling issues mentioned previously). To find a suitable range of `penalty` values, we need to identify an upper limit for the `penalty` value (i.e., the highest `penalty` value that result in a prioritization containing a single reserve). Let's create some preliminary `penalty` to identify this upper limit. **Please note that you might need to adjust the `prelim_upper` value to find the upper limit when analyzing different datasets.** + +```{r} +# define a range of different penalty values +## note that we use a power scale to avoid focusing on very high penalty values +prelim_lower <- -5 # change this for your own data +prelim_upper <- 2.8 # change this for your own data +prelim_penalty <- round(10^seq(prelim_lower, prelim_upper, length.out = 9), 5) + +# print penalty values +print(prelim_penalty) +``` + +Next, let's use the preliminary `penalty` values to generate preliminary prioritizations. As mentioned earlier, we will generate these preliminary prioritizations using customized settings to reduce runtime. Specifically, we will set a time limit of 10 minutes per run, and relax the optimality gap to 20%. Although we would not normally use such settings -- because the resulting prioritizations are not guaranteed to be near-optimal (the default gap is 10%) -- this is fine because our goal here is to tune the preliminary `penalty` values. Indeed, none of these preliminary prioritizations will be considered as candidate prioritizations. **Please note that you might need to set a higher time limit, or relax the optimality gap even further (e.g., 40%) when analyzing larger datasets.** + +```{r, results = "hide"} +# define a problem without boundary penalties +p0 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# generate preliminary prioritizations based on each penalty +## note that we specify a relaxed gap and time limit for the solver +prelim_blended_results <- lapply(prelim_penalty, function(x) { + s <- + p0 %>% + add_boundary_penalties(penalty = x, data = tas_bd) %>% + add_default_solver(gap = 0.2, time_limit = 10 * 60) %>% + solve() + s <- data.frame(s = s$solution_1) + names(s) <- with_options(list(scipen = 30), paste0("penalty_", x)) + s +}) + +# format results as a single spatial object +prelim_blended_results <- cbind( + tas_pu, do.call(bind_cols, prelim_blended_results) +) + +# preview results +print(prelim_blended_results) +``` + +After generating the preliminary prioritizations, let's create some maps to visualize them. In particular, we want to understand how different penalty values influence the spatial fragmentation of the prioritizations. + +```{r, fig.width = 7, fig.height = 5.0} +# plot maps of prioritizations +plot( + x = + prelim_blended_results %>% + dplyr::select(starts_with("penalty_")) %>% + mutate_if(is.numeric, function(x) { + case_when( + prelim_blended_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +We can see that as the `penalty` value used to generate the prioritizations increases, the spatial fragmentation of the prioritizations decreases. In particular, we can see that a `penalty` value of `r prelim_penalty[8]` results in a single reserve -- meaning this is our best guess of the upper limit. Using this `penalty` value as an upper limit, we will now generate a second series of prioritizations that will be the candidate prioritizations. Critically, these candidate prioritizations will not be generated using with time limit and be generated using a more suitable gap (i.e., default gap of 10%). + +```{r, fig.width = 7, fig.height = 5.0, results = "hide"} +# define a new set of penalty values +## note that we use a linear scale to explore both low and high penalty values +penalty <- round(seq(1e-5, prelim_penalty[8], length.out = 9), 5) + +# generate prioritizations based on each penalty +blended_results <- lapply(penalty, function(x) { + ## generate solution + s <- + p0 %>% + add_boundary_penalties(penalty = x, data = tas_bd) %>% + solve() + ## return data frame with solution + s <- data.frame(s = s$solution_1) + names(s) <- with_options(list(scipen = 30), paste0("penalty_", x)) + s +}) + +# format results as a single spatial object +blended_results <- cbind(tas_pu, do.call(bind_cols, blended_results)) + +# plot maps of prioritizations +plot( + x = + blended_results %>% + dplyr::select(starts_with("penalty_")) %>% + mutate_if(is.numeric, function(x) { + case_when( + blended_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +We now have a set of candidate prioritizations generated using the blended approach. The main advantages of this approach is that it is similar calibration analyses used by other decision support tools for conservation (i.e., _Marxan_) and it is relatively straightforward to implement. However, this approach also has a key disadvantage. Because the `penalty` parameter is a unitless trade-off parameter -- meaning that we can't leverage existing knowledge to specify a suitable range of `penalty` values -- we first have to conduct a preliminary analysis to identify an upper limit. Although finding an upper limit was fairly simple for the example datset, it can be difficult to find for more realistic data. In the next section, we will show how to generate a set of candidate prioritzations using the hierachical approach -- which does not have this disadvantage. + +### Hierarchical approach + +The hierarchical approach for multi-objective optimization involves generating a series of incremental prioritizations -- using a different objective at each increment to refine the previous solution -- until the final solution achieves all of the objectives. The advantage with this approach is that we can specify trade-off parameters for each objective based on a percentage from optimality. This means that we can leverage our own knowledge -- or that of decision maker -- when to generate a range of suitable trade-off parameters. As such, this approach does not require us to generate a series of preliminary prioritizations. + +This approach is slightly more complicated to implement within the _prioritizr R_ package then the blended approach. To start off, we generate an initial prioritization based on a problem formulation that does not consider any penalties. Critically, we will generate this prioritization by solving the problem to optimality (using the `gap` parameter of the `add_default_solver()` function). + +```{r, fig.width = w, fig.height = h, results = "hide"} +# define a problem without boundary penalties +p1 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() %>% + add_default_solver(gap = 0) + +# solve problem +s1 <- solve(p1) + +# add column for making a map of the prioritization +s1$map_1 <- case_when( + s1$locked_in > 0.5 ~ "locked in", + s1$solution_1 > 0.5 ~ "priority", + TRUE ~ "other" +) + +# plot map of prioritization +plot( + s0[, "map_1"], pal = c("purple", "grey90", "darkgreen"), + main = NULL, key.pos = 1 +) +``` + +Next, we will calculate the total cost of the initial prioritization. + +```{r, fig.width = w, fig.height = h} +# calculate cost +s1_cost <- eval_cost_summary(p1, s1[, "solution_1"])$cost + +# print cost +print(s1_cost) +``` + +Now we will calculate a series of cost thresholds. These cost thresholds will be calculated by inflating the cost of the initial prioritization by a range of percentage values. Since these values are percentages -- and not unitless values unlike those used in the blended approach -- we can use domain knowledge to specify a suitable range of cost thresholds. For this tutorial, let's assume that it would be impractical -- per our domain knowledge -- to expend more than four times the total cost of the initial prioritization to reduce spatial fragmentation. + +```{r} +# calculate cost threshold values +threshold <- s1_cost + (s1_cost * seq(1e-5, 4, length.out = 9)) +threshold <- ceiling(threshold) + +# print cost thresholds +print(threshold) +``` + +After generating the cost thresholds, we can use them to generate prioritizations. Specifically, we will generate prioritizations that aim to minimize total boundary length as much as possible -- ignoring the total cost of the prioritizations -- whilst ensuring that the total cost of the prioritization does not exceed a given cost threshold and the other considerations (e.g., locked in constraints). To achieve this, we create a new column in the `tas_pu` object that contains only zero values (called `zeros`) and use this new column to specify the cost data for the prioritizations. +**Although we normally recommend against cost data that contain zero values -- because planning units with zero costs are often selected in prioritizations even if they are not needed -- here we use zero cost values so that the prioritization will focus exclusively on spatial fragmentation.** Additionally, when it comes to generating the prioritization, we will add linear constraints to ensure that the total cost of the prioritization does not exceed a given cost threshold (using the `add_linear_constraints()` function). + +```{r, fig.width = 7, fig.height = 5.0, results = "hide"} +# add a column with zeros +tas_pu$zeros <- 0 + +# define a problem with zero cost values and boundary penalties +## note that because all the costs are all zero, it doesn't actually +## matter what penalty value is used (as long as the value is > 0) +## and so we just use a value of 1 +p2 <- problem(tas_pu, tas_features, cost_column = "zeros") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 1, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# generate prioritizations based on each cost threshold +## note that the prioritizations are solved to within 10% of optimality +## (the default gap) because the gap is not specified +hierarchical_results <- lapply(threshold, function(x) { + ## generate solution by adding a constraint based on the threshold and + ## using the "real" cost values (i.e., not zeros) + s <- + p2 %>% + add_linear_constraints(threshold = x, sense = "<=", data = "cost") %>% + solve() + ## return data frame with solution + s <- data.frame(s = s$solution_1) + names(s) <- paste0("threshold_", x) + s +}) + +# format results as a single spatial object +hierarchical_results <- cbind(tas_pu, do.call(bind_cols, hierarchical_results)) + +# plot maps of prioritizations +plot( + x = + hierarchical_results %>% + dplyr::select(starts_with("threshold_")) %>% + mutate_if(is.numeric, function(x) { + case_when( + hierarchical_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +We now have a set of candidate prioritizations generated using the hierarchical approach. This approach can be much faster than the blended approach because it does not require generating a set of prioritizations to identify an upper limit for the `penalty` trade-off parameter. After generating a set of candidate prioritizations, we can then calculate performance metrics to compare the prioritizations. + +## Calculating performance metrics + +Here we will calculate performance metrics to compare the prioritizations. Since we aim to navigate trade-offs between the total cost of a prioritization and the overall level of spatial fragmentation associated with a prioritization (as measured by total boundary length), we will calculate metrics to assess these criteria. Although we generated two sets of candidate prioritizations in the previous section; for brevity, here we will consider the candidate prioritizations generated using the hierarchical approach. **Please note that you could also apply the following procedures to candidate prioritizations generated using the blended approach.** + +```{r} +# calculate metrics for prioritizations +## note that we use p0 and not p1 so that cost calculations are based +## on the cost values and not zeros +hierarchical_metrics <- lapply( + grep("threshold_", names(hierarchical_results)), function(x) { + x <- hierarchical_results[, x] + data.frame( + total_cost = eval_cost_summary(p0, x)$cost, + total_boundary_length = eval_boundary_summary(p0, x)$boundary + ) + } +) +hierarchical_metrics <- do.call(bind_rows, hierarchical_metrics) +hierarchical_metrics$threshold <- threshold +hierarchical_metrics <- as_tibble(hierarchical_metrics) + +# preview metrics +print(hierarchical_metrics) +``` + +After calculating the metrics, let's we can use them to help select a prioritization. + +## Selecting a prioritization + +Now we need to decide on which candidate prioritization achieves the best trade-off. There are a range of qualitative and quantitative methods that are available to select a candidate prioritization [@r45]. Here we will consider three different methods. Since some of these methods a set of candidate prioritizations, we will use the candidate prioritizations using the hierarchical approach for these methods. To keep track of the prioritizations selected by different methods, let's create a `results_data` table. + +```{r} +# create data for plotting +result_data <- + hierarchical_metrics %>% + ## rename threshold column to value column + rename(value = "threshold") %>% + ## add column with column names that contain candidate prioritizations + mutate(name = grep( + "threshold_", names(hierarchical_results), value = TRUE, fixed = TRUE + )) %>% + ## add column with labels for plotting + mutate(label = paste("Threshold =", value)) %>% + ## add column to keep track prioritizations selected by different methods + mutate(method = "none") + +# print table +print(result_data) +``` + +Next, let's examine some different methods for selecting prioritizations. + +### Visual method + +One qualitative method involves plotting the relationship between the different criteria, and using the plot to visually select a candidate prioritization. This visual method is often used to help calibrate trade-offs among prioritizations generated using the _Marxan_ decision support tool [e.g., @r41; @r42]. So, let's create a plot to select a prioritization. + +```{r, fig.width = 7, fig.height = 5.0} +# create plot to visualize trade-offs and show selected candidate prioritization +result_plot <- + ggplot( + data = result_data, + aes(x = total_boundary_length, y = total_cost, label = label) + ) + + geom_line() + + geom_point(size = 3) + + geom_text(hjust = -0.15) + + scale_color_manual( + values = c("visual" = "blue", "not selected" ="black") + ) + + xlab("Total boundary length of prioritization") + + ylab("Total cost of prioritization") + + scale_x_continuous(expand = expansion(mult = c(0.05, 0.4))) + + theme(legend.title = element_blank()) + +# render plot +print(result_plot) +``` + +We can see that there is a clear relationship between total cost and total boundary length. It would seem that in order to achieve a lower total boundary length -- and thus lower spatial fragmentation -- the prioritization must have a greater cost. Although we might expect the results to show a smoother curve -- in other words, only Pareto dominant solutions -- this result is expected because we generated candidate prioritizations using the default optimality gap of 10%. Typically, the visual method involves selecting a prioritization near the elbow of the plot. So, let's select the prioritization generated using a `threshold` value of `r threshold[3]`. To keep of the prioritizations selected based on different methods, let's create a `method` column in the `result_data` table. + +```{r} +# specify prioritization selected by visual method +result_data$method[3] <- "visual" +``` + +Next, let's consider a quantitative approach. + +### TOPSIS method + +Multiple-criteria decision analysis is a disciple that uses analytical methods to evaluate trade-offs between multiple criteria [MCDA; reviewed in @r44]. Although this discipline contains many different methods, here we will use the the Technique for Order of Preference by Similarity to Ideal Solution (TOPSIS) method [@r46]. This method requires (i) data describing the performance of each prioritization according the different criteria, (ii) weights to encode the relative importance of each criteria, and (iii) details on whether each criteria should ideally be minimized or maximized. Let's run the analysis, assuming that we equal weighting for total cost and total boundary length. + +```{r} +# calculate TOPSIS scores +topsis_results <- topsis( + decision = + hierarchical_metrics %>% + dplyr::select(total_cost, total_boundary_length) %>% + as.matrix(), + weights = c(1, 1), + impacts = c("-", "-") +) + +# print results +print(topsis_results) +``` + +The candidate prioritization with the greatest TOPSIS score is considered to represent the best trade-off between total cost and total boundary length. So, based on this method, we would select the prioritization generated using a `threshold` value of `r threshold[which.max(topsis_results$score)]`. Let's update the `result_data` with this information. + +```{r} +# add column indicating prioritization selected by TOPSIS method +result_data$method[which.max(topsis_results$score)] <- "TOPSIS" +``` + +Next, let's consider another quantitative method. + +### Cohon *et al.* (1979) method + +This method is based on an algorithm that was originally developed by Cohon *et al.* [@r49], and was later adapted for use in systematic conservation planning [@r50]. Specifically, it involves generating two ideal prioritizations -- with each prioritization representing the ideal prioritization for each criteria -- and then using the performance metrics calculated for these prioritizations to automatically derive a trade-off `penalty` value [@r45; @r49]. Thus, unlike the two other methods, this method does not require a set of candidate prioritizations. **As such, this method can be used to find a prioritization that meets multiple criteria in a much shorter period of time than the other methods.** To implement this method, we first need to generate the ideal prioritizations (note that we specify an gap of 0% to ensure optimality). + +```{r, results = "hide"} +# generate ideal prioritization based on cost criteria +## note that this is simply the same as the s1 prioritization we generated +## for the hierarchical approach +p3 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() %>% + add_default_solver(gap = 0) + +# solve problem +s3 <- solve(p3) + +# generate ideal prioritization based on spatial fragmentation criteria +## note that any non-zero penalty value would here, +## so we just use a penalty of 1 +p4 <- problem(tas_pu, tas_features, cost_column = "zeros") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 1, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() %>% + add_default_solver(gap = 0) + +# solve problem +s4 <- solve(p4) +``` + +Next, let's calculate the performance metrics for these prioritizations. + +```{r} +# generate problem formulation with costs and boundary penalties for +# calculating performance metrics +p5 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 1, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# calculate performance metrics for ideal cost prioritization +s3_metrics <- tibble( + total_cost = eval_cost_summary(p5, s3[, "solution_1"])$cost, + total_boundary_length = + eval_boundary_summary(p5, s3[, "solution_1"])$boundary +) + +# calculate performance metrics for ideal boundary length prioritization +s4_metrics <- tibble( + total_cost = eval_cost_summary(p5, s4[, "solution_1"])$cost, + total_boundary_length = + eval_boundary_summary(p5, s4[, "solution_1"])$boundary +) +``` + +After calculating these performance metrics, we can use them to automatically calculate a `penalty` value. + +```{r} +# calculate penalty value based on Cohon et al. 1979 +cohon_penalty <- abs( + (s3_metrics$total_cost - s4_metrics$total_cost) / + (s3_metrics$total_boundary_length - s4_metrics$total_boundary_length) +) + +# round to 5 decimal places to avoid numerical issues during optimization +cohon_penalty <- round(cohon_penalty, 5) + +# print penalty value +print(cohon_penalty) +``` + +Now that we have calculated a `penalty` value using this method, we can use it to generate a prioritization. + +```{r, results = "hide"} +# generate prioritization using penalty value calculated using Cohon et al. 1979 +p6 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = cohon_penalty, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# solve problem +s6 <- solve(p6) +``` + +Let's update the `results_data` table with results about the prioritization. + +```{r} +# add new row with data for prioritization generated following Cohon et al. 1979 +result_data <- bind_rows( + result_data, + tibble( + total_cost = eval_cost_summary(p6, s6[, "solution_1"])$cost, + total_boundary_length = + eval_boundary_summary(p6, s6[, "solution_1"])$boundary, + value = cohon_penalty, + name = paste0("penalty_", cohon_penalty), + label = paste0("Penalty = ", cohon_penalty), + method = "Cohon" + ) +) +``` + +Next, let's compare the prioritizations selected by different methods. + +### Method comparison + +Let's create a plot to visualize the results from the different methods. + +```{r, fig.width = 7, fig.height = 5.0} +# create plot to visualize trade-offs and show selected candidate prioritization +result_plot <- + ggplot( + data = + result_data %>% + mutate(vjust = if_else(method == "Cohon", -1, 0.5)), + aes(x = total_boundary_length, y = total_cost, label = label) + ) + + geom_line() + + geom_point(aes(color = method), size = 3) + + geom_text(aes(vjust = vjust, color = method), hjust = -0.1) + + scale_color_manual( + name = "Method", + values = c( + "visual" = "#984ea3", + "none" = "#000000", + "TOPSIS" = "#e41a1c", + "Cohon" = "#377eb8" + ) + ) + + xlab("Total boundary length of prioritization") + + ylab("Total cost of prioritization") + + scale_x_continuous(expand = expansion(mult = c(0.05, 0.4))) + +# render plot +print(result_plot) +``` + +We can see that the different method selected different prioritizations. To further compare the results from the different methods, let's create some maps showing the selected prioritizations. + +```{r, fig.width = 7.0, fig.height = h} +# extract column names for creating the prioritizations +visual_name <- result_data$name[[which(result_data$method == "visual")]] +topsis_name <- result_data$name[[which(result_data$method == "TOPSIS")]] + +# create object with selected prioritizations +solutions <- bind_cols( + tas_pu, + hierarchical_results %>% + st_drop_geometry() %>% + dplyr::select(all_of(c(visual_name, topsis_name))) %>% + setNames(c("Visual", "TOPSIS")), + s6 %>% + st_drop_geometry() %>% + dplyr::select(solution_1) %>% + rename(Cohon = "solution_1") +) + +# plot maps of selected prioritizations +plot( + x = + solutions %>% + dplyr::select(Visual, TOPSIS, Cohon) %>% + mutate_if(is.numeric, function(x) { + case_when( + hierarchical_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +How do we determine which one is best? This is difficult to say. Ideally, additional information could be used to help select a prioritization, such as knowledge on available resources, species' connectivity requirements, and impacts of neighboring land use. However, from a practical perspective, prioritizations generated for academic contexts might find the quantitative approaches more useful because they have greater transparency and reproducibility. Ultimately, all of these methods are designed to support decision making. This means that they are intended to assist the decision making process, not serve as a replacement. + +## Conclusion + +Hopefully, this vignette has provided a useful introduction for resolving trade-offs in prioritizations. Although we only explored trade-offs between total cost and spatial fragmentation in this tutorial, this analysis could be adapted to explore trade-offs between a wide range of different criteria. For instance, instead of considering total cost as the primary objective, future analyses could explore trade-offs with feature representation (using the `add_min_shortfall_objective()` function). Additionally, instead of spatial fragmentation, future analyses could explore trade-offs that directly relate to connectivity (using the `add_connectivity_penalties()` function) or specific variables of interest -- such as ecosystem intactness or inverse human footprint index [@r47; @r48] -- to inform decision making (using the `add_linear_penalties()` function). Furthermore, after identifying the best `penalty` or `threshold` values to strike a balance between multiple criteria, you could generate a portfolio of prioritizations (e.g., using the `add_gap_portfolio_function()`) to find multiple options for achieving a similar balance. This might be helpful when you need to generate a set of prioritizations that have comparable performance -- in terms of how well they achieve different criteria -- but select different planning units. + +## References diff --git a/inst/doc/calibrating_trade-offs_tutorial.html b/inst/doc/calibrating_trade-offs_tutorial.html new file mode 100644 index 000000000..873c2fc03 --- /dev/null +++ b/inst/doc/calibrating_trade-offs_tutorial.html @@ -0,0 +1,868 @@ + + + + + + + + + + + + + + +Calibrating trade-offs tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Calibrating trade-offs tutorial

    + + + + +
    +

    Introduction

    +

    Systematic conservation planning requires making trade-offs (Margules & Pressey 2000; Vane-Wright et al. 1991). Since different criteria may conflict with one another – or not align perfectly – prioritizations need to make trade-offs between different criteria (Klein et al. 2013). Although some criteria can easily be accounted for by using locked constraints or representation targets (e.g., Dorji et al. 2020; Hermoso et al. 2018), this is not always the case (e.g., Beger et al. 2010). For example, prioritizations often need to balance overall cost with the overall level spatial fragmentation among reserves (Hermoso et al. 2011; Stewart & Possingham 2005). Additionally, prioritizations often need to balance the overall level of connectivity among reserves against other criteria (Hermoso et al. 2012). Since the best trade-off depends on a range of factors – such as available budgets, species’ connectivity requirements, and management capacity – finding the best balance can be challenging.

    +

    The prioritizr R package provides multi-objective optimization methods to help identify the best trade-offs between different criteria. To achieve this, a conservation planning problem can be formulated with a primary objective (e.g., add_min_set_objective()) and penalties (e.g., add_boundary_penalties()) that relate to such criteria. When building the problem, the nature of the trade-offs can be specified using certain parameters (e.g., the penalty parameter of the add_boundary_penalties() function). To identify a prioritization that finds the best balance between different criteria, the trade-off parameters can be tuned using a calibration analysis. These analyses – in the context of systematic conservation planning – typically involve generating a set of candidate prioritizations based on different parameters, measuring their performance according to each of the criteria, and then selecting a prioritization (or set of prioritizations) based on how well they achieve the criteria (Hermoso et al. 2011; Stewart & Possingham 2005; Hermoso et al. 2012). For example, the Marxan decision support tool has a range of parameters (e.g., species penalty factors, boundary length modifier) that are calibrated to balance cost, species’ representation, and spatial fragmentation (Ardron et al. 2010).

    +

    The aim of this tutorial is to provide guidance on calibrating trade-offs when using the prioritizr R package. Here we will explore a couple of different approaches for generating candidate prioritizations, and methods for finding the best balance between different criteria. Specifically, we will try to generate prioritizations that strike the best balance between total cost and spatial fragmentation (measured as total boundary length). As such, the code used in this vignette will be directly applicable when performing a boundary length calibration analysis.

    +
    +
    +

    Data

    +

    Let’s load the packages and dataset used in this tutorial. Since this tutorial uses the prioritizrdata R package along with several other R packages (see below), please ensure that they are all installed. This particular dataset comprises two object: tas_pu and tas_features. Although we will briefly discuss this dataset below, please refer to the Tasmania Tutorial vignette for further details.

    +
    # load packages
    +library(prioritizrdata)
    +library(prioritizr)
    +library(dplyr)
    +library(tibble)
    +library(scales)
    +library(ggplot2)
    +library(topsis)
    +library(withr)
    +
    +# load planning unit data
    +data(tas_pu)
    +
    +# convert planning units to sf format
    +tas_pu <- st_as_sf(tas_pu)
    +
    +# load feature data
    +data(tas_features)
    +
    +# print planning unit data
    +print(tas_pu)
    +
    ## Simple feature collection with 1130 features and 5 fields
    +## Geometry type: MULTIPOLYGON
    +## Dimension:     XY
    +## Bounding box:  xmin: 298809.6 ymin: 5167775 xmax: 613818.8 ymax: 5502544
    +## Projected CRS: WGS 84 / UTM zone 55S
    +## First 10 features:
    +##   id      cost status locked_in locked_out                       geometry
    +## 0  1 60.246377      0     FALSE      FALSE MULTIPOLYGON (((328497 5497...
    +## 1  2 19.863008      0     FALSE      FALSE MULTIPOLYGON (((307121.6 54...
    +## 2  3 59.680513      0     FALSE      FALSE MULTIPOLYGON (((321726.1 54...
    +## 3  4 32.416138      0     FALSE      FALSE MULTIPOLYGON (((304314.5 54...
    +## 4  5 26.177062      0     FALSE      FALSE MULTIPOLYGON (((314958.5 54...
    +## 5  6 51.262177      0     FALSE      FALSE MULTIPOLYGON (((327904.3 54...
    +## 6  7 32.299112      0     FALSE      FALSE MULTIPOLYGON (((308194.1 54...
    +## 7  8 38.404063      0     FALSE      FALSE MULTIPOLYGON (((322792.7 54...
    +## 8  9  3.554745      0     FALSE      FALSE MULTIPOLYGON (((334896.6 54...
    +## 9 10  1.834921      0     FALSE      FALSE MULTIPOLYGON (((356377.1 54...
    +
    # print feature data
    +print(tas_features)
    +
    ## class      : RasterStack 
    +## dimensions : 398, 359, 142882, 62  (nrow, ncol, ncell, nlayers)
    +## resolution : 1000, 1000  (x, y)
    +## extent     : 288801.7, 647801.7, 5142976, 5540976  (xmin, xmax, ymin, ymax)
    +## crs        : +proj=utm +zone=55 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## names      : tas_features.1, tas_features.2, tas_features.3, tas_features.4, tas_features.5, tas_features.6, tas_features.7, tas_features.8, tas_features.9, tas_features.10, tas_features.11, tas_features.12, tas_features.13, tas_features.14, tas_features.15, ... 
    +## min values :              0,              0,              0,              0,              0,              0,              0,              0,              0,               0,               0,               0,               0,               0,               0, ... 
    +## max values :              1,              1,              1,              1,              1,              1,              1,              1,              1,               1,               1,               1,               1,               1,               1, ...
    +

    The tas_pu object contains planning units represented as spatial polygons (i.e., converted to a sf::st_sf() object). This object has three columns that denote the following information for each planning unit: a unique identifier (id), unimproved land value (cost), and current conservation status (locked_in). Specifically, the conservation status column indicates if at least half the area planning unit is covered by existing protected areas (denoted by a value of 1) or not (denoted by a value of zero).

    +
    # plot map of planning unit costs
    +plot(tas_pu[, "cost"], main = "Planning unit costs")
    +

    +
    # plot map of planning unit statuses
    +plot(tas_pu[, "locked_in"], main = "Planning unit status")
    +

    +

    The tas_features object describes the spatial distribution of different vegetation communities (using presence/absence data). We will use the vegetation communities as the biodiversity features for the prioritization.

    +
    # plot map of the first four vegetation classes
    +plot(tas_features[[1:4]], main = paste("Feature", 1:4))
    +

    +

    We can use this dataset to generate a prioritization. Specifically, we will use the minimum set objective so that the optimization process minimizes total cost. We will add representation targets to ensure that prioritizations cover 17% of each vegetation community. Additionally, we will add constraints to ensure that planning units covered by existing protected areas are selected (i.e., locked in). Finally, we will specify that the conservation planning exercise involves binary decisions (i.e., selecting or not selecting planning units for protected area establishment).

    +
    # define a problem
    +p0 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p0)
    +
    ## Conservation Problem
    +##   planning units: sf (1130 units)
    +##   cost:           min: 0.19249, max: 61.92727
    +##   features:       tas_features.1, tas_features.2, tas_features.3, ... (62 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
    +##   decisions:      Binary decision 
    +##   constraints:    <Locked in planning units [257 locked units]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
    # solve problem
    +s0 <- solve(p0)
    +
    +# print result
    +print(s0)
    +
    +# create column for making a map of the prioritization
    +s0$map_1 <- case_when(
    +  s0$locked_in > 0.5 ~ "locked in",
    +  s0$solution_1 > 0.5 ~ "priority",
    +  TRUE ~ "other"
    +)
    +
    +# plot map of prioritization
    +plot(
    +  s0[, "map_1"], pal = c("purple", "grey90", "darkgreen"),
    +  main = NULL, key.pos = 1
    +)
    +

    +

    We can see that the priority areas identified by the prioritization are scattered across the study area (shown in green). Indeed, none of the priority areas are connect to existing protected areas (shown in purple), and very of them are connect with other priority areas. As such, the prioritization has a high level of spatial fragmentation. If it is important avoid such levels of spatial fragmentation, then we will need to explicitly account spatial fragmentation in the optimization process.

    +
    +
    +

    Preliminary processing

    +

    We need to conduct some preliminary processing procedures to prepare the data for subsequent analysis. This is important to help make it easier to find suitable trade-off parameters, and avoid numerical scaling issues that can result in overly long run times (see presolve_check() for further information). These processing steps are akin to data scaling (or normalization) procedures that are applied in statistical analysis to improve model convergence.

    +

    The first processing procedure involves setting the cost values for all locked in planning units to zero. This is so that the total cost estimates of the prioritization reflects the total cost of establishing new protected areas – not just total land value. In other words, we want the total cost estimate for a prioritization to reflect the cost of implementing conservation actions. This procedure is especially important when using the hierarchical approach described below, so that cost thresholds are based on percentage increases in the cost of establishing new protected areas.

    +
    # set costs for planning units covered by existing protected areas to zero
    +tas_pu$cost[tas_pu$locked_in > 0.5] <- 0
    +
    +# plot map of planning unit costs
    +plot(tas_pu[, "cost"], main = "Planning unit cost")
    +

    +

    The second procedure involves pre-computing the boundary length data and manually re-scaling the boundary length values. This procedure is important because boundary length values are often very large that, in turn, can cause numerical issues that result in excessive run times (see presolve_check() for further details).

    +
    # generate boundary length data for the planning units
    +tas_bd <- boundary_matrix(tas_pu)
    +
    +# manually re-scale the boundary length values
    +tas_bd@x <- rescale(tas_bd@x, to = c(0.01, 100))
    +

    After applying these procedures, our data is ready for subsequent analysis.

    +
    +
    +

    Generating candidate prioritizations

    +

    Here we will start the calibration analysis by generating a set of candidate prioritizations. Specifically, these prioritizations will be generated using different parameters to specify different trade-offs between the different criteria. Since this tutorial involves navigating trade-offs between the overall cost of a prioritization and the level of spatial fragmentation associated with a prioritization (as measured by total boundary length), we will generate prioritizations using different parameters related to these criteria. We will examine two approaches for generating candidate prioritizations based on multi-objective optimization procedures. Although we’ll be examining both approaches in this tutorial, you would normally only use one of these approaches when conducting your own analysis

    +
    +

    Blended approach

    +

    The blended approach for multi-objective optimization involves combining separate criteria (e.g., total cost and total boundary length) into a single joint criterion. To achieve this, a trade-off (or scaling) parameter is used to specify the relative importance of each criterion. This approach is the default approach provided by the prioritizr R package. Specifically, each of the functions for adding a penalty to a problem formulation (e.g., add_boundary_penalties()) contains a parameter to control the relative importance of the penalties (i.e., the penalty parameter). For example, when using the add_boundary_penalties() function, setting a high penalty value will indicate that it is important to reduce the overall exposed boundary (perimeter) of the prioritization.

    +

    The main challenge with the blended approach is identifying a range of suitable penalty values to generate candidate prioritizations. If we set a penalty value that is too low, then the penalties will have no effect (e.g., boundary length penalties would have no effect on the prioritization). If we set a penalty value too high, then the prioritization will effectively ignore the primary objective. In such cases, the prioritization will be overly spatially clustered – because the planning unit cost values have no effect — and contain a single reserve. Thus we need to find a suitable range of penalty values before we can generate a set of candidate prioritizations.

    +

    We can find a suitable range of penalty values by generating a set of preliminary prioritizations. These preliminary prioritizations will be based on different penalty values – similar to the process for generating the candidate prioritizations – but solved using customized settings that sacrifice optimality for fast run times (see below for details). This is especially important because specifying a penalty value that is too high will cause the optimization process to take a very long time to generate a solutions (due to the numerical scaling issues mentioned previously). To find a suitable range of penalty values, we need to identify an upper limit for the penalty value (i.e., the highest penalty value that result in a prioritization containing a single reserve). Let’s create some preliminary penalty to identify this upper limit. Please note that you might need to adjust the prelim_upper value to find the upper limit when analyzing different datasets.

    +
    # define a range of different penalty values
    +## note that we use a power scale to avoid focusing on very high penalty values
    +prelim_lower <- -5   # change this for your own data
    +prelim_upper <- 2.8  # change this for your own data
    +prelim_penalty <- round(10^seq(prelim_lower, prelim_upper, length.out = 9), 5)
    +
    +# print penalty values
    +print(prelim_penalty)
    +
    ## [1]   0.00001   0.00009   0.00089   0.00841   0.07943   0.74989   7.07946
    +## [8]  66.83439 630.95734
    +

    Next, let’s use the preliminary penalty values to generate preliminary prioritizations. As mentioned earlier, we will generate these preliminary prioritizations using customized settings to reduce runtime. Specifically, we will set a time limit of 10 minutes per run, and relax the optimality gap to 20%. Although we would not normally use such settings – because the resulting prioritizations are not guaranteed to be near-optimal (the default gap is 10%) – this is fine because our goal here is to tune the preliminary penalty values. Indeed, none of these preliminary prioritizations will be considered as candidate prioritizations. Please note that you might need to set a higher time limit, or relax the optimality gap even further (e.g., 40%) when analyzing larger datasets.

    +
    # define a problem without boundary penalties
    +p0 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions()
    +
    +# generate preliminary prioritizations based on each penalty
    +## note that we specify a relaxed gap and time limit for the solver
    +prelim_blended_results <- lapply(prelim_penalty, function(x) {
    +  s <-
    +    p0 %>%
    +    add_boundary_penalties(penalty = x, data = tas_bd) %>%
    +    add_default_solver(gap = 0.2, time_limit = 10 * 60) %>%
    +    solve()
    +  s <- data.frame(s = s$solution_1)
    +  names(s) <- with_options(list(scipen = 30), paste0("penalty_", x))
    +  s
    +})
    +
    +# format results as a single spatial object
    +prelim_blended_results <- cbind(
    +  tas_pu, do.call(bind_cols, prelim_blended_results)
    +)
    +
    +# preview results
    +print(prelim_blended_results)
    +

    After generating the preliminary prioritizations, let’s create some maps to visualize them. In particular, we want to understand how different penalty values influence the spatial fragmentation of the prioritizations.

    +
    # plot maps of prioritizations
    +plot(
    +  x =
    +    prelim_blended_results %>%
    +    dplyr::select(starts_with("penalty_")) %>%
    +    mutate_if(is.numeric, function(x) {
    +      case_when(
    +        prelim_blended_results$locked_in > 0.5 ~ "locked in",
    +        x > 0.5 ~ "priority",
    +        TRUE ~ "other"
    +      )
    +    }),
    +  pal = c("purple", "grey90", "darkgreen")
    +)
    +

    +

    We can see that as the penalty value used to generate the prioritizations increases, the spatial fragmentation of the prioritizations decreases. In particular, we can see that a penalty value of 66.83439 results in a single reserve – meaning this is our best guess of the upper limit. Using this penalty value as an upper limit, we will now generate a second series of prioritizations that will be the candidate prioritizations. Critically, these candidate prioritizations will not be generated using with time limit and be generated using a more suitable gap (i.e., default gap of 10%).

    +
    # define a new set of penalty values
    +## note that we use a linear scale to explore both low and high penalty values
    +penalty <- round(seq(1e-5, prelim_penalty[8], length.out = 9), 5)
    +
    +# generate prioritizations based on each penalty
    +blended_results <- lapply(penalty, function(x) {
    +  ## generate solution
    +  s <-
    +    p0 %>%
    +    add_boundary_penalties(penalty = x, data = tas_bd) %>%
    +    solve()
    +  ## return data frame with solution
    +  s <- data.frame(s = s$solution_1)
    +  names(s) <- with_options(list(scipen = 30), paste0("penalty_", x))
    +  s
    +})
    +
    +# format results as a single spatial object
    +blended_results <- cbind(tas_pu, do.call(bind_cols, blended_results))
    +
    +# plot maps of prioritizations
    +plot(
    +  x =
    +    blended_results %>%
    +    dplyr::select(starts_with("penalty_")) %>%
    +    mutate_if(is.numeric, function(x) {
    +      case_when(
    +        blended_results$locked_in > 0.5 ~ "locked in",
    +        x > 0.5 ~ "priority",
    +        TRUE ~ "other"
    +      )
    +    }),
    +  pal = c("purple", "grey90", "darkgreen")
    +)
    +

    +

    We now have a set of candidate prioritizations generated using the blended approach. The main advantages of this approach is that it is similar calibration analyses used by other decision support tools for conservation (i.e., Marxan) and it is relatively straightforward to implement. However, this approach also has a key disadvantage. Because the penalty parameter is a unitless trade-off parameter – meaning that we can’t leverage existing knowledge to specify a suitable range of penalty values – we first have to conduct a preliminary analysis to identify an upper limit. Although finding an upper limit was fairly simple for the example datset, it can be difficult to find for more realistic data. In the next section, we will show how to generate a set of candidate prioritzations using the hierachical approach – which does not have this disadvantage.

    +
    +
    +

    Hierarchical approach

    +

    The hierarchical approach for multi-objective optimization involves generating a series of incremental prioritizations – using a different objective at each increment to refine the previous solution – until the final solution achieves all of the objectives. The advantage with this approach is that we can specify trade-off parameters for each objective based on a percentage from optimality. This means that we can leverage our own knowledge – or that of decision maker – when to generate a range of suitable trade-off parameters. As such, this approach does not require us to generate a series of preliminary prioritizations.

    +

    This approach is slightly more complicated to implement within the prioritizr R package then the blended approach. To start off, we generate an initial prioritization based on a problem formulation that does not consider any penalties. Critically, we will generate this prioritization by solving the problem to optimality (using the gap parameter of the add_default_solver() function).

    +
    # define a problem without boundary penalties
    +p1 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0)
    +
    +# solve problem
    +s1 <- solve(p1)
    +
    +# add column for making a map of the prioritization
    +s1$map_1 <- case_when(
    +  s1$locked_in > 0.5 ~ "locked in",
    +  s1$solution_1 > 0.5 ~ "priority",
    +  TRUE ~ "other"
    +)
    +
    +# plot map of prioritization
    +plot(
    +  s0[, "map_1"], pal = c("purple", "grey90", "darkgreen"),
    +  main = NULL, key.pos = 1
    +)
    +

    +

    Next, we will calculate the total cost of the initial prioritization.

    +
    # calculate cost
    +s1_cost <- eval_cost_summary(p1, s1[, "solution_1"])$cost
    +
    +# print cost
    +print(s1_cost)
    +
    ## [1] 904.5156
    +

    Now we will calculate a series of cost thresholds. These cost thresholds will be calculated by inflating the cost of the initial prioritization by a range of percentage values. Since these values are percentages – and not unitless values unlike those used in the blended approach – we can use domain knowledge to specify a suitable range of cost thresholds. For this tutorial, let’s assume that it would be impractical – per our domain knowledge – to expend more than four times the total cost of the initial prioritization to reduce spatial fragmentation.

    +
    # calculate cost threshold values
    +threshold <- s1_cost + (s1_cost * seq(1e-5, 4, length.out = 9))
    +threshold <- ceiling(threshold)
    +
    +# print cost thresholds
    +print(threshold)
    +
    ## [1]  905 1357 1810 2262 2714 3166 3619 4071 4523
    +

    After generating the cost thresholds, we can use them to generate prioritizations. Specifically, we will generate prioritizations that aim to minimize total boundary length as much as possible – ignoring the total cost of the prioritizations – whilst ensuring that the total cost of the prioritization does not exceed a given cost threshold and the other considerations (e.g., locked in constraints). To achieve this, we create a new column in the tas_pu object that contains only zero values (called zeros) and use this new column to specify the cost data for the prioritizations. Although we normally recommend against cost data that contain zero values – because planning units with zero costs are often selected in prioritizations even if they are not needed – here we use zero cost values so that the prioritization will focus exclusively on spatial fragmentation. Additionally, when it comes to generating the prioritization, we will add linear constraints to ensure that the total cost of the prioritization does not exceed a given cost threshold (using the add_linear_constraints() function).

    +
    # add a column with zeros
    +tas_pu$zeros <- 0
    +
    +# define a problem with zero cost values and boundary penalties
    +## note that because all the costs are all zero, it doesn't actually
    +## matter what penalty value is used (as long as the value is > 0)
    +## and so we just use a value of 1
    +p2 <- problem(tas_pu, tas_features, cost_column = "zeros") %>%
    +      add_min_set_objective() %>%
    +      add_boundary_penalties(penalty = 1, data = tas_bd) %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions()
    +
    +# generate prioritizations based on each cost threshold
    +## note that the prioritizations are solved to within 10% of optimality
    +## (the default gap) because the gap is not specified
    +hierarchical_results <- lapply(threshold, function(x) {
    +  ## generate solution by adding a constraint based on the threshold and
    +  ## using the "real" cost values (i.e., not zeros)
    +  s <-
    +    p2 %>%
    +    add_linear_constraints(threshold = x, sense = "<=", data = "cost") %>%
    +    solve()
    +  ## return data frame with solution
    +  s <- data.frame(s = s$solution_1)
    +  names(s) <- paste0("threshold_", x)
    +  s
    +})
    +
    +# format results as a single spatial object
    +hierarchical_results <- cbind(tas_pu, do.call(bind_cols, hierarchical_results))
    +
    +# plot maps of prioritizations
    +plot(
    +  x =
    +    hierarchical_results %>%
    +    dplyr::select(starts_with("threshold_")) %>%
    +    mutate_if(is.numeric, function(x) {
    +      case_when(
    +        hierarchical_results$locked_in > 0.5 ~ "locked in",
    +        x > 0.5 ~ "priority",
    +        TRUE ~ "other"
    +      )
    +    }),
    +  pal = c("purple", "grey90", "darkgreen")
    +)
    +

    +

    We now have a set of candidate prioritizations generated using the hierarchical approach. This approach can be much faster than the blended approach because it does not require generating a set of prioritizations to identify an upper limit for the penalty trade-off parameter. After generating a set of candidate prioritizations, we can then calculate performance metrics to compare the prioritizations.

    +
    +
    +
    +

    Calculating performance metrics

    +

    Here we will calculate performance metrics to compare the prioritizations. Since we aim to navigate trade-offs between the total cost of a prioritization and the overall level of spatial fragmentation associated with a prioritization (as measured by total boundary length), we will calculate metrics to assess these criteria. Although we generated two sets of candidate prioritizations in the previous section; for brevity, here we will consider the candidate prioritizations generated using the hierarchical approach. Please note that you could also apply the following procedures to candidate prioritizations generated using the blended approach.

    +
    # calculate metrics for prioritizations
    +## note that we use p0 and not p1 so that cost calculations are based
    +## on the cost values and not zeros
    +hierarchical_metrics <- lapply(
    +  grep("threshold_", names(hierarchical_results)), function(x) {
    +    x <- hierarchical_results[, x]
    +    data.frame(
    +      total_cost = eval_cost_summary(p0, x)$cost,
    +      total_boundary_length = eval_boundary_summary(p0, x)$boundary
    +    )
    +  }
    +)
    +hierarchical_metrics <- do.call(bind_rows, hierarchical_metrics)
    +hierarchical_metrics$threshold <- threshold
    +hierarchical_metrics <- as_tibble(hierarchical_metrics)
    +
    +# preview metrics
    +print(hierarchical_metrics)
    +
    ## # A tibble: 9 × 3
    +##   total_cost total_boundary_length threshold
    +##        <dbl>                 <dbl>     <dbl>
    +## 1       905.              2919388.       905
    +## 2      1357.              2407733.      1357
    +## 3      1807.              2205799.      1810
    +## 4      2256.              2049117.      2262
    +## 5      2712.              1945849.      2714
    +## 6      3164.              1862288.      3166
    +## 7      3608.              1809343.      3619
    +## 8      4071.              1755416.      4071
    +## 9      4521.              1737961.      4523
    +

    After calculating the metrics, let’s we can use them to help select a prioritization.

    +
    +
    +

    Selecting a prioritization

    +

    Now we need to decide on which candidate prioritization achieves the best trade-off. There are a range of qualitative and quantitative methods that are available to select a candidate prioritization (Ardron et al. 2010). Here we will consider three different methods. Since some of these methods a set of candidate prioritizations, we will use the candidate prioritizations using the hierarchical approach for these methods. To keep track of the prioritizations selected by different methods, let’s create a results_data table.

    +
    # create data for plotting
    +result_data <-
    +  hierarchical_metrics %>%
    +  ## rename threshold column to value column
    +  rename(value = "threshold") %>%
    +  ## add column with column names that contain candidate prioritizations
    +  mutate(name = grep(
    +    "threshold_", names(hierarchical_results), value = TRUE, fixed = TRUE
    +  )) %>%
    +  ## add column with labels for plotting
    +  mutate(label = paste("Threshold =", value)) %>%
    +  ## add column to keep track prioritizations selected by different methods
    +  mutate(method = "none")
    +
    +# print table
    +print(result_data)
    +
    ## # A tibble: 9 × 6
    +##   total_cost total_boundary_length value name           label            method
    +##        <dbl>                 <dbl> <dbl> <chr>          <chr>            <chr> 
    +## 1       905.              2919388.   905 threshold_905  Threshold = 905  none  
    +## 2      1357.              2407733.  1357 threshold_1357 Threshold = 1357 none  
    +## 3      1807.              2205799.  1810 threshold_1810 Threshold = 1810 none  
    +## 4      2256.              2049117.  2262 threshold_2262 Threshold = 2262 none  
    +## 5      2712.              1945849.  2714 threshold_2714 Threshold = 2714 none  
    +## 6      3164.              1862288.  3166 threshold_3166 Threshold = 3166 none  
    +## 7      3608.              1809343.  3619 threshold_3619 Threshold = 3619 none  
    +## 8      4071.              1755416.  4071 threshold_4071 Threshold = 4071 none  
    +## 9      4521.              1737961.  4523 threshold_4523 Threshold = 4523 none
    +

    Next, let’s examine some different methods for selecting prioritizations.

    +
    +

    Visual method

    +

    One qualitative method involves plotting the relationship between the different criteria, and using the plot to visually select a candidate prioritization. This visual method is often used to help calibrate trade-offs among prioritizations generated using the Marxan decision support tool (e.g., Hermoso et al. 2011; Stewart & Possingham 2005). So, let’s create a plot to select a prioritization.

    +
    # create plot to visualize trade-offs and show selected candidate prioritization
    +result_plot <-
    +  ggplot(
    +    data = result_data,
    +    aes(x = total_boundary_length, y = total_cost, label = label)
    +  ) +
    +  geom_line() +
    +  geom_point(size = 3) +
    +  geom_text(hjust = -0.15) +
    +  scale_color_manual(
    +    values = c("visual" = "blue", "not selected" ="black")
    +  ) +
    +  xlab("Total boundary length of prioritization") +
    +  ylab("Total cost of prioritization") +
    +  scale_x_continuous(expand = expansion(mult = c(0.05, 0.4))) +
    +  theme(legend.title = element_blank())
    +
    +# render plot
    +print(result_plot)
    +

    +

    We can see that there is a clear relationship between total cost and total boundary length. It would seem that in order to achieve a lower total boundary length – and thus lower spatial fragmentation – the prioritization must have a greater cost. Although we might expect the results to show a smoother curve – in other words, only Pareto dominant solutions – this result is expected because we generated candidate prioritizations using the default optimality gap of 10%. Typically, the visual method involves selecting a prioritization near the elbow of the plot. So, let’s select the prioritization generated using a threshold value of 1810. To keep of the prioritizations selected based on different methods, let’s create a method column in the result_data table.

    +
    # specify prioritization selected by visual method
    +result_data$method[3] <- "visual"
    +

    Next, let’s consider a quantitative approach.

    +
    +
    +

    TOPSIS method

    +

    Multiple-criteria decision analysis is a disciple that uses analytical methods to evaluate trade-offs between multiple criteria (MCDA; reviewed in Greene et al. 2011). Although this discipline contains many different methods, here we will use the the Technique for Order of Preference by Similarity to Ideal Solution (TOPSIS) method (Hwang & Yoon 1981). This method requires (i) data describing the performance of each prioritization according the different criteria, (ii) weights to encode the relative importance of each criteria, and (iii) details on whether each criteria should ideally be minimized or maximized. Let’s run the analysis, assuming that we equal weighting for total cost and total boundary length.

    +
    # calculate TOPSIS scores
    +topsis_results <- topsis(
    +  decision =
    +    hierarchical_metrics %>%
    +    dplyr::select(total_cost, total_boundary_length) %>%
    +    as.matrix(),
    +  weights = c(1, 1),
    +  impacts = c("-", "-")
    +)
    +
    +# print results
    +print(topsis_results)
    +
    ##   alt.row     score rank
    +## 1       1 0.6861663    3
    +## 2       2 0.7570982    1
    +## 3       3 0.7218541    2
    +## 4       4 0.6442282    4
    +## 5       5 0.5530166    5
    +## 6       6 0.4697259    6
    +## 7       7 0.3999050    7
    +## 8       8 0.3480699    8
    +## 9       9 0.3138337    9
    +

    The candidate prioritization with the greatest TOPSIS score is considered to represent the best trade-off between total cost and total boundary length. So, based on this method, we would select the prioritization generated using a threshold value of 1357. Let’s update the result_data with this information.

    +
    # add column indicating prioritization selected by TOPSIS method
    +result_data$method[which.max(topsis_results$score)] <- "TOPSIS"
    +

    Next, let’s consider another quantitative method.

    +
    +
    +

    Cohon et al. (1979) method

    +

    This method is based on an algorithm that was originally developed by Cohon et al. (Cohon et al. 1979), and was later adapted for use in systematic conservation planning (Fischer & Church 2005). Specifically, it involves generating two ideal prioritizations – with each prioritization representing the ideal prioritization for each criteria – and then using the performance metrics calculated for these prioritizations to automatically derive a trade-off penalty value (Ardron et al. 2010; Cohon et al. 1979). Thus, unlike the two other methods, this method does not require a set of candidate prioritizations. As such, this method can be used to find a prioritization that meets multiple criteria in a much shorter period of time than the other methods. To implement this method, we first need to generate the ideal prioritizations (note that we specify an gap of 0% to ensure optimality).

    +
    # generate ideal prioritization based on cost criteria
    +## note that this is simply the same as the s1 prioritization we generated
    +## for the hierarchical approach
    +p3 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0)
    +
    +# solve problem
    +s3 <- solve(p3)
    +
    +# generate ideal prioritization based on spatial fragmentation criteria
    +## note that any non-zero penalty value would here,
    +## so we just use a penalty  of 1
    +p4 <- problem(tas_pu, tas_features, cost_column = "zeros") %>%
    +      add_min_set_objective() %>%
    +      add_boundary_penalties(penalty = 1, data = tas_bd) %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(gap = 0)
    +
    +# solve problem
    +s4 <- solve(p4)
    +

    Next, let’s calculate the performance metrics for these prioritizations.

    +
    # generate problem formulation with costs and boundary penalties for
    +# calculating performance metrics
    +p5 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_boundary_penalties(penalty = 1, data = tas_bd) %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions()
    +
    +# calculate performance metrics for ideal cost prioritization
    +s3_metrics <- tibble(
    +  total_cost = eval_cost_summary(p5, s3[, "solution_1"])$cost,
    +  total_boundary_length =
    +    eval_boundary_summary(p5, s3[, "solution_1"])$boundary
    +)
    +
    +# calculate performance metrics for ideal boundary length prioritization
    +s4_metrics <- tibble(
    +  total_cost = eval_cost_summary(p5, s4[, "solution_1"])$cost,
    +  total_boundary_length =
    +    eval_boundary_summary(p5, s4[, "solution_1"])$boundary
    +)
    +

    After calculating these performance metrics, we can use them to automatically calculate a penalty value.

    +
    # calculate penalty value based on Cohon et al. 1979
    +cohon_penalty <- abs(
    +  (s3_metrics$total_cost - s4_metrics$total_cost) /
    +  (s3_metrics$total_boundary_length - s4_metrics$total_boundary_length)
    +)
    +
    +# round to 5 decimal places to avoid numerical issues during optimization
    +cohon_penalty <- round(cohon_penalty, 5)
    +
    +# print penalty value
    +print(cohon_penalty)
    +
    ## [1] 0.00945
    +

    Now that we have calculated a penalty value using this method, we can use it to generate a prioritization.

    +
    # generate prioritization using penalty value calculated using Cohon et al. 1979
    +p6 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_boundary_penalties(penalty = cohon_penalty, data = tas_bd) %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions()
    +
    +# solve problem
    +s6 <- solve(p6)
    +

    Let’s update the results_data table with results about the prioritization.

    +
    # add new row with data for prioritization generated following Cohon et al. 1979
    +result_data <- bind_rows(
    +  result_data,
    +  tibble(
    +    total_cost = eval_cost_summary(p6, s6[, "solution_1"])$cost,
    +    total_boundary_length =
    +      eval_boundary_summary(p6, s6[, "solution_1"])$boundary,
    +    value = cohon_penalty,
    +    name = paste0("penalty_", cohon_penalty),
    +    label = paste0("Penalty = ",  cohon_penalty),
    +    method = "Cohon"
    +  )
    +)
    +

    Next, let’s compare the prioritizations selected by different methods.

    +
    +
    +

    Method comparison

    +

    Let’s create a plot to visualize the results from the different methods.

    +
    # create plot to visualize trade-offs and show selected candidate prioritization
    +result_plot <-
    +  ggplot(
    +    data =
    +      result_data %>%
    +      mutate(vjust = if_else(method == "Cohon", -1, 0.5)),
    +    aes(x = total_boundary_length, y = total_cost, label = label)
    +  ) +
    +  geom_line() +
    +  geom_point(aes(color = method), size = 3) +
    +  geom_text(aes(vjust = vjust, color = method), hjust = -0.1) +
    +  scale_color_manual(
    +    name = "Method",
    +    values = c(
    +      "visual" = "#984ea3",
    +      "none" = "#000000",
    +      "TOPSIS" = "#e41a1c",
    +      "Cohon" = "#377eb8"
    +    )
    +  ) +
    +  xlab("Total boundary length of prioritization") +
    +  ylab("Total cost of prioritization") +
    +  scale_x_continuous(expand = expansion(mult = c(0.05, 0.4)))
    +
    +# render plot
    +print(result_plot)
    +

    +

    We can see that the different method selected different prioritizations. To further compare the results from the different methods, let’s create some maps showing the selected prioritizations.

    +
    # extract column names for creating the prioritizations
    +visual_name <- result_data$name[[which(result_data$method == "visual")]]
    +topsis_name <- result_data$name[[which(result_data$method == "TOPSIS")]]
    +
    +# create object with selected prioritizations
    +solutions  <- bind_cols(
    +  tas_pu,
    +  hierarchical_results %>%
    +    st_drop_geometry() %>%
    +    dplyr::select(all_of(c(visual_name, topsis_name))) %>%
    +    setNames(c("Visual", "TOPSIS")),
    +  s6 %>%
    +    st_drop_geometry() %>%
    +    dplyr::select(solution_1) %>%
    +    rename(Cohon = "solution_1")
    +)
    +
    +# plot maps of selected prioritizations
    +plot(
    +  x =
    +    solutions %>%
    +    dplyr::select(Visual, TOPSIS, Cohon) %>%
    +    mutate_if(is.numeric, function(x) {
    +      case_when(
    +        hierarchical_results$locked_in > 0.5 ~ "locked in",
    +        x > 0.5 ~ "priority",
    +        TRUE ~ "other"
    +      )
    +    }),
    +  pal = c("purple", "grey90", "darkgreen")
    +)
    +

    +

    How do we determine which one is best? This is difficult to say. Ideally, additional information could be used to help select a prioritization, such as knowledge on available resources, species’ connectivity requirements, and impacts of neighboring land use. However, from a practical perspective, prioritizations generated for academic contexts might find the quantitative approaches more useful because they have greater transparency and reproducibility. Ultimately, all of these methods are designed to support decision making. This means that they are intended to assist the decision making process, not serve as a replacement.

    +
    +
    +
    +

    Conclusion

    +

    Hopefully, this vignette has provided a useful introduction for resolving trade-offs in prioritizations. Although we only explored trade-offs between total cost and spatial fragmentation in this tutorial, this analysis could be adapted to explore trade-offs between a wide range of different criteria. For instance, instead of considering total cost as the primary objective, future analyses could explore trade-offs with feature representation (using the add_min_shortfall_objective() function). Additionally, instead of spatial fragmentation, future analyses could explore trade-offs that directly relate to connectivity (using the add_connectivity_penalties() function) or specific variables of interest – such as ecosystem intactness or inverse human footprint index (Williams et al. 2020; Beyer et al. 2019) – to inform decision making (using the add_linear_penalties() function). Furthermore, after identifying the best penalty or threshold values to strike a balance between multiple criteria, you could generate a portfolio of prioritizations (e.g., using the add_gap_portfolio_function()) to find multiple options for achieving a similar balance. This might be helpful when you need to generate a set of prioritizations that have comparable performance – in terms of how well they achieve different criteria – but select different planning units.

    +
    +
    +

    References

    +
    +
    +Ardron, J.A., Possingham, H.P. & Klein, C.J. (2010). Marxan Good Practices Handbook. Pacific Marine Analysis; Research Association, Vancouver, Victoria, BC. +
    +
    +Beger, M., Linke, S., Watts, M., Game, E., Treml, E., Ball, I. & Possingham, H.P. (2010). Incorporating asymmetric connectivity into spatial decision making for conservation. Conservation Letters, 3, 359–368. +
    +
    +Beyer, H.L., Venter, O., Grantham, H.S. & Watson, J.E.M. (2019). Substantial losses in ecoregion intactness highlight urgency of globally coordinated action. Conservation Letters, 13. +
    +
    +Cohon, J.L., Church, R.L. & Sheer, D.P. (1979). Generating multiobjective trade-offs: An algorithm for bicriterion problems. Water Resources Research, 15, 1001–1010. +
    +
    +Dorji, T., Linke, S. & Sheldon, F. (2020). Freshwater conservation planning in the context of nature needs half and protected area dynamism in bhutan. Biological Conservation, 251, 108785. +
    +
    +Fischer, D.T. & Church, R.L. (2005). The SITES reserve selection system: A critical review. Environmental Modeling and Assessment, 10, 215–228. +
    +
    +Greene, R., Devillers, R., Luther, J.E. & Eddy, B.G. (2011). GIS-based multiple-criteria decision analysis. Geography Compass, 5, 412–432. +
    +
    +Hermoso, V., Cattarino, L., Linke, S. & Kennard, M.J. (2018). Catchment zoning to enhance co-benefits and minimize trade-offs between ecosystem services and freshwater biodiversity conservation. Aquatic Conservation: Marine and Freshwater Ecosystems, 28, 1004–1014. +
    +
    +Hermoso, V., Kennard, M.J. & Linke, S. (2012). Integrating multidirectional connectivity requirements in systematic conservation planning for freshwater systems. Diversity and Distributions, 18, 448–458. +
    +
    +Hermoso, V., Linke, S., Prenda, J. & Possingham, H.P. (2011). Addressing longitudinal connectivity in the systematic conservation planning of fresh waters. Freshwater Biology, 56, 57–70. +
    +
    +Hwang, C.L. & Yoon, K.G. (1981). Multiple Attribute Decision Making: Methods and Applications. Springer-Verlag, New York, NY. +
    +
    +Klein, C.J., Tulloch, V.J., Halpern, B.S., Selkoe, K.A., Watts, M.E., Steinback, C., Scholz, A. & Possingham, H.P. (2013). Tradeoffs in marine reserve design: Habitat condition, representation, and socioeconomic costs. Conservation Letters, 324–332. +
    +
    +Margules, C.R. & Pressey, R.L. (2000). Systematic conservation planning. Nature, 405, 243–253. +
    +
    +Stewart, R.R. & Possingham, H.P. (2005). Efficiency, costs and trade-offs in marine reserve system design. Environmental Modeling and Assessment, 10, 203–213. +
    +
    +Vane-Wright, R.I., Humphries, C.J. & Williams, P.H. (1991). What to protect? — Systematics and the agony of choice. Biological Conservation, 55, 235–254. +
    +
    +Williams, B.A., Venter, O., Allan, J.R., Atkinson, S.C., Rehbein, J.A., Ward, M., Marco, M.D., Grantham, H.S., Ervin, J., Goetz, S.J., Hansen, A.J., Jantz, P., Pillay, R., Rodrı́guez-Buriticá, S., Supples, C., Virnig, A.L.S. & Watson, J.E.M. (2020). Change in terrestrial human footprint drives continued loss of intact ecosystems. One Earth, 3, 371–382. +
    +
    +
    + + + + + + + + + + + diff --git a/inst/doc/connectivity_tutorial.Rmd b/inst/doc/connectivity_tutorial.Rmd new file mode 100644 index 000000000..4deb120fc --- /dev/null +++ b/inst/doc/connectivity_tutorial.Rmd @@ -0,0 +1,487 @@ +--- +title: "Connectivity tutorial" +output: + rmarkdown::html_vignette: + toc: true + fig_caption: true + self_contained: yes +fontsize: 11pt +documentclass: article +bibliography: references.bib +csl: reference-style.csl +vignette: > + %\VignetteIndexEntry{Connectivity tutorial} + %\VignetteEngine{knitr::rmarkdown_notangle} +--- + +```{r, include = FALSE} +h <- 4.5 +w <- 4.5 +is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", + "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) +knitr::opts_knit$set(global.par = TRUE) +knitr::opts_chunk$set( + fig.align = "center", eval = !is_check, + fig.width = 4.0, fig.height = 3.5 +) +``` + +## Introduction + +Connectivity is a key consideration in systematic conservation planning [@r4; @r55]. This is because isolated and fragmented populations are often more vulnerable to extinction [@r60; @r61; @r62]. To promote connectivity in prioritizations, a range of different approaches are available [reviewed in @r56]. These approaches can solely on the spatial configuration of a prioritization to enhance structural connectivity [e.g, reducing the spatial fragmentation of a prioritization\; @r2]. They can also leverage data -- such as environmental, river flow, and telemetry data -- to generate prioritizations that promote functional connectivity [e.g., @r43; @r58; @r59]. + +The aim of this tutorial is to show how connectivity can be incorporated into prioritizations using the _prioritizr R_ package. Here we will explore various approaches for incorporating connectivity, and see how they alter the spatial configuration of prioritizations. As you will discover, many of these approaches involve setting threshold or penalty values to specify the relative importance of connectivity compared to other criteria (e.g., overall cost). For more information on calibrating these values, please see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html). + +## Data + +The dataset used in this tutorial was created for the Coastal Douglas-fir Conservation Partnership [CDFCP\; @r29]. Although the original dataset covers a much larger area; for brevity, here we focus only on Salt Spring Island, British Columbia. Briefly, Salt Spring Island supports a diverse and globally unique mix of dry forest and savanna habitats. Today, these habitats are critically threatened due to land conversion, invasive species, and altered disturbance regimes. For more information on the data, please refer to the [Marxan tool portal](https://arcese.forestry.ubc.ca/marxan-tool/) and the [tool tutorial](https://peter-arcese-lab.sites.olt.ubc.ca/files/2016/09/CDFCP_tutorial_2017_05.pdf). + +
    + +![Extent of Coastal Douglas-fir Conservation Partnership Tool area and location of Salt Spring Island](figures/map.jpg) + +
    + +Let's begin by loading the packages and data for this tutorial. Since this tutorial requires the _prioritizrdata R_ package, please ensure that it is installed. Specifically, two objects underpin the data for this tutorial. The `salt_pu` object specifies the planning unit data as a raster layer (i.e., `RasterLayer` object), and the `salt_features` object contains biodiversity data represented as a multi-band raster stack (i.e., a `RasterStack` object). + +```{r, message = FALSE} +# load packages +library(prioritizr) +library(prioritizrdata) +library(scales) + +# load planning unit data +data(salt_pu) + +# load ecological data +data(salt_features) +``` + +Now we will conduct some preliminary processing. Specifically, we will aggregate from the 100 m resolution to the 300 m resolution. This is to reduce the time needed to generate prioritizations in this tutorial. In practice, we generally recommend consider other criteria too -- such as the spatial scale that is relevant for decision making and the resolution of available datasets -- when deciding on an appropriate scale for planning units. + +```{r} +# aggregate data to coarser resolution +salt_pu <- aggregate(salt_pu, fact = 3) +salt_features <- aggregate(salt_features, fact = 3) +``` + +Next, let's have a look at the `salt_pu` object. Here each grid cell represents a planning unit, and the grid cell values denote acquisition costs [@r28]. To aid with visualization, we will log-transform the values when plotting them on a map. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# print planning unit data +print(salt_pu) + +# plot map showing the planning units costs on a log-scale +plot(log(salt_pu), main = "Planning unit costs (log)", axes = FALSE) +``` + +Let's also look at the `salt_features` object. This object is a stack of raster layers, with each layer corresponding to a different variable that describes a particular aspect of biodiversity. The first four layers correspond to different ecological communities (i.e., _Old Forest_, _Savannah_, _Wetland_, and _Shrub_ communities), and their cell values indicate the probability of encountering a bird species associated a given community. The fifth layer describes the inverse probability of occurrence of human commensal species. In this tutorial, we will use the first four layers as biodiversity features, and the fifth layer to help parameterize connectivity (wherein higher values denote greater connectivity). So, let's extract the data and visualize them. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# print original data +print(salt_features) + +# extract connectivity data +salt_con <- salt_features[[nlayers(salt_features)]] + +# print connectivity data +print(salt_con) + +# plot map showing the connectivity data +plot(salt_con, main = "Connectivity data", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1), oma = c(0, 0, 0, 0.5)) +``` + +```{r, fig.width = 7.0, fig.height = 7.0} +# extract ecological communities and use these as features +salt_features <- salt_features[[seq_len(4)]] + +# assign names to features +names(salt_features) <- c("Old_Forest", "Savannah", "Wetland", "Shrub") + +# print features +print(salt_features) + +# plot map showing the distribution of the features +plot(salt_features, main = names(salt_features), axes = FALSE) +``` + +## Baseline problem + +In this tutorial, we will explore a few different ways of incorporating connectivity into prioritizations. To enable comparisons among prioritizations based on different approaches, we will first create a baseline problem formulation that we will subsequently customize to incorporate connectivity. Specifically, we will formulate the baseline problem using the minimum set objective. We will use representation targets of 17% -- based on [Aichi Biodiversity Target 11](https://www.cbd.int/sp/targets/) -- to provide adequate coverage of each ecological community. Additionally, because land properties on Salt Spring Island can either be acquired in their entirety or not at all, we will use binary decision types. This means that planning units are either selected in the solution or not selected in the solution---planning units cannot be partially acquired. Given all these details, let's formulate the baseline problem. + +```{r} +# create problem +p0 <- problem(salt_pu, salt_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_binary_decisions() %>% + add_default_solver() + +# print problem +print(p0) +``` + +After formulating the baseline problem, we can solve it to generate a prioritization. + +```{r, results = "hide"} +# solve problem +s0 <- solve(p0) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# print solution +print(s0) + +# plot solution +plot( + s0, main = "Baseline prioritization", axes = FALSE, + breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Next, let's explore some options for incorporating connectivity. + +## Adding constraints + +Let's explore approaches for promoting connectivity in prioritizations by adding constraints to the baseline problem formulation. These approaches ensure that prioritizations exhibit certain characteristics [e.g., ensure prioritizations form a contiguous reserve\; @r57]. This means that, regardless of the optimality gap used to generate a prioritization, the prioritization will always exhibit these characteristics. + +### Neighbor constraints + +Neighbor constraints can be added to ensure that each selected planning unit has a certain number of neighbors surrounding it (using the `add_neighbor_constraints()` function) [based on @r16]. The `k` parameter can be used to specify the required number of neighbors for each selected planning unit. Let's generate a prioritization by specifying that each planning unit requires at least two neighbors. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added neighbor constraints and solve it +s1 <- p0 %>% + add_neighbor_constraints(k = 2) %>% + solve() + +# plot solutions +plot( + stack(s0, s1), main = c("baseline", "neighbors constraints"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +### Contiguity constraints + +Contiguity constraints can be added to ensure that all planning units form a single contiguous reserve (using the `add_contiguity_constraints()` function) [similar to @r57]. These constraints are extremely complex. As such, they can only be applied to small conservation planning problems and the _Gurobi_ solver is required to solve them in a feasible period of time. Since it would take a long time to generate a near-optimal prioritization for this dataset with contiguity constraints, we will also tell the solver to simply return the first solution that it finds which meets the representation targets and the contiguity constraints. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added contiguity constraints and solve it +s2 <- p0 %>% + add_contiguity_constraints() %>% + add_gurobi_solver(first_feasible = TRUE) %>% + solve() + +# plot solutions +plot( + stack(s0, s2), main = c("baseline", "contiguity constraints"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +There is also an even more complex version of the contiguity constraints that is available. These constraints -- termed feature contiguity constraints [similar to @r64] -- can be added to ensure that all of the selected planning units used to the reach representation targets within a prioritization form a contiguous network for each feature (using the `add_feature_contiguity_constraints()` function). In other words, they ensure that each feature can disperse through the prioritization to access a target threshold amount of habitat. However, these constraints are extraordinarily complex, only feasible for small problems, and require preprocessing routines to identify initial solutions. As such, we will not consider them in this tutorial. + +### Linear constraints + +Linear constraints can be used to specify that the prioritizations must meet an arbitrary set of criteria. As such, they can be used to ensure that prioritizations provide adequate coverage of planning units that have facilitate a high level of connectivity. Recall that the `salt_con` data are used to describe connectivity across the study area. Since higher values denote planning units with greater connectivity, we could use linear constraints to ensure that the total sum of connectivity values -- based on this dataset -- meets a particular threshold (e.g. cover at least 30% of the total amount). This would effectively be treating connectivity as an additional feature [similar to @r52]. + +```{r} +# compute threshold for constraints +## here we use a threshold of 30% of the total connectivity values +threshold <- cellStats(salt_con, "sum") * 0.3 + +# print threshold +print(threshold) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +s3 <- p0 %>% + add_linear_constraints( + data = salt_con, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s3), main = c("baseline", "linear constraints"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Although using continuous values has the advantage that the prioritization process can explicitly account for differences in the relative amount of connectivity facilitated by different planning units, the disadvantage is that the prioritization could potentially focus on selecting lots of planning units with low connectivity values. To avoid this result, one strategy is to convert the continuous values into binary values using a threshold limit [similar to @r53]. By applying such a threshold limit, linear constraints can then be used to ensure that the prioritization selects a minimum amount of planning units with high connectivity values (i.e., those with connectivity values that are equal to or greater than the threshold limit). + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# calculate threshold limit +## here we set a threshold limit based on the median +threshold_limit <- quantile(salt_con, probs = 0.5) + +# convert continuous values to binary values +salt_con_binary <- round(salt_con <= threshold_limit) + +# plot binary values +plot(salt_con_binary, main = "salt_con_binary", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +## note that we use the original threshold computed before, +## to ensure the prioritization covers at least 30% of the total amount +## connectivity values +s4 <- p0 %>% + add_linear_constraints( + data = salt_con_binary, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s4), main = c("baseline", "linear constraints (binary)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Another strategy is to clamp the continuous values below a threshold limit are assigned a value of zero [similar to @r54]. This strategy has the advantage that (i) the prioritization won't focus on selecting lots of planning units with low connectivity values to meet the constraint threshold, and (ii) the optimization process can use semi-continuous values to distinguish between places that can facilitate a moderate amount and a high amount of connectivity. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, results = "hide"} +# clamp continuous values using the threshold limit we computed before +salt_con_clamp <- salt_con +salt_con_clamp[Which(salt_con <= threshold_limit)] <- 0 + +# plot clamped values +plot(salt_con_clamp, main = "salt_con_clamp", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +## note that we use the original threshold computed before, +## to ensure the prioritization covers at least 30% of the total amount +## connectivity values +s5 <- p0 %>% + add_linear_constraints( + data = salt_con_clamp, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s5), main = c("baseline", "linear constraints (clamped)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +If we were concerned that the prioritization did not facilitate a high enough level of connectivity, we could increase the `threshold` value or the `threshold_limit` value. For example, let's increase the `threshold_limit` value used to clamp the continuous connectivity values. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, results = "hide"} +# compute threshold limit +threshold_limit2 <- quantile(salt_con, probs = 0.7) + +# clamp continuous values using the threshold limit we computed before +salt_con_clamp2 <- salt_con +salt_con_clamp2[Which(salt_con <= threshold_limit2)] <- 0 + +# plot clamped values +plot(salt_con_clamp2, main = "salt_con_clamp", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +## note that we use the original threshold computed before, +## to ensure the prioritization covers at least 30% of the total amount +## connectivity values +s6 <- p0 %>% + add_linear_constraints( + data = salt_con_clamp2, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s6), main = c("baseline", "linear constraints (clamped 2)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Despite the advantages of clamping the connectivity values, we can see that the prioritization has a relatively high level of spatial fragmentation. In fact, all prioritizations generated using the linear constraints can potentially have this issue. This is because linear constraints do not explicitly account for the spatial arrangement of the planning units. As such, we recommend combining the linear constraints approach with another approach [e.g., the boundary penalties approach discussed below; @r53]. + +## Adding penalties + +Now let's explore approaches for promoting connectivity in prioritizations by adding penalties to the baseline problem formulation. These approaches involve penalizing solutions according to exhibit certain [e.g., penalize spatial fragmentation of prioritizations\; @r2]. Unlike constraint-based methods for incorporating connectivity -- if the optimality gap used to generate a prioritization is too high -- they may not necessarily produce prioritizations that exhibit desirable characteristics. + +### Boundary penalties + +Boundary penalties can be added to used to reduce the spatial fragmentation of prioritizations (using the `add_boundary_penalties()` function). Specifically, these penalties update the problem formulation to penalize solutions that have a high total amount of exposed boundary length [@r3]. Since boundary data often have large values which can degrade solver performance and result in excessive run times (see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html) for details), we will first precompute rescale the boundry data. + +```{r} +# precompute the boundary data +salt_boundary_data <- boundary_matrix(salt_pu) + +# rescale boundary data +salt_boundary_data@x <- rescale(salt_boundary_data@x, to = c(0.01, 100)) +``` + +Next, let's generate a prioritization using boundary penalties. To specify the relative importance reducing spatial fragmentation -- compared with the primary objective of a problem (e.g. minimizing cost) -- we need to a value for the `penalty` parameter is used. Setting a higher value for `penalty` indicates that it is more important to avoid highly fragmented solutions. Let's generate a prioritization with a `penalty` value of 0.001. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added boundary penalties +s7 <- p0 %>% + add_boundary_penalties(penalty = 0.001, data = salt_boundary_data) %>% + solve() + +# plot solutions +plot( + stack(s0, s7), main = c("baseline", "boundary penalties (0.001)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +We can see that the resulting prioritization is still relatively fragmented, so let's try generating another prioritization with a higher `penalty` value. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with increased boundary penalties +s8 <- p0 %>% + add_boundary_penalties(penalty = 10, data = salt_boundary_data) %>% + solve() + +# plot solutions +plot( + stack(s0, s8), main = c("baseline", "boundary penalties (10)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Although the prioritization is now less fragmented, it has also selected a greater number of planning units. Let's calculate the cost of the prioritizations to see how they vary in overall cost. + +```{r} +# calculate cost of baseline prioritization +eval_cost_summary(p0, s0) + +# calculate cost of prioritization with low boundary penalties (i.e., 0.001) +eval_cost_summary(p0, s7) + +# calculate cost of prioritization high low boundary penalties (i.e., 0.1) +eval_cost_summary(p0, s8) +``` + +We can see that the cost of the prioritizations increase with when we use higher `penalty` values. This is because there is a trade-off between the cost of a prioritization and the level of spatial fragmentation. Although it can be challenging to find the best balance, there are qualitative and quantitative methods available to help navigate such trade-offs. Please see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html) for a details on these methods. + +### Connectivity penalties + +Connectivity penalties can be used to promote connectivity in prioritizations (using the `add_connectivity_penalties()` function). These penalties use connectivity scores to parametrize the strength of connectivity between pairs of planning units [@r38]. Thus higher scores denote a greater level of connectivity between different planning units. For example, previous studies have parametrized connectivity scores using habitat quality, environmental, and river flow data [e.g. @r59; @r63; @r43]. Although there are many approaches to calculate connectivity scores, one approach involves using conductance data -- data that describe how much each planning unit facilitates movement (opposite of landscape resistance data) -- and calculating scores for each pair of planning units by averaging their conductance values (implemented using the `connectivity_matrix()` function). + +Let's compute connectivity scores by treating the `salt_con` object as conductance data. This means that we assume that neighboring planning units with higher values in the `salt_con` object are capable of facilitating a greater amount of connectivity. **Note that the data used to compute connectivity scores must conform to the same spatial properties as the planning unit data (e.g., resolution, spatial extent, coordinate reference system).** Also, although we are using raster data here, these scores can also be computed for vector data too (e.g., `sf::st_sf()` objects). Similar to the boundary data, we will also rescale the connectivity scores to avoid numerical issues during optimization. + +```{r} +# compute connectivity scores +salt_con_scores <- connectivity_matrix(salt_pu, salt_con) + +# rescale scores +salt_con_scores@x <- rescale(salt_con_scores@x, to = c(0.01, 100)) +``` + +After computing the connectivity scores, we can use them to generate prioritizations using connectivity penalties. Similar to the boundary penalties, we use the `penalty` parameter to specify the relative importance of promoting connectivity relative to the primary objective of a problem (i.e., minimizing overall cost). Let's generate a prioritization with a `penalty` value of 0.001. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added connectivity penalties +s9 <- p0 %>% + add_connectivity_penalties(penalty = 0.001, data = salt_con_scores) %>% + solve() + +# plot solutions +plot( + stack(s0, s9), main = c("baseline", "connectivity penalties (0.001)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Now let's try generating another prioritization with a higher `penalty` value. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with increased connectivity penalties +s10 <- p0 %>% + add_connectivity_penalties(penalty = 0.002, data = salt_con_scores) %>% + solve() + +# plot solutions +plot( + stack(s0, s10), main = c("baseline", "connectivity penalties (0.002)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +We can see that increasing the `penalty` parameter causes the prioritizations to select planning units in regions with greater connectivity values (i.e., per the `salt_con` object). As discussed with the boundary penalties, increasing the `penalty` value tells the optimization process to focus more on promoting connectivity---meaning that it won't focus as much on the primary objective (i.e., because the primary objective is to minimize overall costs). For details on calibrating these trade-offs please see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html). **Note that you will need to the use `eval_connectivity_summary()` function -- instead of the `eval_boundary_summary()` function -- when adapting the tutorial code for connectivity penalties.** + +## Conclusion + +Hopefully, this tutorial has provided a helpful introduction for incorporating connectivity into prioritizations. Broadly speaking, we recommend using the boundary penalties or the connectivity penalties to ensure that prioritizations explicitly account for the spatial configuration of selected planning units. Additionally, though not fully explored here, the connectivity penalties are a very flexible approach for promoting connectivity. For instance, in addition to parameterizing pair-wise connectivity scores for neighboring planning units, they can also be used to parametrize pair-wise connectivity scores between more distant planning units. Thus connectivity penalties could be used to parametrize connectivity across both small scales and large spatial scales (e.g., using a scaling procedure wherein connectivity scores between pairs of planning units decline with the distance between them). + +## References diff --git a/inst/doc/connectivity_tutorial.html b/inst/doc/connectivity_tutorial.html new file mode 100644 index 000000000..411b7e4ab --- /dev/null +++ b/inst/doc/connectivity_tutorial.html @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + +Connectivity tutorial + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Connectivity tutorial

    + + + + +
    +

    Introduction

    +

    Connectivity is a key consideration in systematic conservation planning (Margules & Pressey 2000; Briers 2002). This is because isolated and fragmented populations are often more vulnerable to extinction (Dixo et al. 2009; Olds et al. 2012; Hodgson et al. 2009). To promote connectivity in prioritizations, a range of different approaches are available (reviewed in Balbar & Metaxas 2019). These approaches can solely on the spatial configuration of a prioritization to enhance structural connectivity (e.g, reducing the spatial fragmentation of a prioritization; Watts et al. 2009). They can also leverage data – such as environmental, river flow, and telemetry data – to generate prioritizations that promote functional connectivity (e.g., Hermoso et al. 2012; Dwyer et al. 2019; Leonard et al. 2017).

    +

    The aim of this tutorial is to show how connectivity can be incorporated into prioritizations using the prioritizr R package. Here we will explore various approaches for incorporating connectivity, and see how they alter the spatial configuration of prioritizations. As you will discover, many of these approaches involve setting threshold or penalty values to specify the relative importance of connectivity compared to other criteria (e.g., overall cost). For more information on calibrating these values, please see the Calibrating trade-offs tutorial.

    +
    +
    +

    Data

    +

    The dataset used in this tutorial was created for the Coastal Douglas-fir Conservation Partnership (CDFCP; Morrell et al. 2017). Although the original dataset covers a much larger area; for brevity, here we focus only on Salt Spring Island, British Columbia. Briefly, Salt Spring Island supports a diverse and globally unique mix of dry forest and savanna habitats. Today, these habitats are critically threatened due to land conversion, invasive species, and altered disturbance regimes. For more information on the data, please refer to the Marxan tool portal and the tool tutorial.

    +
    +
    + +

    Extent of Coastal Douglas-fir Conservation Partnership Tool area and location of Salt Spring Island

    +
    +
    +

    Let’s begin by loading the packages and data for this tutorial. Since this tutorial requires the prioritizrdata R package, please ensure that it is installed. Specifically, two objects underpin the data for this tutorial. The salt_pu object specifies the planning unit data as a raster layer (i.e., RasterLayer object), and the salt_features object contains biodiversity data represented as a multi-band raster stack (i.e., a RasterStack object).

    +
    # load packages
    +library(prioritizr)
    +library(prioritizrdata)
    +library(scales)
    +
    +# load planning unit data
    +data(salt_pu)
    +
    +# load ecological data
    +data(salt_features)
    +

    Now we will conduct some preliminary processing. Specifically, we will aggregate from the 100 m resolution to the 300 m resolution. This is to reduce the time needed to generate prioritizations in this tutorial. In practice, we generally recommend consider other criteria too – such as the spatial scale that is relevant for decision making and the resolution of available datasets – when deciding on an appropriate scale for planning units.

    +
    # aggregate data to coarser resolution
    +salt_pu <- aggregate(salt_pu, fact = 3)
    +salt_features <- aggregate(salt_features, fact = 3)
    +

    Next, let’s have a look at the salt_pu object. Here each grid cell represents a planning unit, and the grid cell values denote acquisition costs (BC Assessment 2015). To aid with visualization, we will log-transform the values when plotting them on a map.

    +
    # print planning unit data
    +print(salt_pu)
    +
    ## class      : RasterLayer 
    +## dimensions : 94, 67, 6298  (nrow, ncol, ncell)
    +## resolution : 300, 300  (x, y)
    +## extent     : 454589.9, 474689.9, 5394414, 5422614  (xmin, xmax, ymin, ymax)
    +## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## source     : memory
    +## names      : salt_pu 
    +## values     : 0.02552, 8290.383  (min, max)
    +
    # plot map showing the planning units costs on a log-scale
    +plot(log(salt_pu), main = "Planning unit costs (log)", axes = FALSE)
    +

    +

    Let’s also look at the salt_features object. This object is a stack of raster layers, with each layer corresponding to a different variable that describes a particular aspect of biodiversity. The first four layers correspond to different ecological communities (i.e., Old Forest, Savannah, Wetland, and Shrub communities), and their cell values indicate the probability of encountering a bird species associated a given community. The fifth layer describes the inverse probability of occurrence of human commensal species. In this tutorial, we will use the first four layers as biodiversity features, and the fifth layer to help parameterize connectivity (wherein higher values denote greater connectivity). So, let’s extract the data and visualize them.

    +
    # print original data
    +print(salt_features)
    +
    ## class      : RasterBrick 
    +## dimensions : 94, 67, 6298, 5  (nrow, ncol, ncell, nlayers)
    +## resolution : 300, 300  (x, y)
    +## extent     : 454589.9, 474689.9, 5394414, 5422614  (xmin, xmax, ymin, ymax)
    +## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## source     : memory
    +## names      : salt_features.1, salt_features.2, salt_features.3, salt_features.4, salt_features.5 
    +## min values :       0.3596232,       0.3340984,       0.1420134,       0.4415211,       0.4060181 
    +## max values :       0.8951858,       0.6440319,       0.5926431,       0.8065580,       0.8969550
    +
    # extract connectivity data
    +salt_con <- salt_features[[nlayers(salt_features)]]
    +
    +# print connectivity data
    +print(salt_con)
    +
    ## class      : RasterLayer 
    +## dimensions : 94, 67, 6298  (nrow, ncol, ncell)
    +## resolution : 300, 300  (x, y)
    +## extent     : 454589.9, 474689.9, 5394414, 5422614  (xmin, xmax, ymin, ymax)
    +## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## source     : memory
    +## names      : salt_features.5 
    +## values     : 0.4060181, 0.896955  (min, max)
    +
    # plot map showing the connectivity data
    +plot(salt_con, main = "Connectivity data", axes = FALSE)
    +

    +
    # extract ecological communities and use these as features
    +salt_features <- salt_features[[seq_len(4)]]
    +
    +# assign names to features
    +names(salt_features) <- c("Old_Forest", "Savannah", "Wetland", "Shrub")
    +
    +# print features
    +print(salt_features)
    +
    ## class      : RasterBrick 
    +## dimensions : 94, 67, 6298, 4  (nrow, ncol, ncell, nlayers)
    +## resolution : 300, 300  (x, y)
    +## extent     : 454589.9, 474689.9, 5394414, 5422614  (xmin, xmax, ymin, ymax)
    +## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## source     : memory
    +## names      : Old_Forest,  Savannah,   Wetland,     Shrub 
    +## min values :  0.3596232, 0.3340984, 0.1420134, 0.4415211 
    +## max values :  0.8951858, 0.6440319, 0.5926431, 0.8065580
    +
    # plot map showing the distribution of the features
    +plot(salt_features, main = names(salt_features), axes = FALSE)
    +

    +
    +
    +

    Baseline problem

    +

    In this tutorial, we will explore a few different ways of incorporating connectivity into prioritizations. To enable comparisons among prioritizations based on different approaches, we will first create a baseline problem formulation that we will subsequently customize to incorporate connectivity. Specifically, we will formulate the baseline problem using the minimum set objective. We will use representation targets of 17% – based on Aichi Biodiversity Target 11 – to provide adequate coverage of each ecological community. Additionally, because land properties on Salt Spring Island can either be acquired in their entirety or not at all, we will use binary decision types. This means that planning units are either selected in the solution or not selected in the solution—planning units cannot be partially acquired. Given all these details, let’s formulate the baseline problem.

    +
    # create problem
    +p0 <- problem(salt_pu, salt_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.17) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver()
    +
    +# print problem
    +print(p0)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (2389 units)
    +##   cost:           min: 0.02552, max: 8290.38315
    +##   features:       Old_Forest, Savannah, Wetland, Shrub (4 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (1)]
    +

    After formulating the baseline problem, we can solve it to generate a prioritization.

    +
    # solve problem
    +s0 <- solve(p0)
    +
    # print solution
    +print(s0)
    +
    ## class      : RasterLayer 
    +## dimensions : 94, 67, 6298  (nrow, ncol, ncell)
    +## resolution : 300, 300  (x, y)
    +## extent     : 454589.9, 474689.9, 5394414, 5422614  (xmin, xmax, ymin, ymax)
    +## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## source     : memory
    +## names      : salt_pu 
    +## values     : 0, 1  (min, max)
    +
    # plot solution
    +plot(
    +  s0, main = "Baseline prioritization", axes = FALSE,
    +  breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    Next, let’s explore some options for incorporating connectivity.

    +
    +
    +

    Adding constraints

    +

    Let’s explore approaches for promoting connectivity in prioritizations by adding constraints to the baseline problem formulation. These approaches ensure that prioritizations exhibit certain characteristics (e.g., ensure prioritizations form a contiguous reserve; Önal & Briers 2006). This means that, regardless of the optimality gap used to generate a prioritization, the prioritization will always exhibit these characteristics.

    +
    +

    Neighbor constraints

    +

    Neighbor constraints can be added to ensure that each selected planning unit has a certain number of neighbors surrounding it (using the add_neighbor_constraints() function) (based on Billionnet 2013). The k parameter can be used to specify the required number of neighbors for each selected planning unit. Let’s generate a prioritization by specifying that each planning unit requires at least two neighbors.

    +
    # create problem with added neighbor constraints and solve it
    +s1 <- p0 %>%
    +      add_neighbor_constraints(k = 2) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s1), main = c("baseline", "neighbors constraints"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +
    +
    +

    Contiguity constraints

    +

    Contiguity constraints can be added to ensure that all planning units form a single contiguous reserve (using the add_contiguity_constraints() function) (similar to Önal & Briers 2006). These constraints are extremely complex. As such, they can only be applied to small conservation planning problems and the Gurobi solver is required to solve them in a feasible period of time. Since it would take a long time to generate a near-optimal prioritization for this dataset with contiguity constraints, we will also tell the solver to simply return the first solution that it finds which meets the representation targets and the contiguity constraints.

    +
    # create problem with added contiguity constraints and solve it
    +s2 <- p0 %>%
    +      add_contiguity_constraints() %>%
    +      add_gurobi_solver(first_feasible = TRUE) %>%
    +      solve()
    +
    ## Warning in res(x, ...): overwriting previously defined solver
    +
    # plot solutions
    +plot(
    +  stack(s0, s2), main = c("baseline", "contiguity constraints"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    There is also an even more complex version of the contiguity constraints that is available. These constraints – termed feature contiguity constraints (similar to Cerdeira et al. 2010) – can be added to ensure that all of the selected planning units used to the reach representation targets within a prioritization form a contiguous network for each feature (using the add_feature_contiguity_constraints() function). In other words, they ensure that each feature can disperse through the prioritization to access a target threshold amount of habitat. However, these constraints are extraordinarily complex, only feasible for small problems, and require preprocessing routines to identify initial solutions. As such, we will not consider them in this tutorial.

    +
    +
    +

    Linear constraints

    +

    Linear constraints can be used to specify that the prioritizations must meet an arbitrary set of criteria. As such, they can be used to ensure that prioritizations provide adequate coverage of planning units that have facilitate a high level of connectivity. Recall that the salt_con data are used to describe connectivity across the study area. Since higher values denote planning units with greater connectivity, we could use linear constraints to ensure that the total sum of connectivity values – based on this dataset – meets a particular threshold (e.g. cover at least 30% of the total amount). This would effectively be treating connectivity as an additional feature (similar to Daigle et al. 2020).

    +
    # compute threshold for constraints
    +## here we use a threshold of 30% of the total connectivity values
    +threshold <- cellStats(salt_con, "sum") * 0.3
    +
    +# print threshold
    +print(threshold)
    +
    ## [1] 520.7628
    +
    # create problem with added linear constraints and solve it
    +s3 <- p0 %>%
    +      add_linear_constraints(
    +        data = salt_con, threshold = threshold, sense = ">="
    +      ) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s3), main = c("baseline", "linear constraints"),
    +   axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    Although using continuous values has the advantage that the prioritization process can explicitly account for differences in the relative amount of connectivity facilitated by different planning units, the disadvantage is that the prioritization could potentially focus on selecting lots of planning units with low connectivity values. To avoid this result, one strategy is to convert the continuous values into binary values using a threshold limit (similar to Carroll 2021). By applying such a threshold limit, linear constraints can then be used to ensure that the prioritization selects a minimum amount of planning units with high connectivity values (i.e., those with connectivity values that are equal to or greater than the threshold limit).

    +
    # calculate threshold limit
    +## here we set a threshold limit based on the median
    +threshold_limit <- quantile(salt_con, probs = 0.5)
    +
    +# convert continuous values to binary values
    +salt_con_binary <- round(salt_con <= threshold_limit)
    +
    +# plot binary values
    +plot(salt_con_binary, main = "salt_con_binary", axes = FALSE)
    +

    +
    # create problem with added linear constraints and solve it
    +## note that we use the original threshold computed before,
    +## to ensure the prioritization covers at least 30% of the total amount
    +## connectivity values
    +s4 <- p0 %>%
    +      add_linear_constraints(
    +        data = salt_con_binary, threshold = threshold, sense = ">="
    +      ) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s4), main = c("baseline", "linear constraints (binary)"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    Another strategy is to clamp the continuous values below a threshold limit are assigned a value of zero (similar to Hanson et al. 2020). This strategy has the advantage that (i) the prioritization won’t focus on selecting lots of planning units with low connectivity values to meet the constraint threshold, and (ii) the optimization process can use semi-continuous values to distinguish between places that can facilitate a moderate amount and a high amount of connectivity.

    +
    # clamp continuous values using the threshold limit we computed before
    +salt_con_clamp <- salt_con
    +salt_con_clamp[Which(salt_con <= threshold_limit)] <- 0
    +
    +# plot clamped values
    +plot(salt_con_clamp, main = "salt_con_clamp", axes = FALSE)
    +

    +
    # create problem with added linear constraints and solve it
    +## note that we use the original threshold computed before,
    +## to ensure the prioritization covers at least 30% of the total amount
    +## connectivity values
    +s5 <- p0 %>%
    +      add_linear_constraints(
    +        data = salt_con_clamp, threshold = threshold, sense = ">="
    +      ) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s5), main = c("baseline", "linear constraints (clamped)"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    If we were concerned that the prioritization did not facilitate a high enough level of connectivity, we could increase the threshold value or the threshold_limit value. For example, let’s increase the threshold_limit value used to clamp the continuous connectivity values.

    +
    # compute threshold limit
    +threshold_limit2 <- quantile(salt_con, probs = 0.7)
    +
    +# clamp continuous values using the threshold limit we computed before
    +salt_con_clamp2 <- salt_con
    +salt_con_clamp2[Which(salt_con <= threshold_limit2)] <- 0
    +
    +# plot clamped values
    +plot(salt_con_clamp2, main = "salt_con_clamp", axes = FALSE)
    +

    +
    # create problem with added linear constraints and solve it
    +## note that we use the original threshold computed before,
    +## to ensure the prioritization covers at least 30% of the total amount
    +## connectivity values
    +s6 <- p0 %>%
    +      add_linear_constraints(
    +        data = salt_con_clamp2, threshold = threshold, sense = ">="
    +      ) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s6), main = c("baseline", "linear constraints (clamped 2)"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    Despite the advantages of clamping the connectivity values, we can see that the prioritization has a relatively high level of spatial fragmentation. In fact, all prioritizations generated using the linear constraints can potentially have this issue. This is because linear constraints do not explicitly account for the spatial arrangement of the planning units. As such, we recommend combining the linear constraints approach with another approach [e.g., the boundary penalties approach discussed below; Carroll (2021)].

    +
    +
    +
    +

    Adding penalties

    +

    Now let’s explore approaches for promoting connectivity in prioritizations by adding penalties to the baseline problem formulation. These approaches involve penalizing solutions according to exhibit certain (e.g., penalize spatial fragmentation of prioritizations; Watts et al. 2009). Unlike constraint-based methods for incorporating connectivity – if the optimality gap used to generate a prioritization is too high – they may not necessarily produce prioritizations that exhibit desirable characteristics.

    +
    +

    Boundary penalties

    +

    Boundary penalties can be added to used to reduce the spatial fragmentation of prioritizations (using the add_boundary_penalties() function). Specifically, these penalties update the problem formulation to penalize solutions that have a high total amount of exposed boundary length (Ball et al. 2009). Since boundary data often have large values which can degrade solver performance and result in excessive run times (see the Calibrating trade-offs tutorial for details), we will first precompute rescale the boundry data.

    +
    # precompute the boundary data
    +salt_boundary_data <- boundary_matrix(salt_pu)
    +
    +# rescale boundary data
    +salt_boundary_data@x <- rescale(salt_boundary_data@x, to = c(0.01, 100))
    +

    Next, let’s generate a prioritization using boundary penalties. To specify the relative importance reducing spatial fragmentation – compared with the primary objective of a problem (e.g. minimizing cost) – we need to a value for the penalty parameter is used. Setting a higher value for penalty indicates that it is more important to avoid highly fragmented solutions. Let’s generate a prioritization with a penalty value of 0.001.

    +
    # create problem with added boundary penalties
    +s7 <- p0 %>%
    +      add_boundary_penalties(penalty = 0.001, data = salt_boundary_data) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s7), main = c("baseline", "boundary penalties (0.001)"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    We can see that the resulting prioritization is still relatively fragmented, so let’s try generating another prioritization with a higher penalty value.

    +
    # create problem with increased boundary penalties
    +s8 <- p0 %>%
    +      add_boundary_penalties(penalty = 10, data = salt_boundary_data) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s8), main = c("baseline", "boundary penalties (10)"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    Although the prioritization is now less fragmented, it has also selected a greater number of planning units. Let’s calculate the cost of the prioritizations to see how they vary in overall cost.

    +
    # calculate cost of baseline prioritization
    +eval_cost_summary(p0, s0)
    +
    ## # A tibble: 1 × 2
    +##   summary  cost
    +##   <chr>   <dbl>
    +## 1 overall  46.0
    +
    # calculate cost of prioritization with low boundary penalties (i.e., 0.001)
    +eval_cost_summary(p0, s7)
    +
    ## # A tibble: 1 × 2
    +##   summary  cost
    +##   <chr>   <dbl>
    +## 1 overall  46.0
    +
    # calculate cost of prioritization high low boundary penalties (i.e., 0.1)
    +eval_cost_summary(p0, s8)
    +
    ## # A tibble: 1 × 2
    +##   summary  cost
    +##   <chr>   <dbl>
    +## 1 overall  52.3
    +

    We can see that the cost of the prioritizations increase with when we use higher penalty values. This is because there is a trade-off between the cost of a prioritization and the level of spatial fragmentation. Although it can be challenging to find the best balance, there are qualitative and quantitative methods available to help navigate such trade-offs. Please see the Calibrating trade-offs tutorial for a details on these methods.

    +
    +
    +

    Connectivity penalties

    +

    Connectivity penalties can be used to promote connectivity in prioritizations (using the add_connectivity_penalties() function). These penalties use connectivity scores to parametrize the strength of connectivity between pairs of planning units (Beger et al. 2010). Thus higher scores denote a greater level of connectivity between different planning units. For example, previous studies have parametrized connectivity scores using habitat quality, environmental, and river flow data (e.g. Leonard et al. 2017; Alagador et al. 2012; Hermoso et al. 2012). Although there are many approaches to calculate connectivity scores, one approach involves using conductance data – data that describe how much each planning unit facilitates movement (opposite of landscape resistance data) – and calculating scores for each pair of planning units by averaging their conductance values (implemented using the connectivity_matrix() function).

    +

    Let’s compute connectivity scores by treating the salt_con object as conductance data. This means that we assume that neighboring planning units with higher values in the salt_con object are capable of facilitating a greater amount of connectivity. Note that the data used to compute connectivity scores must conform to the same spatial properties as the planning unit data (e.g., resolution, spatial extent, coordinate reference system). Also, although we are using raster data here, these scores can also be computed for vector data too (e.g., sf::st_sf() objects). Similar to the boundary data, we will also rescale the connectivity scores to avoid numerical issues during optimization.

    +
    # compute connectivity scores
    +salt_con_scores <- connectivity_matrix(salt_pu, salt_con)
    +
    +# rescale scores
    +salt_con_scores@x <- rescale(salt_con_scores@x, to = c(0.01, 100))
    +

    After computing the connectivity scores, we can use them to generate prioritizations using connectivity penalties. Similar to the boundary penalties, we use the penalty parameter to specify the relative importance of promoting connectivity relative to the primary objective of a problem (i.e., minimizing overall cost). Let’s generate a prioritization with a penalty value of 0.001.

    +
    # create problem with added connectivity penalties
    +s9 <- p0 %>%
    +      add_connectivity_penalties(penalty = 0.001, data = salt_con_scores) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s9), main = c("baseline", "connectivity penalties (0.001)"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    Now let’s try generating another prioritization with a higher penalty value.

    +
    # create problem with increased connectivity penalties
    +s10 <- p0 %>%
    +      add_connectivity_penalties(penalty = 0.002, data = salt_con_scores) %>%
    +      solve()
    +
    +# plot solutions
    +plot(
    +  stack(s0, s10), main = c("baseline", "connectivity penalties (0.002)"),
    +  axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")
    +)
    +

    +

    We can see that increasing the penalty parameter causes the prioritizations to select planning units in regions with greater connectivity values (i.e., per the salt_con object). As discussed with the boundary penalties, increasing the penalty value tells the optimization process to focus more on promoting connectivity—meaning that it won’t focus as much on the primary objective (i.e., because the primary objective is to minimize overall costs). For details on calibrating these trade-offs please see the Calibrating trade-offs tutorial. Note that you will need to the use eval_connectivity_summary() function – instead of the eval_boundary_summary() function – when adapting the tutorial code for connectivity penalties.

    +
    +
    +
    +

    Conclusion

    +

    Hopefully, this tutorial has provided a helpful introduction for incorporating connectivity into prioritizations. Broadly speaking, we recommend using the boundary penalties or the connectivity penalties to ensure that prioritizations explicitly account for the spatial configuration of selected planning units. Additionally, though not fully explored here, the connectivity penalties are a very flexible approach for promoting connectivity. For instance, in addition to parameterizing pair-wise connectivity scores for neighboring planning units, they can also be used to parametrize pair-wise connectivity scores between more distant planning units. Thus connectivity penalties could be used to parametrize connectivity across both small scales and large spatial scales (e.g., using a scaling procedure wherein connectivity scores between pairs of planning units decline with the distance between them).

    +
    +
    +

    References

    +
    +
    +Alagador, D., Trivino, M., Cerdeira, J.O., Brás, R., Cabeza, M. & Araújo, M.B. (2012). Linking like with like: Optimising connectivity between environmentally-similar habitats. Landscape Ecology, 27, 291–301. +
    +
    +Balbar, A.C. & Metaxas, A. (2019). The current application of ecological connectivity in the design of marine protected areas. Global Ecology and Conservation, 17, e00569. +
    +
    +Ball, I.R., Possingham, H. & Watts, M.E. (2009). Marxan and relatives: Software for spatial conservation prioritisation. Spatial Conservation Prioritisation: Quantitative Methods & Computational Tools (eds A. Moilanen, K.A. Wilson & H. Possingham), pp. 185–189. Oxford University Press, Oxford, UK. +
    +
    +BC Assessment. (2015). Property Information Services. URL https://info.bcassessment.ca/ [accessed 13 June 2016]. +
    +
    +Beger, M., Linke, S., Watts, M., Game, E., Treml, E., Ball, I. & Possingham, H.P. (2010). Incorporating asymmetric connectivity into spatial decision making for conservation. Conservation Letters, 3, 359–368. +
    +
    +Billionnet, A. (2013). Mathematical optimization ideas for biodiversity conservation. European Journal of Operational Research, 231, 514–534. +
    +
    +Briers, R.A. (2002). Incorporating connectivity into reserve selection procedures. Biological Conservation, 103, 77–83. +
    +
    +Carroll, K.A. (2021). Systematic prioritization protocol applied to wolverine habitat connectivity. STAR Protocols, 2, 100882. +
    +
    +Cerdeira, J.O., Pinto, L.S., Cabeza, M. & Gaston, K.J. (2010). Species specific connectivity in reserve-network design using graphs. Biological Conservation, 143, 408–415. +
    +
    +Daigle, R.M., Metaxas, A., Balbar, A.C., McGowan, J., Treml, E.A., Kuempel, C.D., Possingham, H.P. & Beger, M. (2020). Operationalizing ecological connectivity in spatial conservation planning with Marxan Connect. Methods in Ecology and Evolution, 11, 570–579. +
    +
    +Dixo, M., Metzger, J.P., Morgante, J.S. & Zamudio, K.R. (2009). Habitat fragmentation reduces genetic diversity and connectivity among toad populations in the brazilian atlantic coastal forest. Biological Conservation, 142, 1560–1569. +
    +
    +Dwyer, R.G., Campbell, H.A., Pillans, R.D., Watts, M.E., Lyon, B.J., Guru, S.M., Dinh, M.N., Possingham, H.P. & Franklin, C.E. (2019). Using individual-based movement information to identify spatial conservation priorities for mobile species. Conservation Biology, 33, 1426–1437. +
    +
    +Hanson, J.O., Marques, A., Verı́ssimo, A., Camacho-Sanchez, M., Velo-Antón, G., Martı́nez-Solano, Íñigo & Carvalho, S.B. (2020). Conservation planning for adaptive and neutral evolutionary processes. Journal of Applied Ecology, 57, 2159–2169. +
    +
    +Hermoso, V., Kennard, M.J. & Linke, S. (2012). Integrating multidirectional connectivity requirements in systematic conservation planning for freshwater systems. Diversity and Distributions, 18, 448–458. +
    +
    +Hodgson, J.A., Thomas, C.D., Wintle, B.A. & Moilanen, A. (2009). Climate change, connectivity and conservation decision making: Back to basics. Journal of Applied Ecology, 46, 964–969. +
    +
    +Leonard, P.B., Baldwin, R.F. & Hanks, R.D. (2017). Landscape-scale conservation design across biotic realms: Sequential integration of aquatic and terrestrial landscapes. Scientific Reports, 7, 1–12. +
    +
    +Margules, C.R. & Pressey, R.L. (2000). Systematic conservation planning. Nature, 405, 243–253. +
    +
    +Morrell, N., Schuster, R., Crombie, M. & Arcese, P. (2017). A Prioritization Tool for the Conservation of Coastal Douglas-fir Forest and Savannah Habitats of the Georgia Basin. The Nature Trust of British Colombia, Coastal Douglas Fir Conservation Partnership, and the Department of Forest and Conservation Sciences, University of British Colombia, URL https://peter-arcese-lab.sites.olt.ubc.ca/files/2016/09/CDFCP_tutorial_2017_05.pdf [accessed 9 October 2017]. +
    +
    +Olds, A.D., Connolly, R.M., Pitt, K.A. & Maxwell, P.S. (2012). Habitat connectivity improves reserve performance. Conservation Letters, 5, 56–63. +
    +
    +Önal, H. & Briers, R.A. (2006). Optimal selection of a connected reserve network. Operations Research, 54, 379–388. +
    +
    +Watts, M.E., Ball, I.R., Stewart, R.S., Klein, C.J., Wilson, K., Steinback, C., Lourival, R., Kircher, L. & Possingham, H.P. (2009). Marxan with Zones: Software for optimal conservation based land- and sea-use zoning. Environmental Modelling & Software, 24, 1513–1521. +
    +
    +
    + + + + + + + + + + + diff --git a/vignettes/gurobi_installation.Rmd b/inst/doc/gurobi_installation_guide.Rmd similarity index 88% rename from vignettes/gurobi_installation.Rmd rename to inst/doc/gurobi_installation_guide.Rmd index 016dd7dfd..e36fe4832 100644 --- a/vignettes/gurobi_installation.Rmd +++ b/inst/doc/gurobi_installation_guide.Rmd @@ -1,5 +1,5 @@ --- -title: "Gurobi Installation Guide" +title: "Gurobi installation guide" author: "Richard Schuster" date: "`r Sys.Date()`" output: @@ -10,7 +10,7 @@ output: fontsize: 11pt documentclass: article vignette: > - %\VignetteIndexEntry{Gurobi Installation Guide} + %\VignetteIndexEntry{Gurobi installation guide} %\VignetteEngine{knitr::rmarkdown_notangle} --- @@ -28,7 +28,7 @@ devtools::load_all() ## Introduction -_Gurobi_ is the most powerful and fastest solver that the _prioritizr R_ package can use to solve conservation planning problems. This vignette will walk you through the process of setting up _Gurobi_ on your computer so that you can use it to solve conservation planning problems. If you encounter any problems while following the instructions below, check out the [official _Gurobi_ documentation](https://www.gurobi.com/documentation/). +_Gurobi_ is the most powerful and fastest solver that the _prioritizr R_ package can use to solve conservation planning problems (see the [_Solver benchmarks_](solver_benchmarks.html) vignette for further details). This guide will walk you through the process of setting up _Gurobi_ on your computer so that it can be used to solve conservation planning problems. If you encounter any problems while following the instructions below, please refer to the [official _Gurobi_ documentation](https://www.gurobi.com/documentation/). ## Obtaining a license @@ -48,7 +48,7 @@ After obtaining a license, you will need to download the _Gurobi_ installer to y ## Software installation -The process for installing the _Gurobi_ software depends on the operating system on your computer. Fortunately, _Gurobi_ provide platform-specific ["Quick Start Guides"](https://www.gurobi.com/documentation/) for [Windows](https://www.gurobi.com/documentation/9.0/quickstart_windows/software_installation_guid.html#section:Installation), [MacOS](https://www.gurobi.com/documentation/9.0/quickstart_mac/software_installation_guid.html), and [Linux](https://www.gurobi.com/documentation/9.0/quickstart_linux/software_installation_guid.html) systems that should help with this. Briefly, on Windows systems, you just need to double-click on the _Gurobi_ installer, follow the prompts, and the installer will automatically handle everything for you. On Linux and MacOS systems, you will need to manually extract the downloaded file's contents to a folder, move the extracted contents to a suitable location (typically _/opt/gurobi_), and update your system's variables so that it knows where to find _Gurobi_ (i.e. the `PATH` variable). +The process for installing the _Gurobi_ software depends on the operating system on your computer. Fortunately, _Gurobi_ provide platform-specific ["Quick Start Guides"](https://www.gurobi.com/documentation/) for [Windows](https://www.gurobi.com/documentation/9.0/quickstart_windows/software_installation_guid.html#section:Installation), [MacOS](https://www.gurobi.com/documentation/9.0/quickstart_mac/software_installation_guid.html), and [Linux](https://www.gurobi.com/documentation/9.0/quickstart_linux/software_installation_guid.html) systems that should help with this. Briefly, on Windows systems, you just need to double-click on the _Gurobi_ installer, follow the prompts, and the installer will automatically handle everything for you. On Linux and MacOS systems, you will need to manually extract the downloaded file's contents to a folder, move the extracted contents to a suitable location (typically _/opt/gurobi_), and update your system's variables so that it knows where to find _Gurobi_ (i.e., the `PATH` variable). Additionally, if you are using [_RStudio_](https://www.rstudio.com/products/rstudio/) on a Linux system, you might need to add the following text to a Rstudio configuration file (located at `/etc/rstudio/rserver.conf`). @@ -64,7 +64,7 @@ Now we will activate the _Gurobi_ software using the license you obtained earlie
    ![](figures/cmd-windows-success.png){ width=75% }

    -Next, we will now check that the license has been successfully activated. To achieve this, we will try running _Gurobi_ directly from the command line. Note that the following commands assume you are using version 8.0.0 of _Gurobi_, and so you will need to modify the command if you are using a more recent version (e.g. if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). +Next, we will now check that the license has been successfully activated. To achieve this, we will try running _Gurobi_ directly from the command line. Note that the following commands assume you are using version 8.0.0 of _Gurobi_, and so you will need to modify the command if you are using a more recent version (e.g., if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). On Windows systems, users can type in the following system command to check their license activation. @@ -86,7 +86,7 @@ After activating the license, you now need to install the _gurobi R_ package. Th ## _R_ package installation -Now we will install the _gurobi R_ package. This package is not available on the Comprehensive R Archive Network and is instead distributed with the _Gurobi_ software suite. Specifically, the _gurobi_ _R_ package should be located within the folder where you installed the _Gurobi_ software suite. We will install the _gurobi_ _R_ package by running the following _R_ code within your _R_ session. Note that the following code assumes that you are using version 8.0.0 of _Gurobi_, and so you will need to modify the code if you are using a more recent version (e.g. if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). +Now we will install the _gurobi R_ package. This package is not available on the Comprehensive R Archive Network and is instead distributed with the _Gurobi_ software suite. Specifically, the _gurobi_ _R_ package should be located within the folder where you installed the _Gurobi_ software suite. We will install the _gurobi_ _R_ package by running the following _R_ code within your _R_ session. Note that the following code assumes that you are using version 8.0.0 of _Gurobi_, and so you will need to modify the code if you are using a more recent version (e.g., if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). Assuming you installed _Gurobi_ in the default location, Windows users can install _gurobi_ _R_ package using the following code. @@ -102,7 +102,7 @@ install.packages(file.path(Sys.getenv("GUROBI_HOME"), repos = NULL) ``` -Next, you will need to install the _slam R_ package because the _gurobi_ _R_ package needs this package to work. Users of all platforms (i.e. Windows, Linux, and MacOS) can install the package using the following _R_ code. +Next, you will need to install the _slam R_ package because the _gurobi_ _R_ package needs this package to work. Users of all platforms (i.e., Windows, Linux, and MacOS) can install the package using the following _R_ code. ```{r, eval = FALSE} install.packages("slam", repos = "https://cloud.r-project.org") @@ -136,7 +136,7 @@ If you see the outputs for `result$objval` and `result$x` and you don't see any ## Solving a _prioritzr_ problem with _Gurobi_ -If you successfully installed the _Gurobi_ software suite and the _gurobi_ _R_ package, you can now try solving conservation planning problems using the _prioritzr_ _R_ package. Although the _prioritizr_ _R_ package should automatically detect that _Gurobi_ has been installed, you can use the function `add_gurobi_solver` to manually specify that _Gurobi_ should be used to solve problems. This function is also useful because you can use it to customize the optimization process (e.g. specify the desired optimality gap or set a limit on how much time should be spent searching for a solution). +If you successfully installed the _Gurobi_ software suite and the _gurobi_ _R_ package, you can now try solving conservation planning problems using the _prioritzr_ _R_ package. Although the _prioritizr_ _R_ package should automatically detect that _Gurobi_ has been installed, you can use the function `add_gurobi_solver()` to manually specify that _Gurobi_ should be used to solve problems. This function is also useful because you can use it to customize the optimization process (e.g., specify the desired optimality gap or set a limit on how much time should be spent searching for a solution). Finally, to check that everything has been installed correctly, we will use the _Gurobi_ software suite to solve a reserve selection problem created using the _prioritzr_ _R_ package. diff --git a/inst/doc/gurobi_installation.html b/inst/doc/gurobi_installation_guide.html similarity index 99% rename from inst/doc/gurobi_installation.html rename to inst/doc/gurobi_installation_guide.html index 9c6613b8b..263f45ca2 100644 --- a/inst/doc/gurobi_installation.html +++ b/inst/doc/gurobi_installation_guide.html @@ -12,9 +12,9 @@ - + -Gurobi Installation Guide +Gurobi installation guide @@ -139,9 +139,9 @@ -

    Gurobi Installation Guide

    +

    Gurobi installation guide

    Richard Schuster

    -

    2021-11-16

    +

    2021-12-02

    @@ -158,7 +158,7 @@

    2021-11-16

    Introduction

    -

    Gurobi is the most powerful and fastest solver that the prioritizr R package can use to solve conservation planning problems. This vignette will walk you through the process of setting up Gurobi on your computer so that you can use it to solve conservation planning problems. If you encounter any problems while following the instructions below, check out the official Gurobi documentation.

    +

    Gurobi is the most powerful and fastest solver that the prioritizr R package can use to solve conservation planning problems (see the Solver benchmarks vignette for further details). This guide will walk you through the process of setting up Gurobi on your computer so that it can be used to solve conservation planning problems. If you encounter any problems while following the instructions below, please refer to the official Gurobi documentation.

    Obtaining a license

    @@ -182,7 +182,7 @@

    Downloading the software

    Software installation

    -

    The process for installing the Gurobi software depends on the operating system on your computer. Fortunately, Gurobi provide platform-specific “Quick Start Guides” for Windows, MacOS, and Linux systems that should help with this. Briefly, on Windows systems, you just need to double-click on the Gurobi installer, follow the prompts, and the installer will automatically handle everything for you. On Linux and MacOS systems, you will need to manually extract the downloaded file’s contents to a folder, move the extracted contents to a suitable location (typically /opt/gurobi), and update your system’s variables so that it knows where to find Gurobi (i.e. the PATH variable).

    +

    The process for installing the Gurobi software depends on the operating system on your computer. Fortunately, Gurobi provide platform-specific “Quick Start Guides” for Windows, MacOS, and Linux systems that should help with this. Briefly, on Windows systems, you just need to double-click on the Gurobi installer, follow the prompts, and the installer will automatically handle everything for you. On Linux and MacOS systems, you will need to manually extract the downloaded file’s contents to a folder, move the extracted contents to a suitable location (typically /opt/gurobi), and update your system’s variables so that it knows where to find Gurobi (i.e., the PATH variable).

    Additionally, if you are using RStudio on a Linux system, you might need to add the following text to a Rstudio configuration file (located at /etc/rstudio/rserver.conf).

    rsession-ld-library-path=/opt/gurobi650/linux64/lib

    After installing the Gurobi software suite on your computer, you will need to activate your license.

    @@ -195,7 +195,7 @@

    License activation


    -

    Next, we will now check that the license has been successfully activated. To achieve this, we will try running Gurobi directly from the command line. Note that the following commands assume you are using version 8.0.0 of Gurobi, and so you will need to modify the command if you are using a more recent version (e.g. if using version 9.1.2, then use gurobi912 instead of gurobi800 below).

    +

    Next, we will now check that the license has been successfully activated. To achieve this, we will try running Gurobi directly from the command line. Note that the following commands assume you are using version 8.0.0 of Gurobi, and so you will need to modify the command if you are using a more recent version (e.g., if using version 9.1.2, then use gurobi912 instead of gurobi800 below).

    On Windows systems, users can type in the following system command to check their license activation.

    gurobi_cl c:\gurobi800\win64\examples\data\coins.lp

    On Linux and MacOS systems, users can type in the following system command.

    @@ -210,14 +210,14 @@

    License activation

    R package installation

    -

    Now we will install the gurobi R package. This package is not available on the Comprehensive R Archive Network and is instead distributed with the Gurobi software suite. Specifically, the gurobi R package should be located within the folder where you installed the Gurobi software suite. We will install the gurobi R package by running the following R code within your R session. Note that the following code assumes that you are using version 8.0.0 of Gurobi, and so you will need to modify the code if you are using a more recent version (e.g. if using version 9.1.2, then use gurobi912 instead of gurobi800 below).

    +

    Now we will install the gurobi R package. This package is not available on the Comprehensive R Archive Network and is instead distributed with the Gurobi software suite. Specifically, the gurobi R package should be located within the folder where you installed the Gurobi software suite. We will install the gurobi R package by running the following R code within your R session. Note that the following code assumes that you are using version 8.0.0 of Gurobi, and so you will need to modify the code if you are using a more recent version (e.g., if using version 9.1.2, then use gurobi912 instead of gurobi800 below).

    Assuming you installed Gurobi in the default location, Windows users can install gurobi R package using the following code.

    install.packages("c:/gurobi800/win64/R/gurobi_8.0-0.zip", repos = NULL)

    Similarly, Linux and MacOS users can install the gurobi R package using the following code.

    install.packages(file.path(Sys.getenv("GUROBI_HOME"),
                                "R/gurobi_8.0-0_R_x86_64-pc-linux-gnu.tar.gz"),
                      repos = NULL)
    -

    Next, you will need to install the slam R package because the gurobi R package needs this package to work. Users of all platforms (i.e. Windows, Linux, and MacOS) can install the package using the following R code.

    +

    Next, you will need to install the slam R package because the gurobi R package needs this package to work. Users of all platforms (i.e., Windows, Linux, and MacOS) can install the package using the following R code.

    install.packages("slam", repos = "https://cloud.r-project.org")

    Let’s check that the gurobi R package has been successfully installed. To do this, we can try using the gurobi R package to solve an optimization problem. Copy and paste the R code below into R.

    # load gurobi package
    @@ -266,7 +266,7 @@ 

    R package installation

    Solving a prioritzr problem with Gurobi

    -

    If you successfully installed the Gurobi software suite and the gurobi R package, you can now try solving conservation planning problems using the prioritzr R package. Although the prioritizr R package should automatically detect that Gurobi has been installed, you can use the function add_gurobi_solver to manually specify that Gurobi should be used to solve problems. This function is also useful because you can use it to customize the optimization process (e.g. specify the desired optimality gap or set a limit on how much time should be spent searching for a solution).

    +

    If you successfully installed the Gurobi software suite and the gurobi R package, you can now try solving conservation planning problems using the prioritzr R package. Although the prioritizr R package should automatically detect that Gurobi has been installed, you can use the function add_gurobi_solver() to manually specify that Gurobi should be used to solve problems. This function is also useful because you can use it to customize the optimization process (e.g., specify the desired optimality gap or set a limit on how much time should be spent searching for a solution).

    Finally, to check that everything has been installed correctly, we will use the Gurobi software suite to solve a reserve selection problem created using the prioritzr R package.

    library(prioritizr)
     
    diff --git a/vignettes/zones.Rmd b/inst/doc/management_zones_tutorial.Rmd
    similarity index 70%
    rename from vignettes/zones.Rmd
    rename to inst/doc/management_zones_tutorial.Rmd
    index b28c8541d..48efd1835 100644
    --- a/vignettes/zones.Rmd
    +++ b/inst/doc/management_zones_tutorial.Rmd
    @@ -1,5 +1,5 @@
     ---
    -title: "Management Zones"
    +title: "Management zones tutorial"
     output:
       rmarkdown::html_vignette:
         toc: true
    @@ -10,7 +10,7 @@ documentclass: article
     bibliography: references.bib
     csl: reference-style.csl
     vignette: >
    -  %\VignetteIndexEntry{Management Zones}
    +  %\VignetteIndexEntry{Management zones tutorial}
       %\VignetteEngine{knitr::rmarkdown_notangle}
     ---
     
    @@ -23,21 +23,22 @@ knitr::opts_chunk$set(fig.align = "center", eval = !is_check,
     
     ## Introduction
     
    -One of the main aims in conservation planning is to identify the most cost-effective set of areas to manage biodiversity [@r4]. To achieve this, prioritizations are generally created to identify areas for expanding protected area systems. However, many real-world conservation problems do not simply involve deciding if an area should be protected or not [e.g. @r5; @r18]. Instead, many real-world problems involve a range of different management categories and the goal is to determine which areas should be allocated to which management category. For example, a manager might have a range of different methods (e.g. baiting or trapping at various intensities) for controlling invasive pests in a set of different areas [e.g. @r25]. They would need a prioritization that shows which control methods should be implemented in which areas. In this particular case, a binary prioritization showing which areas contain the most biodiversity is simply not helpful. Furthermore, many real-world problems require decisions that meet multiple, and sometimes conflicting, objectives from different stakeholders. For example, a manager might need to implement a set of no-take and partial-take areas to prevent overfishing, but also ensure that there still remain plenty of areas for fishing activities [e.g. @r26; @r27]. Popularized by _Marxan with Zones_ [@r2], this concept has become known as "zones" and is becoming increasingly important in conservation planning.
    +One of the main aims in conservation planning is to identify the most cost-effective set of areas to manage biodiversity [@r4]. To achieve this, prioritizations are generally created to identify areas for expanding protected area systems. However, many real-world conservation problems do not simply involve deciding if an area should be protected or not [e.g., @r5; @r18]. Instead, many problems involve a range of different management categories and the goal is to determine which areas should be allocated to which management category. For example, a manager might have a range of different methods (e.g., baiting or trapping at various intensities) for controlling invasive pests in a set of different areas [e.g., @r25]. They would need a prioritization that shows which control methods should be implemented in which areas. In this particular case, a binary prioritization showing which areas contain the most biodiversity is simply not helpful. Furthermore, many real-world problems require decisions that meet multiple, and sometimes conflicting, objectives from different stakeholders. For example, a manager might need to implement a set of no-take and partial-take areas to prevent overfishing, but also ensure that there still remain plenty of areas for fishing activities [e.g., @r26; @r27]. Popularized by the _Marxan with Zones_ decision support tool [@r2], this concept has become known as "zones" and is becoming increasingly important in conservation planning.
     
    -The aim of this vignette is to showcase the zones functionality of the _prioritizr R_ package. It will assume a certain level of familiarity with conservation planning terminology and the _prioritizr R_ package. We recommend reading the [_prioritizr_ vignette](prioritizr.html) first if you don't have much experience in either of these topics.
    +The aim of this tutorial is to showcase the zones functionality of the _prioritizr R_ package. It will assume a certain level of familiarity with conservation planning terminology and the package. If you don't have much experience in either of these topics, we recommend first reading the [_Package overview_](package_overview.html) vignette.
     
     ## Usage
     
    -
     ### Simple minimum set problem
     
    -In the _prioritizr R_ package, all conservation planning problems---including those which contain multiple management zones or actions---are initialized using the `problem` function. To refresh our memory on how we can construct problems using the _prioritizr R_ package, let us quickly construct a simple conservation planning problem. This problem will use the simulated built-in planning unit and feature data distributed with the package. It will have a minimum set objective, targets which require that solution secure to 10 % of the habitat in the study area for each feature, and binary decision variables indicating that planning units are selected or not selected for protection.
    +In the _prioritizr R_ package, all conservation planning problems -- including those which contain multiple management zones or actions -- are initialized using the `problem` function. To refresh our memory on how we can construct problems, let us quickly construct a simple conservation planning problem. This problem will use the simulated built-in planning unit and feature data distributed with the package. It will have a minimum set objective, targets which require that solution secure to 10 % of the habitat in the study area for each feature, and binary decision variables indicating that planning units are selected or not selected for protection.
     
    -```{r}
    +```{r, results = "hide"}
     # load prioritizr package
     library(prioritizr)
    +```
     
    +```{r}
     # load data
     data(sim_pu_raster, sim_features)
     
    @@ -66,7 +67,7 @@ plot(s1, main = "solution", axes = FALSE, box = FALSE)
     
     ### Adding management zones
     
    -Now let us imagine that instead of having a single management zone (e.g. protected area), we have two management zones. Similar to the example above, we require a solution that secures 10 % of the habitat in the study area for each feature in the first management zone. But we also require a solution that secures 5 % of the habitat in the study area for each feature in the second management zone. Each planning unit must be allocated to either zone or not selected for management at all. In this example, each planning unit costs the same when it is allocated to either of the two zones. We can formulate and solve this problem using the following code.
    +Now let us imagine that instead of having a single management zone (e.g., protected area), we have two management zones. Similar to the example above, we require a solution that secures 10 % of the habitat in the study area for each feature in the first management zone. But we also require a solution that secures 5 % of the habitat in the study area for each feature in the second management zone. Each planning unit must be allocated to either zone or not selected for management at all. In this example, each planning unit costs the same when it is allocated to either of the two zones. We can formulate and solve this problem using the following code.
     
     ```{r}
     # create a matrix with the targets
    @@ -125,7 +126,7 @@ plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
     
     ### Multiple zones with varying costs
     
    -Real-world problems often have different costs for managing planning units under different zones. These problems also tend to have different expected amounts of each feature when planning units are managed differently. So let us consider a slightly more complex example. Similar to before we will have two management zones. But this time, the cost of managing each planning unit is different depending on which management zone it is assigned to in the solution. Furthermore, when we assign a planning unit to the second zone, we only expect to end up with half of the habitat we would get if we managed the unit in the first zone (e.g. because the second zone is a partial-take zone and the first zone is a no-take zone). We will use the same target data as in the previous example.
    +Real-world problems often have different costs for managing planning units under different zones. These problems also tend to have different expected amounts of each feature when planning units are managed differently. So let us consider a slightly more complex example. Similar to before we will have two management zones. But this time, the cost of managing each planning unit is different depending on which management zone it is assigned to in the solution. Furthermore, when we assign a planning unit to the second zone, we only expect to end up with half of the habitat we would get if we managed the unit in the first zone (e.g., because the second zone is a partial-take zone and the first zone is a no-take zone). We will use the same target data as in the previous example.
     
     ```{r, fig.width = 4.0}
     # create new planning unit and cost data
    @@ -159,7 +160,7 @@ plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
     
     ### Multiple zones with complex targets
     
    -So far, we have dealt with problems where each feature has a target that pertains to a single zone. But sometimes we have targets that pertain to multiple zones. For example, what if we were in charge of managing pest control in a set of areas and we had three different pest control methods we could implement in any given planning unit. We could (1) set up a few traps in a given planning unit and make 10 % of the habitat in the unit pest-free, (2) set up a lot of traps and make 50 % of the habitat in the unit pest-free, or (3) drop baits over a given planning unit and make 80 % of the planning unit pest-free. Each of these different actions has a different cost, with a few low intensity trapping costing $100 per planning unit, a high intensity trapping costing $300, and baiting costing $200 (please note these costs aren't meant to be realistic). After defining our management actions and costs, we require a solution that will yield 8 units of pest free habitat per feature. It's important to note that unlike the previous examples, here we don't have targets for each feature in each zone, but rather our targets are for each feature and across multiple zones. In other words, we don't really care which management actions we implement, we just want the set of actions that will meet our targets for minimum expenditure. We can formulate and solve this problem using the following code.
    +So far, we have dealt with problems where each feature has a target that pertains to a single zone. But sometimes we have targets that pertain to multiple zones. For example, what if we were in charge of managing pest control in a set of areas and we had three different pest control methods we could implement in any given planning unit. We could (i) set up a few traps in a given planning unit and make 10 % of the habitat in the unit pest-free, (ii) set up a lot of traps and make 50 % of the habitat in the unit pest-free, or (iii) drop baits over a given planning unit and make 80 % of the planning unit pest-free. Each of these different actions has a different cost, with a few low intensity trapping costing $100 per planning unit, a high intensity trapping costing $300, and baiting costing $200 (please note these costs aren't meant to be realistic). After defining our management actions and costs, we require a solution that will yield 8 units of pest free habitat per feature. It's important to note that unlike the previous examples, here we don't have targets for each feature in each zone, but rather our targets are for each feature and across multiple zones. In other words, we don't really care which management actions we implement, we just want the set of actions that will meet our targets for minimum expenditure. We can formulate and solve this problem using the following code.
     
     ```{r, fig.width = 4.5, fig.height = 3}
     # create planning unit data with costs
    @@ -318,6 +319,6 @@ plot(category_layer(s7), main = "solution", axes = FALSE, box = FALSE)
     
     ## Conclusion
     
    -Hopefully, this vignette has provided an informative introduction to building and solving problems with multiple zones using the _prioritizr R_ package. Although we have examined only a few different functions here, almost every single function for modifying conservation planning problems is compatible with problems that contain zones. It's worth noting that working with multiple zones is a lot trickier than working with a single zone, so we would recommend playing around with the code in the Examples sections of the help pages to get a feel for what different functions do when they are applied to problems with multiple zones.
    +Hopefully, this vignette has provided an informative introduction to building and solving problems with multiple zones. Although we have examined only a few different functions here, almost every single function for modifying conservation planning problems is compatible with problems that contain zones. It's worth noting that working with multiple zones is a lot trickier than working with a single zone, so we would recommend playing around with the code in the _Examples_ sections of the package documentation to help understand how functions work when applied to multiple zones.
     
     ## References
    diff --git a/inst/doc/zones.html b/inst/doc/management_zones_tutorial.html
    similarity index 88%
    rename from inst/doc/zones.html
    rename to inst/doc/management_zones_tutorial.html
    index ee267c7be..2e9134293 100644
    --- a/inst/doc/zones.html
    +++ b/inst/doc/management_zones_tutorial.html
    @@ -12,7 +12,7 @@
     
     
     
    -Management Zones
    +Management zones tutorial
     
     
     
    @@ -159,7 +159,7 @@
     
     
     
    -

    Management Zones

    +

    Management zones tutorial

    @@ -182,31 +182,30 @@

    Management Zones

    Introduction

    -

    One of the main aims in conservation planning is to identify the most cost-effective set of areas to manage biodiversity (Margules & Pressey 2000). To achieve this, prioritizations are generally created to identify areas for expanding protected area systems. However, many real-world conservation problems do not simply involve deciding if an area should be protected or not (e.g. Klein et al. 2009; Stigner et al. 2016). Instead, many real-world problems involve a range of different management categories and the goal is to determine which areas should be allocated to which management category. For example, a manager might have a range of different methods (e.g. baiting or trapping at various intensities) for controlling invasive pests in a set of different areas (e.g. Cattarino et al. 2018). They would need a prioritization that shows which control methods should be implemented in which areas. In this particular case, a binary prioritization showing which areas contain the most biodiversity is simply not helpful. Furthermore, many real-world problems require decisions that meet multiple, and sometimes conflicting, objectives from different stakeholders. For example, a manager might need to implement a set of no-take and partial-take areas to prevent overfishing, but also ensure that there still remain plenty of areas for fishing activities (e.g. Wilson et al. 2010; Klein et al. 2013). Popularized by Marxan with Zones (Watts et al. 2009), this concept has become known as “zones” and is becoming increasingly important in conservation planning.

    -

    The aim of this vignette is to showcase the zones functionality of the prioritizr R package. It will assume a certain level of familiarity with conservation planning terminology and the prioritizr R package. We recommend reading the prioritizr vignette first if you don’t have much experience in either of these topics.

    +

    One of the main aims in conservation planning is to identify the most cost-effective set of areas to manage biodiversity (Margules & Pressey 2000). To achieve this, prioritizations are generally created to identify areas for expanding protected area systems. However, many real-world conservation problems do not simply involve deciding if an area should be protected or not (e.g., Klein et al. 2009; Stigner et al. 2016). Instead, many problems involve a range of different management categories and the goal is to determine which areas should be allocated to which management category. For example, a manager might have a range of different methods (e.g., baiting or trapping at various intensities) for controlling invasive pests in a set of different areas (e.g., Cattarino et al. 2018). They would need a prioritization that shows which control methods should be implemented in which areas. In this particular case, a binary prioritization showing which areas contain the most biodiversity is simply not helpful. Furthermore, many real-world problems require decisions that meet multiple, and sometimes conflicting, objectives from different stakeholders. For example, a manager might need to implement a set of no-take and partial-take areas to prevent overfishing, but also ensure that there still remain plenty of areas for fishing activities (e.g., Wilson et al. 2010; Klein et al. 2013). Popularized by the Marxan with Zones decision support tool (Watts et al. 2009), this concept has become known as “zones” and is becoming increasingly important in conservation planning.

    +

    The aim of this tutorial is to showcase the zones functionality of the prioritizr R package. It will assume a certain level of familiarity with conservation planning terminology and the package. If you don’t have much experience in either of these topics, we recommend first reading the Package overview vignette.

    Usage

    Simple minimum set problem

    -

    In the prioritizr R package, all conservation planning problems—including those which contain multiple management zones or actions—are initialized using the problem function. To refresh our memory on how we can construct problems using the prioritizr R package, let us quickly construct a simple conservation planning problem. This problem will use the simulated built-in planning unit and feature data distributed with the package. It will have a minimum set objective, targets which require that solution secure to 10 % of the habitat in the study area for each feature, and binary decision variables indicating that planning units are selected or not selected for protection.

    +

    In the prioritizr R package, all conservation planning problems – including those which contain multiple management zones or actions – are initialized using the problem function. To refresh our memory on how we can construct problems, let us quickly construct a simple conservation planning problem. This problem will use the simulated built-in planning unit and feature data distributed with the package. It will have a minimum set objective, targets which require that solution secure to 10 % of the habitat in the study area for each feature, and binary decision variables indicating that planning units are selected or not selected for protection.

    # load prioritizr package
    -library(prioritizr)
    -
    -# load data
    -data(sim_pu_raster, sim_features)
    -
    -# create targets for each of the five features
    -t1 <- rep(0.1, 5)
    -
    -# build single-zone problem
    -p1 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(t1) %>%
    -      add_binary_decisions()
    -
    -# print problem
    -print(p1)
    +library(prioritizr)
    +
    # load data
    +data(sim_pu_raster, sim_features)
    +
    +# create targets for each of the five features
    +t1 <- rep(0.1, 5)
    +
    +# build single-zone problem
    +p1 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(t1) %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p1)
    ## Conservation Problem
     ##   planning units: RasterLayer (90 units)
     ##   cost:           min: 190.13276, max: 215.86384
    @@ -218,8 +217,8 @@ 

    Simple minimum set problem

    ## penalties: <none> ## portfolio: default ## solver: default
    -
    # solve problem
    -s1 <- solve(p1)
    +
    # solve problem
    +s1 <- solve(p1)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
     ## Optimize a model with 5 rows, 90 columns and 450 nonzeros
    @@ -253,9 +252,9 @@ 

    Simple minimum set problem

    ## ## Optimal solution found (tolerance 1.00e-01) ## Best objective 1.987398526526e+03, best bound 1.931581908865e+03, gap 2.8085%
    -
    # calculate feature representation
    -r1 <- eval_feature_representation_summary(p1, s1)
    -print(r1)
    +
    # calculate feature representation
    +r1 <- eval_feature_representation_summary(p1, s1)
    +print(r1)
    ## # A tibble: 5 × 5
     ##   summary feature total_amount absolute_held relative_held
     ##   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    @@ -264,48 +263,48 @@ 

    Simple minimum set problem

    ## 3 overall layer.3 72.0 7.34 0.102 ## 4 overall layer.4 42.7 4.35 0.102 ## 5 overall layer.5 56.7 6.01 0.106
    -
    # plot solution
    -plot(s1, main = "solution", axes = FALSE, box = FALSE)
    +
    # plot solution
    +plot(s1, main = "solution", axes = FALSE, box = FALSE)

    Adding management zones

    -

    Now let us imagine that instead of having a single management zone (e.g. protected area), we have two management zones. Similar to the example above, we require a solution that secures 10 % of the habitat in the study area for each feature in the first management zone. But we also require a solution that secures 5 % of the habitat in the study area for each feature in the second management zone. Each planning unit must be allocated to either zone or not selected for management at all. In this example, each planning unit costs the same when it is allocated to either of the two zones. We can formulate and solve this problem using the following code.

    -
    # create a matrix with the targets
    -# here each column corresponds to a different zone,
    -# each row corresponds to a different feature, and
    -# each cell value corresponds to the target
    -t2 <- matrix(NA, ncol = 2, nrow = 5)
    -t2[, 1] <- 0.1
    -t2[, 2] <- 0.05
    -
    -# print targets
    -print(t2)
    +

    Now let us imagine that instead of having a single management zone (e.g., protected area), we have two management zones. Similar to the example above, we require a solution that secures 10 % of the habitat in the study area for each feature in the first management zone. But we also require a solution that secures 5 % of the habitat in the study area for each feature in the second management zone. Each planning unit must be allocated to either zone or not selected for management at all. In this example, each planning unit costs the same when it is allocated to either of the two zones. We can formulate and solve this problem using the following code.

    +
    # create a matrix with the targets
    +# here each column corresponds to a different zone,
    +# each row corresponds to a different feature, and
    +# each cell value corresponds to the target
    +t2 <- matrix(NA, ncol = 2, nrow = 5)
    +t2[, 1] <- 0.1
    +t2[, 2] <- 0.05
    +
    +# print targets
    +print(t2)
    ##      [,1] [,2]
     ## [1,]  0.1 0.05
     ## [2,]  0.1 0.05
     ## [3,]  0.1 0.05
     ## [4,]  0.1 0.05
     ## [5,]  0.1 0.05
    -
    # create a zones object that contains the amount of each feature
    -# in each planning unit when it is allocated to each zone
    -# since our zones pertain to the same habitat data, we will
    -# to specify the same habitat data for each zone
    -z2 <- zones("zone 1" = sim_features, "zone 2" = sim_features)
    -
    -# print zones
    -print(z2)
    +
    # create a zones object that contains the amount of each feature
    +# in each planning unit when it is allocated to each zone
    +# since our zones pertain to the same habitat data, we will
    +# to specify the same habitat data for each zone
    +z2 <- zones("zone 1" = sim_features, "zone 2" = sim_features)
    +
    +# print zones
    +print(z2)
    ## Zones
     ##   zones: zone 1, zone 2 (2 zones)
     ##   features: 1, 2, 3, ... (5 features)
     ##   data type: RasterStack
    -
    # create a raster stack with the planning unit data
    -# since our planning unit costs are the same for each zone,
    -# we will create a stack with two replicates of the cost data
    -pu2 <- stack(sim_pu_raster, sim_pu_raster)
    -
    -# print stack
    -print(pu2)
    +
    # create a raster stack with the planning unit data
    +# since our planning unit costs are the same for each zone,
    +# we will create a stack with two replicates of the cost data
    +pu2 <- stack(sim_pu_raster, sim_pu_raster)
    +
    +# print stack
    +print(pu2)
    ## class      : RasterStack 
     ## dimensions : 10, 10, 100, 2  (nrow, ncol, ncell, nlayers)
     ## resolution : 0.1, 0.1  (x, y)
    @@ -314,14 +313,14 @@ 

    Adding management zones

    ## names : layer.1, layer.2 ## min values : 190.1328, 190.1328 ## max values : 215.8638, 215.8638
    -
    # build two-zone problem
    -p2 <- problem(pu2, z2) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(t2) %>%
    -      add_binary_decisions()
    -
    -# print problem
    -print(p2)
    +
    # build two-zone problem
    +p2 <- problem(pu2, z2) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(t2) %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p2)
    ## Conservation Problem
     ##   zones:          zone 1, zone 2 (2 zones)
     ##   planning units: RasterStack (90 units)
    @@ -334,8 +333,8 @@ 

    Adding management zones

    ## penalties: <none> ## portfolio: default ## solver: default
    -
    # solve problem
    -s2 <- solve(p2)
    +
    # solve problem
    +s2 <- solve(p2)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
     ## Optimize a model with 100 rows, 180 columns and 1080 nonzeros
    @@ -369,9 +368,9 @@ 

    Adding management zones

    ## ## Optimal solution found (tolerance 1.00e-01) ## Best objective 3.003874397435e+03, best bound 2.911333350606e+03, gap 3.0807%
    -
    # calculate feature representation
    -r2 <- eval_feature_representation_summary(p2, s2)
    -print(r2)
    +
    # calculate feature representation
    +r2 <- eval_feature_representation_summary(p2, s2)
    +print(r2)
    ## # A tibble: 15 × 5
     ##    summary feature total_amount absolute_held relative_held
     ##    <chr>   <chr>          <dbl>         <dbl>         <dbl>
    @@ -390,34 +389,34 @@ 

    Adding management zones

    ## 13 zone 2 3 72.0 3.72 0.0517 ## 14 zone 2 4 42.7 2.17 0.0508 ## 15 zone 2 5 56.7 2.96 0.0521
    -
    # plot solution
    -# here we use the category layer function to generate raster showing the zone
    -# that each planning unit was allocated. Specifically, pixels with the
    -# value 1 are allocated to "zone 1" and pixels with the value 2 are allocated
    -# to "zone 2". Thus units depicted in gray are not allocated
    -# to any zone, units depicted in yellow are allocated to zone 1, and units
    -# depicted in green are allocated to zone 2
    -plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)
    +
    # plot solution
    +# here we use the category layer function to generate raster showing the zone
    +# that each planning unit was allocated. Specifically, pixels with the
    +# value 1 are allocated to "zone 1" and pixels with the value 2 are allocated
    +# to "zone 2". Thus units depicted in gray are not allocated
    +# to any zone, units depicted in yellow are allocated to zone 1, and units
    +# depicted in green are allocated to zone 2
    +plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)

    Multiple zones with varying costs

    -

    Real-world problems often have different costs for managing planning units under different zones. These problems also tend to have different expected amounts of each feature when planning units are managed differently. So let us consider a slightly more complex example. Similar to before we will have two management zones. But this time, the cost of managing each planning unit is different depending on which management zone it is assigned to in the solution. Furthermore, when we assign a planning unit to the second zone, we only expect to end up with half of the habitat we would get if we managed the unit in the first zone (e.g. because the second zone is a partial-take zone and the first zone is a no-take zone). We will use the same target data as in the previous example.

    -
    # create new planning unit and cost data
    -data(sim_pu_zones_stack)
    -pu3 <- sim_pu_zones_stack[[1:2]]
    -
    -# plot cost data
    -plot(pu3, main = c("zone 1", "zone 2"), axes = FALSE, box = FALSE)
    +

    Real-world problems often have different costs for managing planning units under different zones. These problems also tend to have different expected amounts of each feature when planning units are managed differently. So let us consider a slightly more complex example. Similar to before we will have two management zones. But this time, the cost of managing each planning unit is different depending on which management zone it is assigned to in the solution. Furthermore, when we assign a planning unit to the second zone, we only expect to end up with half of the habitat we would get if we managed the unit in the first zone (e.g., because the second zone is a partial-take zone and the first zone is a no-take zone). We will use the same target data as in the previous example.

    +
    # create new planning unit and cost data
    +data(sim_pu_zones_stack)
    +pu3 <- sim_pu_zones_stack[[1:2]]
    +
    +# plot cost data
    +plot(pu3, main = c("zone 1", "zone 2"), axes = FALSE, box = FALSE)

    -
    # create problem
    -p3 <- problem(pu3, zones(sim_features, sim_features * 0.5)) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(t2) %>%
    -      add_binary_decisions()
    -
    -# print problem
    -print(p3)
    +
    # create problem
    +p3 <- problem(pu3, zones(sim_features, sim_features * 0.5)) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(t2) %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p3)
    ## Conservation Problem
     ##   zones:          1, 2 (2 zones)
     ##   planning units: RasterStack (90 units)
    @@ -430,8 +429,8 @@ 

    Multiple zones with varying costs

    ## penalties: <none> ## portfolio: default ## solver: default
    -
    # solve problem
    -s3 <- solve(p3)
    +
    # solve problem
    +s3 <- solve(p3)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
     ## Optimize a model with 100 rows, 180 columns and 1080 nonzeros
    @@ -458,16 +457,16 @@ 

    Multiple zones with varying costs

    ## 0 0 2884.87200 0 8 3388.02489 2884.87200 14.9% - 0s ## H 0 0 2958.1201508 2884.87200 2.48% - 0s ## -## Explored 1 nodes (38 simplex iterations) in 0.00 seconds (0.00 work units) +## Explored 1 nodes (38 simplex iterations) in 0.01 seconds (0.00 work units) ## Thread count was 1 (of 8 available processors) ## ## Solution count 3: 2958.12 3388.02 3667.77 ## ## Optimal solution found (tolerance 1.00e-01) ## Best objective 2.958120150795e+03, best bound 2.884871999973e+03, gap 2.4762%
    -
    # calculate feature representation
    -r3 <- eval_feature_representation_summary(p3, s3)
    -print(r3)
    +
    # calculate feature representation
    +r3 <- eval_feature_representation_summary(p3, s3)
    +print(r3)
    ## # A tibble: 15 × 5
     ##    summary feature total_amount absolute_held relative_held
     ##    <chr>   <chr>          <dbl>         <dbl>         <dbl>
    @@ -486,30 +485,30 @@ 

    Multiple zones with varying costs

    ## 13 2 3 36.0 1.87 0.0519 ## 14 2 4 21.3 1.08 0.0505 ## 15 2 5 28.4 1.44 0.0509
    -
    # plot solution
    -plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)
    +
    # plot solution
    +plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)

    Multiple zones with complex targets

    -

    So far, we have dealt with problems where each feature has a target that pertains to a single zone. But sometimes we have targets that pertain to multiple zones. For example, what if we were in charge of managing pest control in a set of areas and we had three different pest control methods we could implement in any given planning unit. We could (1) set up a few traps in a given planning unit and make 10 % of the habitat in the unit pest-free, (2) set up a lot of traps and make 50 % of the habitat in the unit pest-free, or (3) drop baits over a given planning unit and make 80 % of the planning unit pest-free. Each of these different actions has a different cost, with a few low intensity trapping costing $100 per planning unit, a high intensity trapping costing $300, and baiting costing $200 (please note these costs aren’t meant to be realistic). After defining our management actions and costs, we require a solution that will yield 8 units of pest free habitat per feature. It’s important to note that unlike the previous examples, here we don’t have targets for each feature in each zone, but rather our targets are for each feature and across multiple zones. In other words, we don’t really care which management actions we implement, we just want the set of actions that will meet our targets for minimum expenditure. We can formulate and solve this problem using the following code.

    -
    # create planning unit data with costs
    -pu4 <- stack(Which(!is.na(sim_pu_raster)) * 100,
    -             Which(!is.na(sim_pu_raster)) * 300,
    -             Which(!is.na(sim_pu_raster)) * 200)
    -names(pu4) <- c("few.traps", "many.traps", "baiting")
    -
    -# plot planning unit data
    -plot(pu4, nr = 1, axes = FALSE, box = FALSE)
    -

    -
    # create targets
    -t4 <- tibble::tibble(feature = names(sim_features),
    -                     zone = list(names(pu4))[rep(1, 5)],
    -                     target = rep(8, 5),
    -                     type = rep("absolute", 5))
    +

    So far, we have dealt with problems where each feature has a target that pertains to a single zone. But sometimes we have targets that pertain to multiple zones. For example, what if we were in charge of managing pest control in a set of areas and we had three different pest control methods we could implement in any given planning unit. We could (i) set up a few traps in a given planning unit and make 10 % of the habitat in the unit pest-free, (ii) set up a lot of traps and make 50 % of the habitat in the unit pest-free, or (iii) drop baits over a given planning unit and make 80 % of the planning unit pest-free. Each of these different actions has a different cost, with a few low intensity trapping costing $100 per planning unit, a high intensity trapping costing $300, and baiting costing $200 (please note these costs aren’t meant to be realistic). After defining our management actions and costs, we require a solution that will yield 8 units of pest free habitat per feature. It’s important to note that unlike the previous examples, here we don’t have targets for each feature in each zone, but rather our targets are for each feature and across multiple zones. In other words, we don’t really care which management actions we implement, we just want the set of actions that will meet our targets for minimum expenditure. We can formulate and solve this problem using the following code.

    +
    # create planning unit data with costs
    +pu4 <- stack(Which(!is.na(sim_pu_raster)) * 100,
    +             Which(!is.na(sim_pu_raster)) * 300,
    +             Which(!is.na(sim_pu_raster)) * 200)
    +names(pu4) <- c("few.traps", "many.traps", "baiting")
     
    -# print targets
    -print(t4)
    +# plot planning unit data +plot(pu4, nr = 1, axes = FALSE, box = FALSE)
    +

    +
    # create targets
    +t4 <- tibble::tibble(feature = names(sim_features),
    +                     zone = list(names(pu4))[rep(1, 5)],
    +                     target = rep(8, 5),
    +                     type = rep("absolute", 5))
    +
    +# print targets
    +print(t4)
    ## # A tibble: 5 × 4
     ##   feature zone      target type    
     ##   <chr>   <list>     <dbl> <chr>   
    @@ -518,17 +517,17 @@ 

    Multiple zones with complex targets

    ## 3 layer.3 <chr [3]> 8 absolute ## 4 layer.4 <chr [3]> 8 absolute ## 5 layer.5 <chr [3]> 8 absolute
    -
    # create problem
    -p4 <- problem(pu4, zones(few.traps = sim_features * 0.1,
    -                         many.traps = sim_features * 0.5,
    -                         baiting = sim_features * 0.8,
    -                         feature_names = names(sim_features))) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(t4) %>%
    -      add_binary_decisions()
    -
    -# print problem
    -print(p4)
    +
    # create problem
    +p4 <- problem(pu4, zones(few.traps = sim_features * 0.1,
    +                         many.traps = sim_features * 0.5,
    +                         baiting = sim_features * 0.8,
    +                         feature_names = names(sim_features))) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(t4) %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p4)
    ## Conservation Problem
     ##   zones:          few.traps, many.traps, baiting (3 zones)
     ##   planning units: RasterStack (100 units)
    @@ -541,8 +540,8 @@ 

    Multiple zones with complex targets

    ## penalties: <none> ## portfolio: default ## solver: default
    -
    # solve problem
    -s4 <- solve(p4)
    +
    # solve problem
    +s4 <- solve(p4)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
     ## Optimize a model with 105 rows, 300 columns and 1800 nonzeros
    @@ -577,9 +576,9 @@ 

    Multiple zones with complex targets

    ## ## Optimal solution found (tolerance 1.00e-01) ## Best objective 3.800000000000e+03, best bound 3.700000000000e+03, gap 2.6316%
    -
    # calculate feature representation
    -r4 <- eval_feature_representation_summary(p4, s4)
    -print(r4)
    +
    # calculate feature representation
    +r4 <- eval_feature_representation_summary(p4, s4)
    +print(r4)
    ## # A tibble: 20 × 5
     ##    summary    feature total_amount absolute_held relative_held
     ##    <chr>      <chr>          <dbl>         <dbl>         <dbl>
    @@ -603,20 +602,20 @@ 

    Multiple zones with complex targets

    ## 18 baiting layer.3 57.6 15.2 0.264 ## 19 baiting layer.4 34.1 11.6 0.340 ## 20 baiting layer.5 45.4 12.7 0.281
    -
    # plot solution
    -plot(category_layer(s4), main = "solution", axes = FALSE, box = FALSE)
    +
    # plot solution
    +plot(category_layer(s4), main = "solution", axes = FALSE, box = FALSE)

    Multiple zones with extra constraints

    So it looks like baiting is the way to go! Except that we might recall that we can’t use baits in most of the planning units because they contain native species that are susceptible to baits. So now we need to specify that which of our planning units cannot be assigned to the third zone (baiting) to obtain a more useful solution.

    -
    # create data.frame to specify that we cannot bait in the first 40 units
    -l5 <- data.frame(pu = seq(1, 80),
    -                 zone = "baiting",
    -                 status = 0)
    -
    -# preview locked data
    -head(l5)
    +
    # create data.frame to specify that we cannot bait in the first 40 units
    +l5 <- data.frame(pu = seq(1, 80),
    +                 zone = "baiting",
    +                 status = 0)
    +
    +# preview locked data
    +head(l5)
    ##   pu    zone status
     ## 1  1 baiting      0
     ## 2  2 baiting      0
    @@ -624,18 +623,18 @@ 

    Multiple zones with extra constraints

    ## 4 4 baiting 0 ## 5 5 baiting 0 ## 6 6 baiting 0
    -
    # create problem
    -p5 <- problem(pu4, zones(few.traps = sim_features * 0.1,
    -                         many.traps = sim_features * 0.5,
    -                         baiting = sim_features * 0.8,
    -                         feature_names = names(sim_features))) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(t4) %>%
    -      add_manual_locked_constraints(l5) %>%
    -      add_binary_decisions()
    -
    -# print problem
    -print(p5)
    +
    # create problem
    +p5 <- problem(pu4, zones(few.traps = sim_features * 0.1,
    +                         many.traps = sim_features * 0.5,
    +                         baiting = sim_features * 0.8,
    +                         feature_names = names(sim_features))) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(t4) %>%
    +      add_manual_locked_constraints(l5) %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p5)
    ## Conservation Problem
     ##   zones:          few.traps, many.traps, baiting (3 zones)
     ##   planning units: RasterStack (100 units)
    @@ -648,8 +647,8 @@ 

    Multiple zones with extra constraints

    ## penalties: <none> ## portfolio: default ## solver: default
    -
    # solve problem
    -s5 <- solve(p5)
    +
    # solve problem
    +s5 <- solve(p5)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
     ## Optimize a model with 105 rows, 300 columns and 1800 nonzeros
    @@ -685,9 +684,9 @@ 

    Multiple zones with extra constraints

    ## ## Optimal solution found (tolerance 1.00e-01) ## Best objective 7.000000000000e+03, best bound 7.000000000000e+03, gap 0.0000%
    -
    # calculate feature representation
    -r5 <- eval_feature_representation_summary(p5, s5)
    -print(r5)
    +
    # calculate feature representation
    +r5 <- eval_feature_representation_summary(p5, s5)
    +print(r5)
    ## # A tibble: 20 × 5
     ##    summary    feature total_amount absolute_held relative_held
     ##    <chr>      <chr>          <dbl>         <dbl>         <dbl>
    @@ -711,40 +710,40 @@ 

    Multiple zones with extra constraints

    ## 18 baiting layer.3 57.6 11.7 0.203 ## 19 baiting layer.4 34.1 6.05 0.177 ## 20 baiting layer.5 45.4 9.98 0.220
    -
    # plot solution
    -plot(category_layer(s5), main = "solution", axes = FALSE, box = FALSE)
    +
    # plot solution
    +plot(category_layer(s5), main = "solution", axes = FALSE, box = FALSE)

    Multiple zones with fragmentation penalties

    So now the best strategy seems to be a combination of high intensity trapping and baiting. But we can also see that this solution is fairly fragmented, so we can add penalties to cluster managed planning units together. Here we will add penalties that will cluster the planning units allocated to the two trapping zones together, and penalties that will cluster the planning units allocated to the baiting zone together. We will also set an overall penalty factor to 640 to strongly penalize fragmented solutions.

    -
    # create matrix that describe boundary penalties between planning units
    -# allocated to different zones
    -z6 <- diag(3)
    -z6[1, 2] <- 1
    -z6[2, 1] <- 1
    -colnames(z6) <- c("few.traps", "many.traps", "baiting")
    -rownames(z6) <- colnames(z6)
    -
    -# print matrix
    -print(z6)
    +
    # create matrix that describe boundary penalties between planning units
    +# allocated to different zones
    +z6 <- diag(3)
    +z6[1, 2] <- 1
    +z6[2, 1] <- 1
    +colnames(z6) <- c("few.traps", "many.traps", "baiting")
    +rownames(z6) <- colnames(z6)
    +
    +# print matrix
    +print(z6)
    ##            few.traps many.traps baiting
     ## few.traps          1          1       0
     ## many.traps         1          1       0
     ## baiting            0          0       1
    -
    # create problem
    -p6 <- problem(pu4, zones("few.traps" = sim_features * 0.1,
    -                         "many.traps" = sim_features * 0.5,
    -                         "baiting" = sim_features * 0.8,
    -                         feature_names = names(sim_features))) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(t4) %>%
    -      add_manual_locked_constraints(l5) %>%
    -      add_boundary_penalties(penalty = 640, zones = z6) %>%
    -      add_binary_decisions()
    -
    -# print problem
    -print(p6)
    +
    # create problem
    +p6 <- problem(pu4, zones("few.traps" = sim_features * 0.1,
    +                         "many.traps" = sim_features * 0.5,
    +                         "baiting" = sim_features * 0.8,
    +                         feature_names = names(sim_features))) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(t4) %>%
    +      add_manual_locked_constraints(l5) %>%
    +      add_boundary_penalties(penalty = 640, zones = z6) %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p6)
    ## Conservation Problem
     ##   zones:          few.traps, many.traps, baiting (3 zones)
     ##   planning units: RasterStack (100 units)
    @@ -757,8 +756,8 @@ 

    Multiple zones with fragmentation penalties

    ## penalties: <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (640), zones]> ## portfolio: default ## solver: default
    -
    # solve problem
    -s6 <- solve(p6)
    +
    # solve problem
    +s6 <- solve(p6)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
     ## Optimize a model with 1905 rows, 1200 columns and 5400 nonzeros
    @@ -771,7 +770,7 @@ 

    Multiple zones with fragmentation penalties

    ## RHS range [1e+00, 8e+00] ## Found heuristic solution: objective 20096.000000 ## Presolve removed 1028 rows and 232 columns -## Presolve time: 0.01s +## Presolve time: 0.02s ## Presolved: 877 rows, 968 columns, 2712 nonzeros ## Variable types: 0 continuous, 968 integer (968 binary) ## Found heuristic solution: objective 19632.000000 @@ -794,9 +793,9 @@

    Multiple zones with fragmentation penalties

    ## ## Optimal solution found (tolerance 1.00e-01) ## Best objective 1.058800000000e+04, best bound 1.005600000000e+04, gap 5.0246%
    -
    # calculate feature representation
    -r6 <- eval_feature_representation_summary(p6, s6)
    -print(r6)
    +
    # calculate feature representation
    +r6 <- eval_feature_representation_summary(p6, s6)
    +print(r6)
    ## # A tibble: 20 × 5
     ##    summary    feature total_amount absolute_held relative_held
     ##    <chr>      <chr>          <dbl>         <dbl>         <dbl>
    @@ -820,29 +819,29 @@ 

    Multiple zones with fragmentation penalties

    ## 18 baiting layer.3 57.6 11.7 0.203 ## 19 baiting layer.4 34.1 6.05 0.177 ## 20 baiting layer.5 45.4 9.98 0.220
    -
    # plot solution
    -plot(category_layer(s6), main = "solution", axes = FALSE, box = FALSE)
    +
    # plot solution
    +plot(category_layer(s6), main = "solution", axes = FALSE, box = FALSE)

    Finally, it appears we might have a viable solution for this made-up conservation problem.

    Multiple zones with fragmentation penalties and mandatory allocations

    Finally, we might be interested in conservation planning scenarios where every single planning unit must be allocated to a management zone. This is often the case when developing land-use plans where every single planning unit needs to be allocated to a specific management zone. Though it makes less sense here, let’s see what happens to the solution if we needed to do at least one form of control in every single planning unit.

    -
    # create matrix that describe boundary penalties between planning units
    -# allocated to different zones
    -p7 <- problem(pu4, zones("few.traps" = sim_features * 0.1,
    -                         "many.traps" = sim_features * 0.5,
    -                         "baiting" = sim_features * 0.8,
    -                         feature_names = names(sim_features))) %>%
    -      add_min_set_objective() %>%
    -      add_manual_targets(t4) %>%
    -      add_mandatory_allocation_constraints() %>%
    -      add_manual_locked_constraints(l5) %>%
    -      add_boundary_penalties(penalty = 640, zones = z6) %>%
    -      add_binary_decisions()
    -
    -# print problem
    -print(p7)
    +
    # create matrix that describe boundary penalties between planning units
    +# allocated to different zones
    +p7 <- problem(pu4, zones("few.traps" = sim_features * 0.1,
    +                         "many.traps" = sim_features * 0.5,
    +                         "baiting" = sim_features * 0.8,
    +                         feature_names = names(sim_features))) %>%
    +      add_min_set_objective() %>%
    +      add_manual_targets(t4) %>%
    +      add_mandatory_allocation_constraints() %>%
    +      add_manual_locked_constraints(l5) %>%
    +      add_boundary_penalties(penalty = 640, zones = z6) %>%
    +      add_binary_decisions()
    +
    +# print problem
    +print(p7)
    ## Conservation Problem
     ##   zones:          few.traps, many.traps, baiting (3 zones)
     ##   planning units: RasterStack (100 units)
    @@ -856,8 +855,8 @@ 

    Multiple zones with fragmentation penalties and mandatory allocations

    ## penalties: <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (640), zones]> ## portfolio: default ## solver: default
    -
    # solve problem
    -s7 <- solve(p7)
    +
    # solve problem
    +s7 <- solve(p7)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
     ## Optimize a model with 1905 rows, 1200 columns and 5400 nonzeros
    @@ -892,9 +891,9 @@ 

    Multiple zones with fragmentation penalties and mandatory allocations

    ## ## Optimal solution found (tolerance 1.00e-01) ## Best objective 1.326000000000e+04, best bound 1.323200000000e+04, gap 0.2112%
    -
    # calculate feature representation
    -r7 <- eval_feature_representation_summary(p7, s7)
    -print(r7)
    +
    # calculate feature representation
    +r7 <- eval_feature_representation_summary(p7, s7)
    +print(r7)
    ## # A tibble: 20 × 5
     ##    summary    feature total_amount absolute_held relative_held
     ##    <chr>      <chr>          <dbl>         <dbl>         <dbl>
    @@ -918,14 +917,14 @@ 

    Multiple zones with fragmentation penalties and mandatory allocations

    ## 18 baiting layer.3 57.6 11.7 0.203 ## 19 baiting layer.4 34.1 6.05 0.177 ## 20 baiting layer.5 45.4 9.98 0.220
    -
    # plot solution
    -plot(category_layer(s7), main = "solution", axes = FALSE, box = FALSE)
    +
    # plot solution
    +plot(category_layer(s7), main = "solution", axes = FALSE, box = FALSE)

    Conclusion

    -

    Hopefully, this vignette has provided an informative introduction to building and solving problems with multiple zones using the prioritizr R package. Although we have examined only a few different functions here, almost every single function for modifying conservation planning problems is compatible with problems that contain zones. It’s worth noting that working with multiple zones is a lot trickier than working with a single zone, so we would recommend playing around with the code in the Examples sections of the help pages to get a feel for what different functions do when they are applied to problems with multiple zones.

    +

    Hopefully, this vignette has provided an informative introduction to building and solving problems with multiple zones. Although we have examined only a few different functions here, almost every single function for modifying conservation planning problems is compatible with problems that contain zones. It’s worth noting that working with multiple zones is a lot trickier than working with a single zone, so we would recommend playing around with the code in the Examples sections of the package documentation to help understand how functions work when applied to multiple zones.

    References

    diff --git a/inst/doc/package_overview.Rmd b/inst/doc/package_overview.Rmd new file mode 100644 index 000000000..eb1d5ddf3 --- /dev/null +++ b/inst/doc/package_overview.Rmd @@ -0,0 +1,905 @@ +--- +title: "Package overview" +output: + rmarkdown::html_vignette: + toc: true + fig_caption: true + self_contained: yes +fontsize: 11pt +documentclass: article +bibliography: references.bib +csl: reference-style.csl +vignette: > + %\VignetteIndexEntry{Package overview} + %\VignetteEngine{knitr::rmarkdown_notangle} +--- + +```{r, include = FALSE} +h = 3.5 +w = 3.5 +is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", + "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) +knitr::opts_chunk$set(fig.align = "center", eval = !is_check, + root.dir = normalizePath("../..")) +``` + +## Summary + +The _prioritizr R_ package uses integer linear programming (ILP) techniques to provide a flexible interface for building and solving conservation planning problems [@r11; @r16]. It supports a broad range of objectives, constraints, and penalties that can be used to custom-tailor conservation planning problems to the specific needs of a conservation planning exercise. Once built, conservation planning problems can be solved using a variety of commercial and open-source exact algorithm solvers. In contrast to the algorithms conventionally used to solve conservation problems, such as heuristics or simulated annealing [@r3], the exact algorithms used here are guaranteed to find optimal solutions. Furthermore, conservation problems can be constructed to optimize the spatial allocation of different management zone (or actions), meaning that conservation practitioners can identify solutions that benefit multiple stakeholders. Finally, this package has the functionality to read input data formatted for the _Marxan_ conservation planning program [@r3], and find much cheaper solutions in a much shorter period of time than _Marxan_ [@r1]. + +## Introduction + +Systematic conservation planning is a rigorous, repeatable, and structured approach to designing new protected areas that efficiently meet conservation objectives [@r4]. Historically, conservation decision-making has often evaluated parcels opportunistically as they became available for purchase, donation, or under threat. Although purchasing such areas may improve the _status quo_, such decisions may not substantially enhance the long-term persistence of target species or communities. Faced with this realization, conservation planners began using decision support tools to help simulate alternative reserve designs over a range of different biodiversity and management goals and, ultimately, guide protected area acquisitions and management actions. Due to the systematic, evidence-based nature of these tools, conservation prioritization can help contribute to a transparent, inclusive, and more defensible decision making process. + +A conservation planning exercise typically starts by defining a study area. This study area should encompass all the areas relevant to the decision maker or the hypothesis being tested. The extent of a study area could encompass a few important localities [e.g., @r18], a single state [e.g., @r17], an entire country [@r19], or the entire planet [@r20]. Next, the study area is divided into a set of discrete areas termed _planning units_. Each planning unit represents a discrete locality in the study area that can be managed independently of other areas. The general idea is that some combination of the planning units can be selected for conservation actions (e.g., protected area establishment, habitat restoration). Planning units are often created as square or hexagon cells that are sized according to the scale of the conservation actions, and the resolution of the data that underpin the planning exercise [but see @r5]. + +Cost data (or a surrogate thereof) are needed to inform the prioritization process. Specifically, these cost data describe the relative expenditure associated with managing each planning unit for conservation. For example, if the goal of the conservation planning exercise is to identify priority areas for expanding a local protected area system, then the cost data could represent the physical cost of purchasing the land. Alternatively, if such data are not available, then surrogate data could are often used instead (e.g., human population density, opportunity cost of foregone commercial activities, or planning unit size). + +Conservation planning exercises also require data on the biodiversity elements that are of conservation interest (termed _conservation features_). These features could be species (e.g., _Neofelis nebulosa_, the Clouded Leopard), populations, or habitats (e.g., mangroves or cloud forest). After identifying the set of relevant conservation features for a conservation planning exercise, spatially explicit data need to be obtained for each and every feature to describe their spatial distribution (e.g., habitat suitability data, probability of occurrence data, presence/absence data). This is important to ensure that conservation features are adequately covered (represented) by prioritizations. After assembling all the data, the next step is to define the conservation planning problem. + +The _prioritizr R_ package is designed to help you build and solve conservation planning problems. Specifically, prioritizations are generated by using formulating a mathematical optimization problem and then solving it to generate a solution. These mathematical optimization problems are formulated using the planning unit data, cost data, and feature data, and with information related to the overarching aim of the prioritization process. In general, the goal of an optimization problem is to minimize (or maximize) an _objective function_ that is calculated using a set of _decision variables_, subject to a series of constraints to ensure that solution exhibits specific properties. The objective function describes the quantity which we are trying to minimize (e.g., cost of the solution) or maximize (e.g., number of features conserved). The decision variables describe the entities that we can control, and indicate which planning units are selected for conservation management and which of those are not. The constraints can be thought of as rules that the need decision variables need to follow. For example, a commonly used constraint is specifying that the solution must not exceed a certain budget. + +A wide variety of approaches have been developed for solving optimization problems. Reserve design problems are frequently solved using simulated annealing [@r6] or heuristics [@r8; @r7]. These methods are conceptually simple and can be applied to a wide variety of optimization problems. However, they do not scale well for large or complex problems [@r1]. Additionally, these methods cannot tell you how close any given solution is to the optimal solution. The package uses exact algorithms to efficiently solve conservation planning problems to within a pre-specified a optimality gap. In other words, you can specify that you need the optimal solution (i.e., a gap of 0%) and the algorithms will, given enough time, find a solution that meets this criteria. In the past, exact algorithms have been too slow for conservation planning exercises [@r9]. However, improvements over the last decade mean that they are now much faster [@r23; @r1]. + +In this package, optimization problems are expressed using _integer linear programming_ (ILP) so that they can be solved using (linear) exact algorithm solvers. The general form of an integer programming problem can be expressed in matrix notation using the following equation. + +$$\text{Minimize} \space \boldsymbol{c}^\text{T} \boldsymbol{x} \space +\space \text{subject to} \space A\boldsymbol{x} +\space \Box \space \boldsymbol{b}$$ + +Here, where $x$ is a vector of decision variables, $c$ and $b$ are vectors of known coefficients, and $A$ is the constraint matrix. The final term specifies a series of structural constants and the $\Box$ symbol is used to indicate that the relational operators for the constraints can be either $\geq$, $=$, or $\leq$. In the context of a conservation planning problem, $c$ could be used to represent the planning unit costs, $A$ could be used to store the data showing the presence / absence (or amount) of each feature in each planning unit, $b$ could be used to represent minimum amount of habitat required for each species in the solution, the $\Box$ could be set to $\geq$ symbols to indicate that the total amount of each feature in the solution must exceed the quantities in $b$. But there are many other ways of formulating the reserve selection problem [@r11]. + +## A grammar for conservation planning + +The _prioritizr R_ package uses a grammar to describe elements of conservation planning. This means that functions are organized into verbs that relate to specific concepts. For example, all of the functions used to specify the primary objective for optimization end with the `_objective` suffix (e.g., `add_min_set_objective()` and `add_min_shortfall_objective()`). By combining multiple functions together, they can be used to formulate a complete conservation planning problem. Specifically, the verbs for formulating problems are described below. + +* Create a new conservation planning [problem](https://prioritizr.net/reference/problem.html) by specifying the planning units, features, and management zones of conservation interest. +* Add a primary [objective](https://prioritizr.net/reference/objectives.html) to a conservation planning problem (e.g., minimize overall cost). +* Add [penalties](https://prioritizr.net/reference/penalties.html) to a conservation planning problem to penalize solutions according to specific metric (e.g., connectivity). +* Add [targets](https://prioritizr.net/reference/targets.html) to a conservation planning problem to specify how much of each feature should ideally be represented solutions. +* Add [constraints](https://prioritizr.net/reference/constraints.html) to a conservation planning problem to ensure that solutions exhibit specific properties (e.g., select specific planning units for protection). +* Add [decisions](https://prioritizr.net/reference/decisions.html) to a conservation planning problem to specify the nature of the decisions in the problem (e.g., binary decisions indicate the planning units should be selected or not selected for management). +* Add a [portfolio](https://prioritizr.net/reference/portfolios.html) to a conservation planning problem to specify a methodological approach for generating multiple solutions (e.g. generate multiple solutions by finding 100 solutions within 10% of optimality). +* Add a [solver](https://prioritizr.net/reference/solvers.html) to a conservation problem to specify the optimization software (e.g. Gurobi) for generating solutions and customize the optimization settings (e.g. generate an optimal solution). + +After building a conservation planning problem, it can be solved to generate a prioritization (using the `solve()` function). There are also verbs available to help evaluate and interpret solutions. These verbs are described below. + +* Evaluate performance by computing [summary statistics](https://prioritizr.net/reference/summaries.html) (e.g. overall cost, feature representation, or total boundary length). +* Evaluate the relative [importance](https://prioritizr.net/reference/importance.html) of planning units selected by a solution (e.g. based on irreplaceability). + +## Workflow + +The general workflow when using the _prioritizr R_ package starts with creating a new conservation planning `problem()` object using data. Specifically, the `problem()` object should be constructed using data that specify the planning units, biodiversity features, management zones (if applicable), and costs. After creating a new `problem()` object, it can be customized---by adding objectives, penalties, constraints, and other information---to build a precise representation of the conservation planning problem required, and then solved to obtain a solutions. + +All conservation planning problems require an objective. An objective specifies the property which is used to compare different feasible solutions. Simply put, the objective is the property of the solution which should be maximized or minimized during the optimization process. For instance, with the minimum set objective (specified using `add_min_set_objective()`), we are seeking to minimize the cost of the solution (similar to _Marxan_). On the other hand, with the minimum shortfall objective (specified using `add_min_shortfall_objective()`), we are seeking to minimize the average target shortfall for all features represented in the solution, subject to a budget. + +Many objectives require representation targets (e.g., the minimum set objective). These targets are a specialized set of constraints that relate to the total quantity of each feature secured in the solution (e.g., amount of suitable habitat or number of individuals). In the case of the minimum set objective ( `add_min_set_objective()`), they are used to ensure that solutions secure a sufficient quantity of each feature, and in other objectives, such as the maximum features objective ( `add_max_features_objective()`) they are used to assess whether a feature has been adequately conserved by a candidate solution. Targets can be expressed numerically as the total amount required for a given feature (using `add_absolute_targets()`), or as a proportion of the total amount found in the planning units (using `add_relative_targets()`). Note that not all objectives require targets, and a warning will be thrown if an attempt is made to add targets to a problem with an objective that does not use them. + +Constraints and penalties can be added to a conservation planning problem to ensure that solutions exhibit a specific property or penalize solutions which don't exhibit a specific property (respectively). The difference between constraints and penalties, strictly speaking, is constraints are used to rule out potential solutions that don't exhibit a specific property. For instance, constraints can be used to ensure that specific planning units are selected in the solution for prioritization (using `add_locked_in_constraints()`) or not selected in the solution for prioritization (using `add_locked_out_constraints()`). On the other hand, penalties are combined with the objective of a problem, with a penalty factor, and the overall objective of the problem then becomes to minimize (or maximize) the primary objective function and the penalty function. For example, penalties can be added to a problem to penalize solutions that are excessively fragmented (using `add_boundary_penalties()`). These penalties have a `penalty` argument that specifies the relative importance of having spatially clustered solutions. When the argument to `penalty` is high, then solutions which are less fragmented are valued more highly -- even if they cost more -- and when the argument to `penalty` is low, then the solutions which are more fragmented are valued less highly. + +After building a conservation problem, it can then be solved to obtain a solution (or portfolio of solutions if desired). The solution is returned in the same format as the planning unit data used to construct the problem. For instance, this means that if raster data was used to initialize the problem, then the solution will also be output in raster format. This can be very helpful when it comes to interpreting and visualizing solutions because it means that the solution data does not first have to be merged with spatial data before they can be plotted on a map. + +## Usage + +Here we will provide an introduction to using the _prioritizr R_ package to build and solve a conservation planning problem. Please note that we will not discuss conservation planning with multiple zones in this vignette, for more information on working with multiple management zones please see the [zones vignette](zones.html). + +First, we will load the _prioritizr_ package. + +```{r, results = "hide", message = FALSE} +# load package +library(prioritizr) + +# set default options for printing tabular data +options(tibble.width = Inf) +``` + +### Data + +Now we will load some built-in data sets that are distributed with the _prioritizr R_ package. This package contains several different planning unit data sets. To provide a comprehensive overview of the different ways that we can initialize a conservation planning problem, we will load each of them. + +First, we will load the raster planning unit data (`sim_pu_raster`). Here, the planning units are represented as a raster (i.e., a `RasterLayer` object) and each pixel corresponds to the spatial extent of each panning unit. The pixel values correspond to the acquisition costs of each planning unit. + +```{r} +# load raster planning unit data +data(sim_pu_raster) + +# print description of the data +print(sim_pu_raster) + +# plot the data +plot(sim_pu_raster) +``` + +Secondly, we will load one of the spatial vector planning unit data sets (`sim_pu_polygons`). Here, each polygon (i.e., feature using ArcGIS terminology) corresponds to a different planning unit. This data set has an attribute table that contains additional information about each polygon. Namely, the `cost` field (column) in the attribute table contains the acquisition cost for each planning unit. + +```{r} +# load polygon planning unit data +data(sim_pu_polygons) + +# print first six rows of attribute table +head(sim_pu_polygons@data) + +# plot the planning units +spplot(sim_pu_polygons, zcol = "cost") +``` + +Thirdly, we will load some planning unit data stored in tabular format (i.e., `data.frame` format). For those familiar with _Marxan_ or dealing with very large conservation planning problems (> 10 million planning units), it may be useful to work with data in this format because it does not contain any spatial information which will reduce computational burden. When using tabular data to initialize conservation planning problems, the data must follow the conventions used by _Marxan_. Specifically, each row in the planning unit table must correspond to a different planning unit. The table must also have an "id" column to provide a unique integer identifier for each planning unit, and it must also have a column that indicates the cost of each planning unit. For more information, please see the [official _Marxan_ documentation](https://marxansolutions.org/). + +```{r} +# specify file path for planning unit data +pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr") + +# load in the tabular planning unit data +# note that we use the data.table::fread function, as opposed to the read.csv +# function, because it is much faster +pu_dat <- data.table::fread(pu_path, data.table = FALSE) + +# preview first six rows of the tabular planning unit data +# note that it has some extra columns other than id and cost as per the +# Marxan format +head(pu_dat) +``` + +Finally, we will load data showing the spatial distribution of the conservation features. Our conservation features (`sim_features`) are represented as a stack of raster objects (i.e., a `RasterStack` object) where each layer corresponds to a different feature (e.g., a multi-band GeoTIFF where each band corresponds to a different feature). The pixel values in each layer correspond to the amount of suitable habitat available in a given planning unit. Note that our planning unit raster layer and our conservation feature stack have exactly the same spatial properties (i.e., resolution, extent, coordinate reference system) so their pixels line up perfectly. + +```{r, fig.width = 4, fig.height = 3} +# load feature data +data(sim_features) + +# plot the distribution of suitable habitat for each feature +plot(sim_features, main = paste("Feature", seq_len(nlayers(sim_features))), + nr = 2, box = FALSE, axes = FALSE) +``` + +### Initialize a problem + +After having loaded our planning unit and feature data, we will now try initializing the some conservation planning problems. There are a lot of different ways to initialize a conservation planning problem, so here we will just showcase a few of the more commonly used methods. For an exhaustive description of all the ways you can initialize a conservation problem, see the help file for the `problem()` function. First off, we will initialize a conservation planning problem using the raster data. + +```{r} +# create problem +p1 <- problem(sim_pu_raster, sim_features) + +# print problem +print(p1) + +# print number of planning units +number_of_planning_units(p1) + +# print number of features +number_of_features(p1) +``` + +Generally, we recommend initializing problems using raster data where possible. This is because the `problem()` function needs to calculate the amount of each feature in each planning unit, and by providing both the planning unit and feature data in raster format with the same spatial resolution, extents, and coordinate systems, this means that the `problem()` function does not need to do any geoprocessing behind the scenes. But sometimes we can't use raster planning unit data because our planning units aren't equal-sized grid cells. So, below is an example showing how we can initialize a conservation planning problem using planning units that are formatted as spatial vector data. Note that if we had precomputed the amount of each feature in each planning unit and stored the data in the attribute table, we could pass in the names of the columns as an argument to the `problem()` function. + +```{r} +# create problem with spatial vector data +# note that we have to specify which column in the attribute table contains +# the cost data +p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") + +# print problem +print(p2) +``` + +We can also initialize a conservation planning problem using tabular planning unit data (i.e., `data.frame` format). Since the tabular planning unit data does not contain any spatial information, we also have to provide the feature data in tabular format (i.e., `data.frame` format) and data showing the amount of each feature in each planning unit in tabular format (i.e., `data.frame` format). The feature data must have an "id" column containing a unique integer identifier for each feature, and the planning unit by feature data must contain the following three columns: `pu` corresponding to the planning unit identifiers, `species` corresponding to the feature identifiers, and `amount` showing the amount of a given feature in a given planning unit. + +```{r} +# set file path for feature data +spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr") + +# load in feature data +spec_dat <- data.table::fread(spec_path, data.table = FALSE) + +# print first six rows of the data +# note that it contains extra columns +head(spec_dat) + +# set file path for planning unit vs. feature data +puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr") + +# load in planning unit vs feature data +puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE) + +# print first six rows of the data +head(puvspr_dat) + +# create problem +p3 <- problem(pu_dat, spec_dat, cost_column = "cost", rij = puvspr_dat) + +# print problem +print(p3) +``` + +For more information on initializing problems, please see the help page for the `problem()` function (which you can open by entering the code: `?problem`). Now that we have initialized a conservation planning problem, we will show you how you can customize it to suit the exact needs of your conservation planning scenario. Although we initialized the conservation planning problems using several different methods, moving forward, we will only use raster-based planning unit data to keep things simple. + +### Add an objective + +The next step is to add a primary objective to the problem. A problem objective is used to specify the primary goal of the problem (i.e., the quantity that is to be maximized or minimized). All conservation planning problems involve minimizing or maximizing some kind of objective. For instance, we might require a solution that conserves enough habitat for each species while minimizing the overall cost of the reserve network. Alternatively, we might require a solution that maximizes the number of conserved species while ensuring that the cost of the reserve network does not exceed the budget. Please note that objectives are added in the same way regardless of the type of data used to initialize the problem. The following objectives are available. + +* __Minimum set objective__: Minimize the cost of the solution whilst ensuring that all targets are met [@r11]. This objective is similar to that used in _Marxan_ [@r3]. For example, we can add a minimum set objective to a problem using the following code. +```{r} +# create a new problem that has the minimum set objective +p3 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() + +# print the problem +print(p3) +``` +* __Maximum cover objective__: Represent at least one instance of as many features as possible within a given budget [@r12]. +```{r} +# create a new problem that has the maximum coverage objective and a budget +# of 5000 +p4 <- problem(sim_pu_raster, sim_features) %>% + add_max_cover_objective(5000) + +# print the problem +print(p4) +``` +* __Maximum features objective__: Fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget [inspired by @r10]. This object is similar to the maximum cover objective except that we have the option of later specifying targets for each feature. In practice, this objective is more useful than the maximum cover objective because features often require a certain amount of area for them to persist and simply capturing a single instance of habitat for each feature is generally unlikely to enhance their long-term persistence. +```{r} +# create a new problem that has the maximum features objective and a budget +# of 5000 +p5 <- problem(sim_pu_raster, sim_features) %>% + add_max_features_objective(budget = 5000) + +# print the problem +print(p5) +``` +* __Minimum shortfall objective__: Minimize the overall (weighted sum) shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when there is a large amount of left-over budget when using the maximum feature representation objective and the remaining funds need to be allocated to places that will enhance the representation of features with unmet targets. +```{r} +# create a new problem that has the minimum shortfall objective and a budget +# of 5000 +p6 <- problem(sim_pu_raster, sim_features) %>% + add_min_shortfall_objective(budget = 5000) + +# print the problem +print(p6) +``` +* __Minimum largest shortfall objective__: Minimize the largest (maximum) shortfall while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when the minimum shortfall objective returns solutions that focus too much on representing a small number of features (e.g., because they occur in much cheaper planning units), and solutions are needed to spread conservation effort out more evenly among all features---even if it means that all features will have (relatively) poor representation. +```{r} +# create a new problem that has the minimum largest shortfall objective and a +# budget of 5000 +p7 <- problem(sim_pu_raster, sim_features) %>% + add_min_largest_shortfall_objective(budget = 5000) + +# print the problem +print(p7) +``` +* __Maximum phylogenetic diversity objective__: Maximize the phylogenetic diversity of the features represented in the solution subject to a budget [inspired by @r13; @r14]. This objective is similar to the maximum features objective except that emphasis is placed on protecting features which are associated with a diverse range of evolutionary histories. The package contains a simulated phylogeny that can be used with the simulated feature data (`sim_phylogny`). +```{r} +# load simulated phylogeny data +data(sim_phylogeny) + +# create a new problem that has the maximum phylogenetic diversity +# objective and a budget of 5000 +p8 <- problem(sim_pu_raster, sim_features) %>% + add_max_phylo_div_objective(budget = 5000, tree = sim_phylogeny) + +# print the problem +print(p8) +``` +* __Maximum phylogenetic endemism objective__: Maximize the phylogenetic endemism of the features represented in the solution subject to a budget [inspired by @r13; @r14; @r32]. This objective is similar to the maximum phylogenetic diversity except that emphasis is placed conserving features that are associated with geographically restricted periods of evolutionary history rather than a diverse range of evolutionary histories. +```{r} +# load simulated phylogeny data +data(sim_phylogeny) + +# create a new problem that has the maximum phylogenetic diversity +# objective and a budget of 5000 +p9 <- problem(sim_pu_raster, sim_features) %>% + add_max_phylo_end_objective(budget = 5000, tree = sim_phylogeny) + +# print the problem +print(p9) +``` +* __Maximum utility objective__: Secure as much of the features as possible without exceeding a budget. This objective is functionally equivalent to selecting the planning units with the greatest amounts of each feature (e.g., species richness). Generally, we don't encourage the use of this objective because it will only rarely identify complementary solutions--solutions which adequately conserve a range of different features---except perhaps to explore trade-offs or provide a baseline solution with which to compare other solutions. +```{r} +# create a new problem that has the maximum utility objective and a budget +# of 5000 +p10 <- problem(sim_pu_raster, sim_features) %>% + add_max_utility_objective(budget = 5000) + +# print the problem +print(p10) +``` + +### Add targets + +Most conservation planning problems require targets. Targets are used to specify the minimum amount or proportion of a feature's distribution that needs to be protected in the solution. For example, we may want to develop a reserve network that will secure 20% of the distribution for each feature for minimal cost. The following methods are available for specifying targets. + +* __Absolute targets__: Targets are expressed as the total amount of each feature in the study area that need to be secured. For example, if we had binary feature data that showed the absence or presence of suitable habitat across the study area, we could set an absolute target as 5 to mean that we require 5 planning units with suitable habitat in the solution. +```{r} +# create a problem with targets which specify that the solution must conserve +# a need a sum total of 3 units of suitable habitat for each feature +p11 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_absolute_targets(3) + +# print problem +print(p11) +``` +* __Relative targets__: Targets are set as a proportion (between 0 and 1) of the total amount of each feature in the study area. For example, if we had binary feature data and the feature occupied a total of 20 planning units in the study area, we could set a relative target of 50 % to specify that the solution must secure 10 planning units for the feature. We could alternatively specify an absolute target of 10 to achieve the same result, but sometimes proportions are easier to work with. +```{r} +# create a problem with the minimum set objective and relative targets of 10 % +# for each feature +p12 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) + +# print problem +print(p12) + +# create a problem with targets which specify that we need 10 % of the habitat +# for the first feature, 15 % for the second feature, 20 % for the third feature +# 25 % for the fourth feature and 30 % of the habitat for the fifth feature +targets <- c(0.1, 0.15, 0.2, 0.25, 0.3) +p13 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(targets) + +# print problem +print(p13) +``` +* __Log-linear targets__: Targets are expressed using scaling factors and log-linear interpolation. This method for specifying targets is commonly used for global prioritization analyses [@r15]. +```{r} +# create problem with added log-linear targets +p14 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_loglinear_targets(10, 0.9, 100, 0.2) + +# print problem +print(p14) +``` +* __Manual targets__: Targets are manually specified. This is only really recommended for advanced users or problems that involve multiple management zones. See the [zones vignette](zones.html) for more information on these targets. + +As with the functions for specifying the objective of a problem, if we try adding multiple targets to a problem, only the most recently added set of targets are used. + +### Add constraints + +A constraint can be added to a conservation planning problem to ensure that all solutions exhibit a specific property. For example, they can be used to make sure that all solutions select a specific planning unit or that all selected planning units in the solution follow a certain configuration. The following constraints are available. + +* __Locked in constraints__: Add constraints to ensure that certain planning units are prioritized in the solution. For example, it may be desirable to lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network. +```{r} +# create problem with constraints which specify that the first planning unit +# must be selected in the solution +p15 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints(1) + +# print problem +print(p15) +``` +* __Locked out constraints__: Add constraints to ensure that certain planning units are not prioritized in the solution. For example, it may be useful to lock out planning units that have been degraded and are not suitable for conserving species. +```{r} +# create problem with constraints which specify that the second planning unit +# must not be selected in the solution +p16 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_out_constraints(2) + +# print problem +print(p16) +``` +* __Neighbor constraints__: Add constraints to a conservation problem to ensure that all selected planning units have at least a certain number of neighbors. +```{r} +# create problem with constraints which specify that all selected planning units +# in the solution must have at least 1 neighbor +p17 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_neighbor_constraints(1) + +# print problem +print(p17) +``` +* __Contiguity constraints__: Add constraints to a conservation problem to ensure that all selected planning units are spatially connected to each other and form spatially contiguous unit. +```{r} +# create problem with constraints which specify that all selected planning units +# in the solution must form a single contiguous unit +p18 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_contiguity_constraints() + +# print problem +print(p18) +``` +* __Feature contiguity constraints__: Add constraints to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of those implemented in the `add_contiguity_constraints` function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit. +```{r} +# create problem with constraints which specify that the planning units used +# to conserve each feature must form a contiguous unit +p19 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_feature_contiguity_constraints() + +# print problem +print(p19) +``` + +* __Linear constraints__: Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study region (e.g., different countries). + +```{r} +# create problem with constraints which specify that the sum of +# values in sim_features[[1]] among selected planning units must not exceed a +#' threshold value of 190. +p20 <- problem(sim_pu_raster, sim_features) %>% + add_min_shortfall_objective(budget = 1800) %>% + add_relative_targets(0.1) %>% + add_linear_constraints(190, "<=", sim_features[[1]]) + +# print problem +print(p20) +``` + +* __Mandatory allocation constraints__: Add constraints to ensure that every planning unit is allocated to a management zone in the solution. Please note that this function can only be used with problems that contain multiple zones. For more information on problems with multiple zones and an example using this function, see the Management Zones vignette. + +In particular, The `add_locked_in_constraints` and `add_locked_out_constraints` functions are incredibly useful for real-world conservation planning exercises, so it's worth pointing out that there are several ways we can specify which planning units should be locked in or out of the solutions. If we use raster planning unit data, we can also use raster data to specify which planning units should be locked in our locked out. + +```{r, fig.width = 6.5, fig.height = 2.5} +# load data to lock in or lock out planning units +data(sim_locked_in_raster) +data(sim_locked_out_raster) + +# plot the locked data +plot(stack(sim_locked_in_raster, sim_locked_out_raster), + main = c("Locked In", "Locked Out")) + +# create a problem using raster planning unit data and use the locked raster +# data to lock in some planning units and lock out some other planning units +p21 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints(sim_locked_in_raster) %>% + add_locked_out_constraints(sim_locked_out_raster) + +# print problem +print(p21) +``` + +If our planning unit data are in a spatial vector format (similar to the `sim_pu_polygons` data) or a tabular format (similar to `pu_dat`), we can use the field names in the data to refer to which planning units should be locked in and / or out. For example, the `sim_pu_polygons` object has `TRUE` / `FALSE` values in the "locked_in" field which indicate which planning units should be selected in the solution. We could use the data in this field to specify that those planning units with `TRUE` values should be locked in using the following methods. + +```{r} +# preview first six rows of the attribute table for sim_pu_polygons +head(sim_pu_polygons@data) + +# specify locked in data using the field name +p22 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints("locked_in") + +# print problem +print(p22) + +# specify locked in data using the values in the field +p23 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints(which(sim_pu_polygons$locked_in)) + +# print problem +print(p23) +``` + +### Add penalties + +We can also add penalties to a problem to favor or penalize solutions according to a secondary objective. Unlike the constraint functions, these functions will add extra information to the objective function of the optimization function to penalize solutions that do not exhibit specific characteristics. For example, penalties can be added to a problem to avoid highly fragmented solutions at the expense of accepting slightly more expensive solutions. All penalty functions have a `penalty` argument that controls the relative importance of the secondary penalty function compared to the primary objective function. It is worth noting that incredibly low or incredibly high `penalty` values -- relative to the main objective function -- can cause problems to take a very long time to solve, so when trying out a range of different penalty values it can be helpful to limit the solver to run for a set period of time. The following penalties are available. + +* __Boundary penalties__: Add penalties to penalize solutions that are excessively fragmented. These penalties are similar to those used in _Marxan_ [@r3; @r1]. +```{r} +# create problem with penalties that penalize fragmented solutions with a +# penalty factor of 0.01 +p24 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_boundary_penalties(penalty = 0.01) + +# print problem +print(p24) +``` +* __Connectivity penalties__: Add penalties to favor solutions that select combinations of planning units with high connectivity between them. These penalties are similar to those used in _Marxan with Zones_ [@r2; @r1]. This function supports both symmetric and asymmetric connectivities among planning units. +```{r} +# create problem with penalties that favor combinations of planning units with +# high connectivity, here we will use only the first four layers in +# sim_features for the features and we will use the fifth layer in sim_features +# to represent the connectivity data, where the connectivity_matrix function +# will create a matrix showing the average strength of connectivity between +# adjacent planning units using the data in the fifth layer of sim_features +p25 <- problem(sim_pu_raster, sim_features[[1:4]]) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_boundary_penalties( + penalty = 5, + data = connectivity_matrix(sim_pu_raster, sim_features[[5]])) + +# print problem +print(p25) +``` +* __Linear penalties__: Add penalties to penalize solutions that select planning units according to a certain variable (e.g., anthropogenic pressure). +```{r} +# create data for penalizing planning units +# (note this requires the RandomFields package to be installed) +pen_raster <- simulate_cost(sim_pu_raster) + +# create problem with penalties that penalize solutions that select +# planning units with high values in the pen_raster object, +# here we will use a penalty value of 5 to indicate the trade-off (scaling) +# between the penalty values (in the sim_pu_raster) and the main objective +# (i.e., the cost of the solution) +p26 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_linear_penalties(penalty = 5, data = pen_raster) + +# print problem +print(p26) +``` + +### Add the decision types + +Conservation planning problems involve making decisions on how planning units will be managed. These decisions are then associated with management actions (e.g., turning a planning unit into a protected area). The type of decision describes how the action is applied to planning units. For instance, the default decision-type is a binary decision type, meaning that we are either selecting or not selecting planning units for management. The following decision types are available. + +* __Binary decisions__: Add a binary decision to a conservation planning problem. This is the classic decision of either prioritizing or not prioritizing a planning unit. Typically, this decision has the assumed action of buying the planning unit to include in a protected area network. If no decision is added to a problem object, then this decision class will be used by default. +```{r} +# add binary decisions to a problem +p27 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() + +# print problem +print(p27) +``` +* __Proportion decisions__: Add a proportion decision to a problem. This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the default of the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in a protected area network. Generally, problems can be solved much faster with proportion-type decisions than binary-type decisions, so they can be very useful when commercial solvers are not available. +```{r} +# add proportion decisions to a problem +p28 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_proportion_decisions() + +# print problem +print(p28) +``` +* __Semi-continuous decisions__: Add a semi-continuous decision to a problem. This decision is similar to proportion decisions except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction (e.g., 80%) of a planning unit can be purchased. This type of decision may be useful when it is not practical to conserve the entire area indicated by a planning unit. +```{r} +# add semi-continuous decisions to a problem, where we can only manage at most +# 50 % of the area encompassed by a planning unit +p29 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_semicontinuous_decisions(0.5) + +# print problem +print(p29) +``` + +### Add a solver + +Next, after specifying the mathematical formulation that underpins your conservation planning problem, you can specify how the problem should be solved. If you do not specify this information, the _prioritizr R_ package will automatically use the best solver currently installed on your system with some reasonable defaults. __We strongly recommend installing the [Gurobi software suite and the _gurobi_ _R_ package](https://www.gurobi.com/) to solve problems, and for more information on this topic please refer to the [Gurobi Installation Guide](gurobi_installation.html)__. The following solvers are available. + +* __*Gurobi* solver__: [_Gurobi_](https://www.gurobi.com/) is a state of the art commercial optimization software. It is by far the fastest of the solvers that can be used to solve conservation problems. However, it is not freely available. That said, special licenses are available to academics at no cost. +```{r} +# create a problem and specify that Gurobi should be used to solve the problem +# and specify an optimality gap of zero to obtain the optimal solution +p30 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_gurobi_solver(gap = 0) + +# print problem +print(p30) +``` +* __*IBM CPLEX* solver__: [_IBM CPLEX_](https://www.ibm.com/analytics/cplex-optimizer) is a commercial optimization software. It is faster than the open source solvers available for generating prioritizations (see below), however, it is not freely available. Similar to the _Gurobi_ software, special licenses are available to academics at no cost. +```{r} +# create a problem and specify that IBM CPLEX should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p31 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_cplex_solver(gap = 0) + +# print problem +print(p31) +``` +* __*CBC* solver__: [*CBC*](https://projects.coin-or.org/Cbc) is an +open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks have yet to be completed, preliminary analyses suggest that it is +the fastest open-source solver available for generating prioritizations. It requires the _rcbc R_ package, which is currently only available on [GitHub](https://github.com/dirkschumacher/rcbc). +```{r} +# create a problem and specify that CBC should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p32 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_cbc_solver(gap = 0) + +# print problem +print(p32) +``` +* __*lpsymphony* solver__: [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) is an open-source integer programming solver that is also part of the COIN-OR project. This solver uses the _lpsymphony R_ package (available on [Bioconductor](http://bioconductor.org/packages/release/bioc/html/lpsymphony.html)) to interface with the _SYMPHONY_ software. +```{r} +# create a problem and specify that lpsymphony should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p33 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_lpsymphony_solver(gap = 0) + +# print problem +print(p33) +``` +* __*Rsymphony* solver__: This solver provides a different interface to the [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) software. It uses the _Rsymphony R_ package which is available on The Comprehensive R Archive Network (CRAN). This solver is generally slower than the other solvers, because it cannot use parallel processing. +```{r} +# create a problem and specify that Rsymphony should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p34 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_rsymphony_solver(gap = 0) + +# print problem +print(p34) +``` + +### Add a portfolio + +Many conservation planning exercises require a portfolio of solutions. For example, real-world exercises can involve presenting decision makers with a range of near-optimal decisions. Additionally, the number of times that different planning units are selected in different solutions can provide insight into their relative importance. The following portfolio methods are available. + +* __Gap portfolio__: Generate a portfolio of solutions by finding a certain number of solutions that are all within a pre-specified optimality gap. This method is especially useful for generating multiple solutions that can used be to calculate selection frequencies (similar to _Marxan_). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. +```{r} +# create a problem and specify that a portfolio should be created by +# finding five solutions within 10% of optimality +p35 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) + +# print problem +print(p35) +``` +* __Top portfolio__: Generate a portfolio of solutions by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. +```{r} +# create a problem and specify that a portfolio should be created using +# the top five solutions +p36 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_top_portfolio(number_solutions = 5) + +# print problem +print(p36) +``` +* __Extra portfolio__: Generate a portfolio of solutions by storing feasible solutions found during the optimization process. Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. +```{r} +# create a problem and specify that a portfolio should be created using +# extra solutions found while solving the problem +p37 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_extra_portfolio() + +# print problem +print(p37) +``` +* __Cuts portfolio__: Generate a portfolio of distinct solutions within a pre-specified optimality gap. This method is only recommended if the _Gurobi_ optimization solver is not available. +```{r} +# create a problem and specify that a portfolio containing 10 solutions +# should be created using using Bender's cuts +p38 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_cuts_portfolio(number_solutions = 10) + +# print problem +print(p38) +``` +* __Shuffle portfolio__: Generate a portfolio of solutions by randomly reordering the data prior to attempting to solve the problem. If the _Gurobi_ optimization solver is not available, this method is the fastest method for generating a set number of solutions within a specified distance from optimality. +```{r} +# create a problem and specify a portfolio should be created that contains +# 10 solutions and that any duplicate solutions should not be removed +p39 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_shuffle_portfolio(number_solutions = 10, remove_duplicates = FALSE) + +# print problem +print(p39) +``` + +### Solve the problem + +After formulating our conservation planning problem and specifying how the problem should be solved, we can use the `solve` function to obtain a solution. Note that the solver will typically print out some information describing the size of the problem and report its progress when searching for a suitable solution. + +```{r, fig.height = h, fig.width = w} +# formulate the problem +p40 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_boundary_penalties(penalty = 500, edge_factor = 0.5) %>% + add_binary_decisions() + +# solve the problem (using the default solver) +s40 <- solve(p40) + +# plot solution +plot(s40, col = c("grey90", "darkgreen"), main = "Solution", + xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) +``` + +We can plot this solution because the planning unit input data are spatially referenced in a raster format. The output format will always match the planning unit data used to initialize the problem. For example, the solution to a problem with planning units in a spatial vector (shapefile) format would also be in a spatial vector format. Similarly, if the planning units were in a tabular format (i.e., `data.frame`), the solution would also be returned in a tabular format. + +We can also extract attributes from the solution that describe the quality of the solution and the optimization process. + +```{r} +# extract the objective (numerical value being minimized or maximized) +print(attr(s40, "objective")) + +# extract time spent solving solution +print(attr(s40, "runtime")) + +# extract state message from the solver that describes why this specific +# solution was returned +print(attr(s40, "status")) +``` + +## Evaluate the solution + +Conservation planning involves making trade-offs between different criteria (e.g. overall cost, feature representation, connectivity). After obtaining a solution to a conservation planning problem, it is important to evaluate it to help understand the trade-offs made by the prioritization. This is also useful to compare different solutions with each other. + +### Evaluating performance + +Summary statistics can be computed to evaluate the overall performance of a solution based on certain criteria. The following summaries can be computed. + +The following functions are available to summarize a solution: + +* __Number summary__: Calculate the number of planning units selected by a solution. +```{r} +# calculate statistic +eval_n_summary(p40, s40) +``` +* __Cost summary__: Calculate the total cost of a solution. +```{r} +# calculate statistic +eval_cost_summary(p40, s40) +``` +* __Feature representation summary__: Calculate how well features are represented by a solution. This function can be used for any type of problem. +```{r} +# calculate statistics +eval_feature_representation_summary(p40, s40) +``` +* __Target coverage summary__: Calculate how well representation targets are met by a solution. This function can only be used with problems contain targets. +```{r} +# calculate statistics +eval_target_coverage_summary(p40, s40) +``` +* __Boundary summary__: Calculate the total exposed boundary length (perimeter) associated with a solution. +```{r} +# calculate statistic +eval_boundary_summary(p40, s40) +``` +* __Connectivity summary__: Calculate the connectivity facilitated within a solution. +```{r} +# calculate statistic +# here we use the raster data for the first feature as an example +# to parametrize pair-wise connectivity between different planning units +eval_connectivity_summary( + p40, s40, data = connectivity_matrix(sim_pu_raster, sim_features[[1]])) +``` + +### Evaluating relative importance + +Conservation plans can take a long time to implement. Since funding availability and habitat quality can decline over time, it is critical that the most important places in a prioritization are scheduled for management as early as possible. For instance, some planning units in a solution might contain many rare species which do not occur in any other planning units. Alternatively, some planning units might offer an especially high return on investment that reduces costs considerably. As a consequence, conservation planners often need information on which planning units selected in a prioritization are most important to the overall success of the prioritization. To achieve this, conservation planners can use importance scores for each planning unit selected by a solution. + +Let's generate a prioritization so that can compare the different importance methods. + +```{r, fig.height = h, fig.width = w} +# formulate the problem +p41 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() + +# solve the problem +s41 <- solve(p41) + +# plot solution +plot(s41, col = c("grey90", "darkgreen"), main = "Solution", + xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) +``` + + The following methods are available for computing importance scores. + +* __Replacement cost__: Evaluate importance using the replacement cost method [@r31]. This method quantifies the importance of a given planning unit as the decrease in the performance of the solution (based on the objective function) if the planning unit cannot be acquired (e.g., in terms of the additional costs required to meet feature targets). The advantages of this method are that it (i) accounts for the costs of the planning units, (ii) can be applied to multiple management zones, (iii) can be applied to any objective function, and (iv) can identify truly irreplaceable planning units (denoted with infinite values). However, the key disadvantage of this method, is that can take an infeasible amount of time to complete for large and complex problems. +```{r, fig.height = h, fig.width = w} +# calculate replacement cost scores and make the solver quiet +rc41 <- p41 %>% + add_default_solver(gap = 0, verbose = FALSE) %>% + eval_replacement_importance(s41) + +# plot replacement cost scores +plot(rc41, main = "Replacement cost scores") +``` +* __Ferrier method__: Evaluate importance by computing irreplaceability scores following Ferrier *et al.* [@r-34]. The advantages of this method are that it (i) can be computed relatively quickly for moderate and large-sized problems, and (ii) calculates a score for each feature within each planning unit to provide insight into why certain planning units are more important than others. The disadvatange with this method is that it can only be applied to conservation problems with a minimum set objective and a single zone (i.e., similar to _Marxan_-type problems). +```{r, fig.height = h, fig.width = w} +# calculate Ferrier scores and extract total score +fs41 <- eval_ferrier_importance(p40, s40)[["total"]] + +# plot Ferrier scores +plot(fs41, main = "Ferrier scores") +``` +* __Rarity weighted richness__: Evaluate importance by computing rarity weighted richness scores [@r33]. The only advantage with this method is that it can be computed very quickly for very large problems. The key disadvantage with this approach is that it merely describes the spatial patterns of biodiversity, and does not consider any of the goals that underpin conservation planning exercise. For instance, it does not account for planning costs, management zones, objective functions, or feature representation targets. +```{r, fig.height = h, fig.width = w} +# calculate rarity weighted richness scores +rwr41 <- eval_rare_richness_importance(p40, s40) + +# plot rarity weighted richness scores +plot(rwr41, main = "Rarity weighted richness") +``` + +In general, we recommend using replacement cost scores for small and moderate sized problems (e.g., less than 30,000 planning units) when it is feasible to do so. It can take a very long time to compute replacement cost scores, and so it is simply not feasible to compute these scores for particularly large problems. For moderate and large sized problems (e.g., more than 30,000 planning units), we recommend using the Ferrier method. This is because it explicitly accounts for representation targets, unlike the rarity weighted richness scores. We almost never recommend using the rarity weighted richness scores. This is because they do not consider criteria needed to inform conservation decision making [@r51]. + +## _Marxan_ problems + +Although we encourage users to build and tailor conservation planning problems to suit their own needs, sometimes it just simply easier to use something you're already familiar with. The `marxan_problem()` function is provided as a convenient wrapper for building and solving _Marxan_-style conservation problems. If users already have their conservation planning data formatted for use with _Marxan_, this function can also read _Marxan_ data files and solve the _Marxan_-style problems using exact algorithm solvers. __Please note that problems built using the `marxan_problem()` function are still solved the same way as a problem initialized using the `problem()` function, and therefore still require the installation of one of the solver packages.__ + +Here is a short example showing how the `marxan_problem()` function can be used to read _Marxan_ input files and the `solve` function can be used to solve the problem. + +```{r, results = "hide"} +# set file path for Marxan input file +minput <- system.file("extdata/input.dat", package = "prioritizr") + +# read Marxan input file +mp <- marxan_problem(minput) + +# print problem +print(mp) + +# solve the problem +ms <- solve(mp) + +# since the Marxan data was in a tabular format, the solution is also returned +# in a tabular format, so we will print the first six rows of the table +# containing the solution +head(ms) +``` + +Alternatively, rather then using a _Marxan_ input file to construct the problem, we can manually read in the _Marxan_ data files and input these to the `marxan_problem()` function. + +```{r} +# load data +pu <- system.file("extdata/input/pu.dat", package = "prioritizr") %>% + read.table(sep = ",", header = TRUE) +features <- system.file("extdata/input/spec.dat", package = "prioritizr") %>% + read.table(sep = ",", header = TRUE) +bound <- system.file("extdata/input/bound.dat", package = "prioritizr") %>% + read.table(sep = "\t", header = TRUE) +rij <- system.file("extdata/input/puvspr.dat", package = "prioritizr") %>% + read.table(sep = ",", header = TRUE) + +# build Marxan problem using data.frame objects +mp2 <- marxan_problem(x = pu, spec = features, puvspr = rij, bound = bound, + blm = 0) + +# print problem +print(mp2) +``` + +Conservation planning problems that are built using the `marxan_problem()` function can also be customized. For example, we could change the decision type for `mp2` to involve selecting a proportion of each planing unit (using the `add_proportion_decisions()` function). + +## Conclusion + +Hopefully, this vignette has provided an informative overview of the _prioritizr R_ package. For more examples using the package, please see the other vignettes. Perhaps, one of the best ways to learn a new piece of software is to just try it out. Test it, try breaking it, make mistakes, and learn from them. We would recommend trying to build conservation planning problems that resemble those you face in your own work---but using the built-in example data sets. This way you can quickly verify that the problems you build actually mean what you think they mean. For instance, you can try playing around with the targets and see what effect they have on the solutions, or try playing around with penalties and see what effect they have on the solutions. Finally, if you have any questions about using the package or suggestions for it, please [post an issue on the package's online coding repository](https://github.com/prioritizr/prioritizr/issues). + +## References diff --git a/inst/doc/package_overview.html b/inst/doc/package_overview.html new file mode 100644 index 000000000..68e2990e2 --- /dev/null +++ b/inst/doc/package_overview.html @@ -0,0 +1,1704 @@ + + + + + + + + + + + + + + +Package overview + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Package overview

    + + + + +
    +

    Summary

    +

    The prioritizr R package uses integer linear programming (ILP) techniques to provide a flexible interface for building and solving conservation planning problems (Rodrigues et al. 2000; Billionnet 2013). It supports a broad range of objectives, constraints, and penalties that can be used to custom-tailor conservation planning problems to the specific needs of a conservation planning exercise. Once built, conservation planning problems can be solved using a variety of commercial and open-source exact algorithm solvers. In contrast to the algorithms conventionally used to solve conservation problems, such as heuristics or simulated annealing (Ball et al. 2009), the exact algorithms used here are guaranteed to find optimal solutions. Furthermore, conservation problems can be constructed to optimize the spatial allocation of different management zone (or actions), meaning that conservation practitioners can identify solutions that benefit multiple stakeholders. Finally, this package has the functionality to read input data formatted for the Marxan conservation planning program (Ball et al. 2009), and find much cheaper solutions in a much shorter period of time than Marxan (Beyer et al. 2016).

    +
    +
    +

    Introduction

    +

    Systematic conservation planning is a rigorous, repeatable, and structured approach to designing new protected areas that efficiently meet conservation objectives (Margules & Pressey 2000). Historically, conservation decision-making has often evaluated parcels opportunistically as they became available for purchase, donation, or under threat. Although purchasing such areas may improve the status quo, such decisions may not substantially enhance the long-term persistence of target species or communities. Faced with this realization, conservation planners began using decision support tools to help simulate alternative reserve designs over a range of different biodiversity and management goals and, ultimately, guide protected area acquisitions and management actions. Due to the systematic, evidence-based nature of these tools, conservation prioritization can help contribute to a transparent, inclusive, and more defensible decision making process.

    +

    A conservation planning exercise typically starts by defining a study area. This study area should encompass all the areas relevant to the decision maker or the hypothesis being tested. The extent of a study area could encompass a few important localities (e.g., Stigner et al. 2016), a single state (e.g., Kirkpatrick 1983), an entire country (Fuller et al. 2010), or the entire planet (Butchart et al. 2015). Next, the study area is divided into a set of discrete areas termed planning units. Each planning unit represents a discrete locality in the study area that can be managed independently of other areas. The general idea is that some combination of the planning units can be selected for conservation actions (e.g., protected area establishment, habitat restoration). Planning units are often created as square or hexagon cells that are sized according to the scale of the conservation actions, and the resolution of the data that underpin the planning exercise (but see Klein et al. 2009).

    +

    Cost data (or a surrogate thereof) are needed to inform the prioritization process. Specifically, these cost data describe the relative expenditure associated with managing each planning unit for conservation. For example, if the goal of the conservation planning exercise is to identify priority areas for expanding a local protected area system, then the cost data could represent the physical cost of purchasing the land. Alternatively, if such data are not available, then surrogate data could are often used instead (e.g., human population density, opportunity cost of foregone commercial activities, or planning unit size).

    +

    Conservation planning exercises also require data on the biodiversity elements that are of conservation interest (termed conservation features). These features could be species (e.g., Neofelis nebulosa, the Clouded Leopard), populations, or habitats (e.g., mangroves or cloud forest). After identifying the set of relevant conservation features for a conservation planning exercise, spatially explicit data need to be obtained for each and every feature to describe their spatial distribution (e.g., habitat suitability data, probability of occurrence data, presence/absence data). This is important to ensure that conservation features are adequately covered (represented) by prioritizations. After assembling all the data, the next step is to define the conservation planning problem.

    +

    The prioritizr R package is designed to help you build and solve conservation planning problems. Specifically, prioritizations are generated by using formulating a mathematical optimization problem and then solving it to generate a solution. These mathematical optimization problems are formulated using the planning unit data, cost data, and feature data, and with information related to the overarching aim of the prioritization process. In general, the goal of an optimization problem is to minimize (or maximize) an objective function that is calculated using a set of decision variables, subject to a series of constraints to ensure that solution exhibits specific properties. The objective function describes the quantity which we are trying to minimize (e.g., cost of the solution) or maximize (e.g., number of features conserved). The decision variables describe the entities that we can control, and indicate which planning units are selected for conservation management and which of those are not. The constraints can be thought of as rules that the need decision variables need to follow. For example, a commonly used constraint is specifying that the solution must not exceed a certain budget.

    +

    A wide variety of approaches have been developed for solving optimization problems. Reserve design problems are frequently solved using simulated annealing (Kirkpatrick et al. 1983) or heuristics (Nicholls & Margules 1993; Moilanen 2007). These methods are conceptually simple and can be applied to a wide variety of optimization problems. However, they do not scale well for large or complex problems (Beyer et al. 2016). Additionally, these methods cannot tell you how close any given solution is to the optimal solution. The package uses exact algorithms to efficiently solve conservation planning problems to within a pre-specified a optimality gap. In other words, you can specify that you need the optimal solution (i.e., a gap of 0%) and the algorithms will, given enough time, find a solution that meets this criteria. In the past, exact algorithms have been too slow for conservation planning exercises (Pressey et al. 1996). However, improvements over the last decade mean that they are now much faster (Achterberg & Wunderling 2013; Beyer et al. 2016).

    +

    In this package, optimization problems are expressed using integer linear programming (ILP) so that they can be solved using (linear) exact algorithm solvers. The general form of an integer programming problem can be expressed in matrix notation using the following equation.

    +

    \[\text{Minimize} \space \boldsymbol{c}^\text{T} \boldsymbol{x} \space +\space \text{subject to} \space A\boldsymbol{x} +\space \Box \space \boldsymbol{b}\]

    +

    Here, where \(x\) is a vector of decision variables, \(c\) and \(b\) are vectors of known coefficients, and \(A\) is the constraint matrix. The final term specifies a series of structural constants and the \(\Box\) symbol is used to indicate that the relational operators for the constraints can be either \(\geq\), \(=\), or \(\leq\). In the context of a conservation planning problem, \(c\) could be used to represent the planning unit costs, \(A\) could be used to store the data showing the presence / absence (or amount) of each feature in each planning unit, \(b\) could be used to represent minimum amount of habitat required for each species in the solution, the \(\Box\) could be set to \(\geq\) symbols to indicate that the total amount of each feature in the solution must exceed the quantities in \(b\). But there are many other ways of formulating the reserve selection problem (Rodrigues et al. 2000).

    +
    +
    +

    A grammar for conservation planning

    +

    The prioritizr R package uses a grammar to describe elements of conservation planning. This means that functions are organized into verbs that relate to specific concepts. For example, all of the functions used to specify the primary objective for optimization end with the _objective suffix (e.g., add_min_set_objective() and add_min_shortfall_objective()). By combining multiple functions together, they can be used to formulate a complete conservation planning problem. Specifically, the verbs for formulating problems are described below.

    +
      +
    • Create a new conservation planning problem by specifying the planning units, features, and management zones of conservation interest.
    • +
    • Add a primary objective to a conservation planning problem (e.g., minimize overall cost).
    • +
    • Add penalties to a conservation planning problem to penalize solutions according to specific metric (e.g., connectivity).
    • +
    • Add targets to a conservation planning problem to specify how much of each feature should ideally be represented solutions.
    • +
    • Add constraints to a conservation planning problem to ensure that solutions exhibit specific properties (e.g., select specific planning units for protection).
    • +
    • Add decisions to a conservation planning problem to specify the nature of the decisions in the problem (e.g., binary decisions indicate the planning units should be selected or not selected for management).
    • +
    • Add a portfolio to a conservation planning problem to specify a methodological approach for generating multiple solutions (e.g. generate multiple solutions by finding 100 solutions within 10% of optimality).
    • +
    • Add a solver to a conservation problem to specify the optimization software (e.g. Gurobi) for generating solutions and customize the optimization settings (e.g. generate an optimal solution).
    • +
    +

    After building a conservation planning problem, it can be solved to generate a prioritization (using the solve() function). There are also verbs available to help evaluate and interpret solutions. These verbs are described below.

    +
      +
    • Evaluate performance by computing summary statistics (e.g. overall cost, feature representation, or total boundary length).
    • +
    • Evaluate the relative importance of planning units selected by a solution (e.g. based on irreplaceability).
    • +
    +
    +
    +

    Workflow

    +

    The general workflow when using the prioritizr R package starts with creating a new conservation planning problem() object using data. Specifically, the problem() object should be constructed using data that specify the planning units, biodiversity features, management zones (if applicable), and costs. After creating a new problem() object, it can be customized—by adding objectives, penalties, constraints, and other information—to build a precise representation of the conservation planning problem required, and then solved to obtain a solutions.

    +

    All conservation planning problems require an objective. An objective specifies the property which is used to compare different feasible solutions. Simply put, the objective is the property of the solution which should be maximized or minimized during the optimization process. For instance, with the minimum set objective (specified using add_min_set_objective()), we are seeking to minimize the cost of the solution (similar to Marxan). On the other hand, with the minimum shortfall objective (specified using add_min_shortfall_objective()), we are seeking to minimize the average target shortfall for all features represented in the solution, subject to a budget.

    +

    Many objectives require representation targets (e.g., the minimum set objective). These targets are a specialized set of constraints that relate to the total quantity of each feature secured in the solution (e.g., amount of suitable habitat or number of individuals). In the case of the minimum set objective ( add_min_set_objective()), they are used to ensure that solutions secure a sufficient quantity of each feature, and in other objectives, such as the maximum features objective ( add_max_features_objective()) they are used to assess whether a feature has been adequately conserved by a candidate solution. Targets can be expressed numerically as the total amount required for a given feature (using add_absolute_targets()), or as a proportion of the total amount found in the planning units (using add_relative_targets()). Note that not all objectives require targets, and a warning will be thrown if an attempt is made to add targets to a problem with an objective that does not use them.

    +

    Constraints and penalties can be added to a conservation planning problem to ensure that solutions exhibit a specific property or penalize solutions which don’t exhibit a specific property (respectively). The difference between constraints and penalties, strictly speaking, is constraints are used to rule out potential solutions that don’t exhibit a specific property. For instance, constraints can be used to ensure that specific planning units are selected in the solution for prioritization (using add_locked_in_constraints()) or not selected in the solution for prioritization (using add_locked_out_constraints()). On the other hand, penalties are combined with the objective of a problem, with a penalty factor, and the overall objective of the problem then becomes to minimize (or maximize) the primary objective function and the penalty function. For example, penalties can be added to a problem to penalize solutions that are excessively fragmented (using add_boundary_penalties()). These penalties have a penalty argument that specifies the relative importance of having spatially clustered solutions. When the argument to penalty is high, then solutions which are less fragmented are valued more highly – even if they cost more – and when the argument to penalty is low, then the solutions which are more fragmented are valued less highly.

    +

    After building a conservation problem, it can then be solved to obtain a solution (or portfolio of solutions if desired). The solution is returned in the same format as the planning unit data used to construct the problem. For instance, this means that if raster data was used to initialize the problem, then the solution will also be output in raster format. This can be very helpful when it comes to interpreting and visualizing solutions because it means that the solution data does not first have to be merged with spatial data before they can be plotted on a map.

    +
    +
    +

    Usage

    +

    Here we will provide an introduction to using the prioritizr R package to build and solve a conservation planning problem. Please note that we will not discuss conservation planning with multiple zones in this vignette, for more information on working with multiple management zones please see the zones vignette.

    +

    First, we will load the prioritizr package.

    +
    # load package
    +library(prioritizr)
    +
    +# set default options for printing tabular data
    +options(tibble.width = Inf)
    +
    +

    Data

    +

    Now we will load some built-in data sets that are distributed with the prioritizr R package. This package contains several different planning unit data sets. To provide a comprehensive overview of the different ways that we can initialize a conservation planning problem, we will load each of them.

    +

    First, we will load the raster planning unit data (sim_pu_raster). Here, the planning units are represented as a raster (i.e., a RasterLayer object) and each pixel corresponds to the spatial extent of each panning unit. The pixel values correspond to the acquisition costs of each planning unit.

    +
    # load raster planning unit data
    +data(sim_pu_raster)
    +
    +# print description of the data
    +print(sim_pu_raster)
    +
    ## class      : RasterLayer 
    +## dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +## resolution : 0.1, 0.1  (x, y)
    +## extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +## crs        : NA 
    +## source     : memory
    +## names      : layer 
    +## values     : 190.1328, 215.8638  (min, max)
    +
    # plot the data
    +plot(sim_pu_raster)
    +

    +

    Secondly, we will load one of the spatial vector planning unit data sets (sim_pu_polygons). Here, each polygon (i.e., feature using ArcGIS terminology) corresponds to a different planning unit. This data set has an attribute table that contains additional information about each polygon. Namely, the cost field (column) in the attribute table contains the acquisition cost for each planning unit.

    +
    # load polygon planning unit data
    +data(sim_pu_polygons)
    +
    +# print first six rows of attribute table
    +head(sim_pu_polygons@data)
    +
    ##       cost locked_in locked_out
    +## 1 215.8638     FALSE      FALSE
    +## 2 212.7823     FALSE      FALSE
    +## 3 207.4962     FALSE      FALSE
    +## 4 208.9322     FALSE       TRUE
    +## 5 214.0419     FALSE      FALSE
    +## 6 213.7636     FALSE      FALSE
    +
    # plot the planning units
    +spplot(sim_pu_polygons, zcol = "cost")
    +

    +

    Thirdly, we will load some planning unit data stored in tabular format (i.e., data.frame format). For those familiar with Marxan or dealing with very large conservation planning problems (> 10 million planning units), it may be useful to work with data in this format because it does not contain any spatial information which will reduce computational burden. When using tabular data to initialize conservation planning problems, the data must follow the conventions used by Marxan. Specifically, each row in the planning unit table must correspond to a different planning unit. The table must also have an “id” column to provide a unique integer identifier for each planning unit, and it must also have a column that indicates the cost of each planning unit. For more information, please see the official Marxan documentation.

    +
    # specify file path for planning unit data
    +pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr")
    +
    +# load in the tabular planning unit data
    +# note that we use the data.table::fread function, as opposed to the read.csv
    +# function, because it is much faster
    +pu_dat <- data.table::fread(pu_path, data.table = FALSE)
    +
    +# preview first six rows of the tabular planning unit data
    +# note that it has some extra columns other than id and cost as per the
    +# Marxan format
    +head(pu_dat)
    +
    ##   id       cost status    xloc     yloc
    +## 1  3        0.0      0 1116623 -4493479
    +## 2 30   752727.5      3 1110623 -4496943
    +## 3 56  3734907.5      0 1092623 -4500408
    +## 4 58  1695902.1      0 1116623 -4500408
    +## 5 84  3422025.6      0 1098623 -4503872
    +## 6 85 17890758.4      0 1110623 -4503872
    +

    Finally, we will load data showing the spatial distribution of the conservation features. Our conservation features (sim_features) are represented as a stack of raster objects (i.e., a RasterStack object) where each layer corresponds to a different feature (e.g., a multi-band GeoTIFF where each band corresponds to a different feature). The pixel values in each layer correspond to the amount of suitable habitat available in a given planning unit. Note that our planning unit raster layer and our conservation feature stack have exactly the same spatial properties (i.e., resolution, extent, coordinate reference system) so their pixels line up perfectly.

    +
    # load feature data
    +data(sim_features)
    +
    +# plot the distribution of suitable habitat for each feature
    +plot(sim_features, main = paste("Feature", seq_len(nlayers(sim_features))),
    +     nr = 2, box = FALSE, axes = FALSE)
    +

    +
    +
    +

    Initialize a problem

    +

    After having loaded our planning unit and feature data, we will now try initializing the some conservation planning problems. There are a lot of different ways to initialize a conservation planning problem, so here we will just showcase a few of the more commonly used methods. For an exhaustive description of all the ways you can initialize a conservation problem, see the help file for the problem() function. First off, we will initialize a conservation planning problem using the raster data.

    +
    # create problem
    +p1 <- problem(sim_pu_raster, sim_features)
    +
    +# print problem
    +print(p1)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      none
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
    # print number of planning units
    +number_of_planning_units(p1)
    +
    ## [1] 90
    +
    # print number of features
    +number_of_features(p1)
    +
    ## [1] 5
    +

    Generally, we recommend initializing problems using raster data where possible. This is because the problem() function needs to calculate the amount of each feature in each planning unit, and by providing both the planning unit and feature data in raster format with the same spatial resolution, extents, and coordinate systems, this means that the problem() function does not need to do any geoprocessing behind the scenes. But sometimes we can’t use raster planning unit data because our planning units aren’t equal-sized grid cells. So, below is an example showing how we can initialize a conservation planning problem using planning units that are formatted as spatial vector data. Note that if we had precomputed the amount of each feature in each planning unit and stored the data in the attribute table, we could pass in the names of the columns as an argument to the problem() function.

    +
    # create problem with spatial vector data
    +# note that we have to specify which column in the attribute table contains
    +# the cost data
    +p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost")
    +
    +# print problem
    +print(p2)
    +
    ## Conservation Problem
    +##   planning units: SpatialPolygonsDataFrame (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      none
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +

    We can also initialize a conservation planning problem using tabular planning unit data (i.e., data.frame format). Since the tabular planning unit data does not contain any spatial information, we also have to provide the feature data in tabular format (i.e., data.frame format) and data showing the amount of each feature in each planning unit in tabular format (i.e., data.frame format). The feature data must have an “id” column containing a unique integer identifier for each feature, and the planning unit by feature data must contain the following three columns: pu corresponding to the planning unit identifiers, species corresponding to the feature identifiers, and amount showing the amount of a given feature in a given planning unit.

    +
    # set file path for feature data
    +spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr")
    +
    +# load in feature data
    +spec_dat <- data.table::fread(spec_path, data.table = FALSE)
    +
    +# print first six rows of the data
    +# note that it contains extra columns
    +head(spec_dat)
    +
    ##   id prop spf   name
    +## 1 10  0.3   1  bird1
    +## 2 11  0.3   1  nvis2
    +## 3 12  0.3   1  nvis8
    +## 4 13  0.3   1  nvis9
    +## 5 14  0.3   1 nvis14
    +## 6 15  0.3   1 nvis20
    +
    # set file path for planning unit vs. feature data
    +puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr")
    +
    +# load in planning unit vs feature data
    +puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE)
    +
    +# print first six rows of the data
    +head(puvspr_dat)
    +
    ##   species  pu     amount
    +## 1      26  56 1203448.84
    +## 2      26  58  451670.10
    +## 3      26  84  680473.75
    +## 4      26  85   97356.24
    +## 5      26  86   78034.76
    +## 6      26 111 4783274.17
    +
    # create problem
    +p3 <- problem(pu_dat, spec_dat, cost_column = "cost", rij = puvspr_dat)
    +
    +# print problem
    +print(p3)
    +
    ## Conservation Problem
    +##   planning units: data.frame (1751 units)
    +##   cost:           min: 0, max: 41569219.38232
    +##   features:       bird1, nvis2, nvis8, ... (17 features)
    +##   objective:      none
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +

    For more information on initializing problems, please see the help page for the problem() function (which you can open by entering the code: ?problem). Now that we have initialized a conservation planning problem, we will show you how you can customize it to suit the exact needs of your conservation planning scenario. Although we initialized the conservation planning problems using several different methods, moving forward, we will only use raster-based planning unit data to keep things simple.

    +
    +
    +

    Add an objective

    +

    The next step is to add a primary objective to the problem. A problem objective is used to specify the primary goal of the problem (i.e., the quantity that is to be maximized or minimized). All conservation planning problems involve minimizing or maximizing some kind of objective. For instance, we might require a solution that conserves enough habitat for each species while minimizing the overall cost of the reserve network. Alternatively, we might require a solution that maximizes the number of conserved species while ensuring that the cost of the reserve network does not exceed the budget. Please note that objectives are added in the same way regardless of the type of data used to initialize the problem. The following objectives are available.

    +
      +
    • Minimum set objective: Minimize the cost of the solution whilst ensuring that all targets are met (Rodrigues et al. 2000). This objective is similar to that used in Marxan (Ball et al. 2009). For example, we can add a minimum set objective to a problem using the following code.
    • +
    +
    # create a new problem that has the minimum set objective
    +p3 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective()
    +
    +# print the problem
    +print(p3)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Maximum cover objective: Represent at least one instance of as many features as possible within a given budget (Church et al. 1996).
    • +
    +
    # create a new problem that has the maximum coverage objective and a budget
    +# of 5000
    +p4 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_cover_objective(5000)
    +
    +# print the problem
    +print(p4)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Maximum coverage objective [budget (5000)]
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Maximum features objective: Fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget (inspired by Cabeza & Moilanen 2001). This object is similar to the maximum cover objective except that we have the option of later specifying targets for each feature. In practice, this objective is more useful than the maximum cover objective because features often require a certain amount of area for them to persist and simply capturing a single instance of habitat for each feature is generally unlikely to enhance their long-term persistence.
    • +
    +
    # create a new problem that has the maximum features objective and a budget
    +# of 5000
    +p5 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_features_objective(budget = 5000)
    +
    +# print the problem
    +print(p5)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Maximum representation objective [budget (5000)]
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Minimum shortfall objective: Minimize the overall (weighted sum) shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when there is a large amount of left-over budget when using the maximum feature representation objective and the remaining funds need to be allocated to places that will enhance the representation of features with unmet targets.
    • +
    +
    # create a new problem that has the minimum shortfall objective and a budget
    +# of 5000
    +p6 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_shortfall_objective(budget = 5000)
    +
    +# print the problem
    +print(p6)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum shortfall objective [budget (5000)]
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Minimum largest shortfall objective: Minimize the largest (maximum) shortfall while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when the minimum shortfall objective returns solutions that focus too much on representing a small number of features (e.g., because they occur in much cheaper planning units), and solutions are needed to spread conservation effort out more evenly among all features—even if it means that all features will have (relatively) poor representation.
    • +
    +
    # create a new problem that has the minimum largest shortfall objective and a
    +# budget of 5000
    +p7 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_largest_shortfall_objective(budget = 5000)
    +
    +# print the problem
    +print(p7)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum largest shortfall objective [budget (5000)]
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Maximum phylogenetic diversity objective: Maximize the phylogenetic diversity of the features represented in the solution subject to a budget (inspired by Faith 1992; Rodrigues & Gaston 2002). This objective is similar to the maximum features objective except that emphasis is placed on protecting features which are associated with a diverse range of evolutionary histories. The package contains a simulated phylogeny that can be used with the simulated feature data (sim_phylogny).
    • +
    +
    # load simulated phylogeny data
    +data(sim_phylogeny)
    +
    +# create a new problem that has the maximum phylogenetic diversity
    +# objective and a budget of 5000
    +p8 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_phylo_div_objective(budget = 5000, tree = sim_phylogeny)
    +
    +# print the problem
    +print(p8)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Phylogenetic diversity objective [budget (5000)]
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Maximum phylogenetic endemism objective: Maximize the phylogenetic endemism of the features represented in the solution subject to a budget (inspired by Faith 1992; Rodrigues & Gaston 2002; Rosauer et al. 2009). This objective is similar to the maximum phylogenetic diversity except that emphasis is placed conserving features that are associated with geographically restricted periods of evolutionary history rather than a diverse range of evolutionary histories.
    • +
    +
    # load simulated phylogeny data
    +data(sim_phylogeny)
    +
    +# create a new problem that has the maximum phylogenetic diversity
    +# objective and a budget of 5000
    +p9 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_phylo_end_objective(budget = 5000, tree = sim_phylogeny)
    +
    +# print the problem
    +print(p9)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Phylogenetic endemism objective [budget (5000)]
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Maximum utility objective: Secure as much of the features as possible without exceeding a budget. This objective is functionally equivalent to selecting the planning units with the greatest amounts of each feature (e.g., species richness). Generally, we don’t encourage the use of this objective because it will only rarely identify complementary solutions–solutions which adequately conserve a range of different features—except perhaps to explore trade-offs or provide a baseline solution with which to compare other solutions.
    • +
    +
    # create a new problem that has the maximum utility objective and a budget
    +# of 5000
    +p10 <- problem(sim_pu_raster, sim_features) %>%
    +      add_max_utility_objective(budget = 5000)
    +
    +# print the problem
    +print(p10)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Maximum utility objective [budget (5000)]
    +##   targets:        none
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
    +
    +

    Add targets

    +

    Most conservation planning problems require targets. Targets are used to specify the minimum amount or proportion of a feature’s distribution that needs to be protected in the solution. For example, we may want to develop a reserve network that will secure 20% of the distribution for each feature for minimal cost. The following methods are available for specifying targets.

    +
      +
    • Absolute targets: Targets are expressed as the total amount of each feature in the study area that need to be secured. For example, if we had binary feature data that showed the absence or presence of suitable habitat across the study area, we could set an absolute target as 5 to mean that we require 5 planning units with suitable habitat in the solution.
    • +
    +
    # create a problem with targets which specify that the solution must conserve
    +# a need a sum total of 3 units of suitable habitat for each feature
    +p11 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_absolute_targets(3)
    +
    +# print problem
    +print(p11)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Absolute targets [targets (min: 3, max: 3)]
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Relative targets: Targets are set as a proportion (between 0 and 1) of the total amount of each feature in the study area. For example, if we had binary feature data and the feature occupied a total of 20 planning units in the study area, we could set a relative target of 50 % to specify that the solution must secure 10 planning units for the feature. We could alternatively specify an absolute target of 10 to achieve the same result, but sometimes proportions are easier to work with.
    • +
    +
    # create a problem with the minimum set objective and relative targets of 10 %
    +# for each feature
    +p12 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1)
    +
    +# print problem
    +print(p12)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
    # create a problem with targets which specify that we need 10 % of the habitat
    +# for the first feature, 15 % for the second feature, 20 % for the third feature
    +# 25 % for the fourth feature and 30 % of the habitat for the fifth feature
    +targets <- c(0.1, 0.15, 0.2, 0.25, 0.3)
    +p13 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(targets)
    +
    +# print problem
    +print(p13)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.3)]
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Log-linear targets: Targets are expressed using scaling factors and log-linear interpolation. This method for specifying targets is commonly used for global prioritization analyses (Rodrigues et al. 2004).
    • +
    +
    # create problem with added log-linear targets
    +p14 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_loglinear_targets(10, 0.9, 100, 0.2)
    +
    +# print problem
    +print(p14)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Absolute targets [targets (min: 17.290505409161, max: 21.5906174426385)]
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Manual targets: Targets are manually specified. This is only really recommended for advanced users or problems that involve multiple management zones. See the zones vignette for more information on these targets.
    • +
    +

    As with the functions for specifying the objective of a problem, if we try adding multiple targets to a problem, only the most recently added set of targets are used.

    +
    +
    +

    Add constraints

    +

    A constraint can be added to a conservation planning problem to ensure that all solutions exhibit a specific property. For example, they can be used to make sure that all solutions select a specific planning unit or that all selected planning units in the solution follow a certain configuration. The following constraints are available.

    +
      +
    • Locked in constraints: Add constraints to ensure that certain planning units are prioritized in the solution. For example, it may be desirable to lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network.
    • +
    +
    # create problem with constraints which specify that the first planning unit
    +# must be selected in the solution
    +p15 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_locked_in_constraints(1)
    +
    +# print problem
    +print(p15)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Locked in planning units [1 locked units]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Locked out constraints: Add constraints to ensure that certain planning units are not prioritized in the solution. For example, it may be useful to lock out planning units that have been degraded and are not suitable for conserving species.
    • +
    +
    # create problem with constraints which specify that the second planning unit
    +# must not be selected in the solution
    +p16 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_locked_out_constraints(2)
    +
    +# print problem
    +print(p16)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Locked out planning units [1 locked units]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Neighbor constraints: Add constraints to a conservation problem to ensure that all selected planning units have at least a certain number of neighbors.
    • +
    +
    # create problem with constraints which specify that all selected planning units
    +# in the solution must have at least 1 neighbor
    +p17 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_neighbor_constraints(1)
    +
    +# print problem
    +print(p17)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Neighbor constraint [number of neighbors (1), zones]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Contiguity constraints: Add constraints to a conservation problem to ensure that all selected planning units are spatially connected to each other and form spatially contiguous unit.
    • +
    +
    # create problem with constraints which specify that all selected planning units
    +# in the solution must form a single contiguous unit
    +p18 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_contiguity_constraints()
    +
    +# print problem
    +print(p18)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Contiguity constraints [apply constraints? (1), zones]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Feature contiguity constraints: Add constraints to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of those implemented in the add_contiguity_constraints function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit.
    • +
    +
    # create problem with constraints which specify that the planning units used
    +# to conserve each feature must form a contiguous unit
    +p19 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_feature_contiguity_constraints()
    +
    +# print problem
    +print(p19)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Feature contiguity constraints [apply constraints? (1), layer.1 zones, layer.2 zones, layer.3 zones, layer.4 zones, layer.5 zones]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Linear constraints: Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study region (e.g., different countries).
    • +
    +
    # create problem with constraints which specify that the sum of
    +# values in sim_features[[1]] among selected planning units must not exceed a
    +#' threshold value of 190.
    +p20 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_shortfall_objective(budget = 1800) %>%
    +       add_relative_targets(0.1) %>%
    +       add_linear_constraints(190, "<=", sim_features[[1]])
    +
    +# print problem
    +print(p20)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum shortfall objective [budget (1800)]
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Linear constraints [threshold (190)]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Mandatory allocation constraints: Add constraints to ensure that every planning unit is allocated to a management zone in the solution. Please note that this function can only be used with problems that contain multiple zones. For more information on problems with multiple zones and an example using this function, see the Management Zones vignette.
    • +
    +

    In particular, The add_locked_in_constraints and add_locked_out_constraints functions are incredibly useful for real-world conservation planning exercises, so it’s worth pointing out that there are several ways we can specify which planning units should be locked in or out of the solutions. If we use raster planning unit data, we can also use raster data to specify which planning units should be locked in our locked out.

    +
    # load data to lock in or lock out planning units
    +data(sim_locked_in_raster)
    +data(sim_locked_out_raster)
    +
    +# plot the locked data
    +plot(stack(sim_locked_in_raster, sim_locked_out_raster),
    +     main = c("Locked In", "Locked Out"))
    +

    +
    # create a problem using raster planning unit data and use the locked raster
    +# data to lock in some planning units and lock out some other planning units
    +p21 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_locked_in_constraints(sim_locked_in_raster) %>%
    +       add_locked_out_constraints(sim_locked_out_raster)
    +
    +# print problem
    +print(p21)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Locked out planning units [10 locked units]
    +##                    Locked in planning units [10 locked units]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +

    If our planning unit data are in a spatial vector format (similar to the sim_pu_polygons data) or a tabular format (similar to pu_dat), we can use the field names in the data to refer to which planning units should be locked in and / or out. For example, the sim_pu_polygons object has TRUE / FALSE values in the “locked_in” field which indicate which planning units should be selected in the solution. We could use the data in this field to specify that those planning units with TRUE values should be locked in using the following methods.

    +
    # preview first six rows of the attribute table for sim_pu_polygons
    +head(sim_pu_polygons@data)
    +
    ##       cost locked_in locked_out
    +## 1 215.8638     FALSE      FALSE
    +## 2 212.7823     FALSE      FALSE
    +## 3 207.4962     FALSE      FALSE
    +## 4 208.9322     FALSE       TRUE
    +## 5 214.0419     FALSE      FALSE
    +## 6 213.7636     FALSE      FALSE
    +
    # specify locked in data using the field name
    +p22 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_locked_in_constraints("locked_in")
    +
    +# print problem
    +print(p22)
    +
    ## Conservation Problem
    +##   planning units: SpatialPolygonsDataFrame (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Locked in planning units [10 locked units]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
    # specify locked in data using the values in the field
    +p23 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_locked_in_constraints(which(sim_pu_polygons$locked_in))
    +
    +# print problem
    +print(p23)
    +
    ## Conservation Problem
    +##   planning units: SpatialPolygonsDataFrame (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <Locked in planning units [10 locked units]>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
    +
    +

    Add penalties

    +

    We can also add penalties to a problem to favor or penalize solutions according to a secondary objective. Unlike the constraint functions, these functions will add extra information to the objective function of the optimization function to penalize solutions that do not exhibit specific characteristics. For example, penalties can be added to a problem to avoid highly fragmented solutions at the expense of accepting slightly more expensive solutions. All penalty functions have a penalty argument that controls the relative importance of the secondary penalty function compared to the primary objective function. It is worth noting that incredibly low or incredibly high penalty values – relative to the main objective function – can cause problems to take a very long time to solve, so when trying out a range of different penalty values it can be helpful to limit the solver to run for a set period of time. The following penalties are available.

    +
      +
    • Boundary penalties: Add penalties to penalize solutions that are excessively fragmented. These penalties are similar to those used in Marxan (Ball et al. 2009; Beyer et al. 2016).
    • +
    +
    # create problem with penalties that penalize fragmented solutions with a
    +# penalty factor of 0.01
    +p24 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_boundary_penalties(penalty = 0.01)
    +
    +# print problem
    +print(p24)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (0.01), zones]>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Connectivity penalties: Add penalties to favor solutions that select combinations of planning units with high connectivity between them. These penalties are similar to those used in Marxan with Zones (Watts et al. 2009; Beyer et al. 2016). This function supports both symmetric and asymmetric connectivities among planning units.
    • +
    +
    # create problem with penalties that favor combinations of planning units with
    +# high connectivity, here we will use only the first four layers in
    +# sim_features for the features and we will use the fifth layer in sim_features
    +# to represent the connectivity data, where the connectivity_matrix function
    +# will create a matrix showing the average strength of connectivity between
    +# adjacent planning units using the data in the fifth layer of sim_features
    +p25 <- problem(sim_pu_raster, sim_features[[1:4]]) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_boundary_penalties(
    +         penalty = 5,
    +         data = connectivity_matrix(sim_pu_raster, sim_features[[5]]))
    +
    +# print problem
    +print(p25)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, layer.4 (4 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (5), zones]>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Linear penalties: Add penalties to penalize solutions that select planning units according to a certain variable (e.g., anthropogenic pressure).
    • +
    +
    # create data for penalizing planning units
    +# (note this requires the RandomFields package to be installed)
    +pen_raster <- simulate_cost(sim_pu_raster)
    +
    +# create problem with penalties that penalize solutions that select
    +# planning units with high values in the pen_raster object,
    +# here we will use a penalty value of 5 to indicate the trade-off (scaling)
    +# between the penalty values (in the sim_pu_raster) and the main objective
    +# (i.e., the cost of the solution)
    +p26 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_linear_penalties(penalty = 5, data = pen_raster)
    +
    +# print problem
    +print(p26)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      default
    +##   constraints:    <none>
    +##   penalties:      <Linear penalties [penalty (5)]>
    +##   portfolio:      default
    +##   solver:         default
    +
    +
    +

    Add the decision types

    +

    Conservation planning problems involve making decisions on how planning units will be managed. These decisions are then associated with management actions (e.g., turning a planning unit into a protected area). The type of decision describes how the action is applied to planning units. For instance, the default decision-type is a binary decision type, meaning that we are either selecting or not selecting planning units for management. The following decision types are available.

    +
      +
    • Binary decisions: Add a binary decision to a conservation planning problem. This is the classic decision of either prioritizing or not prioritizing a planning unit. Typically, this decision has the assumed action of buying the planning unit to include in a protected area network. If no decision is added to a problem object, then this decision class will be used by default.
    • +
    +
    # add binary decisions to a problem
    +p27 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions()
    +
    +# print problem
    +print(p27)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Proportion decisions: Add a proportion decision to a problem. This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the default of the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in a protected area network. Generally, problems can be solved much faster with proportion-type decisions than binary-type decisions, so they can be very useful when commercial solvers are not available.
    • +
    +
    # add proportion decisions to a problem
    +p28 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_proportion_decisions()
    +
    +# print problem
    +print(p28)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Proportion decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
      +
    • Semi-continuous decisions: Add a semi-continuous decision to a problem. This decision is similar to proportion decisions except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction (e.g., 80%) of a planning unit can be purchased. This type of decision may be useful when it is not practical to conserve the entire area indicated by a planning unit.
    • +
    +
    # add semi-continuous decisions to a problem, where we can only manage at most
    +# 50 % of the area encompassed by a planning unit
    +p29 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_semicontinuous_decisions(0.5)
    +
    +# print problem
    +print(p29)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Semicontinuous decision [upper limit (0.5)]
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         default
    +
    +
    +

    Add a solver

    +

    Next, after specifying the mathematical formulation that underpins your conservation planning problem, you can specify how the problem should be solved. If you do not specify this information, the prioritizr R package will automatically use the best solver currently installed on your system with some reasonable defaults. We strongly recommend installing the Gurobi software suite and the gurobi R package to solve problems, and for more information on this topic please refer to the Gurobi Installation Guide. The following solvers are available.

    +
      +
    • Gurobi solver: Gurobi is a state of the art commercial optimization software. It is by far the fastest of the solvers that can be used to solve conservation problems. However, it is not freely available. That said, special licenses are available to academics at no cost.
    • +
    +
    # create a problem and specify that Gurobi should be used to solve the problem
    +# and specify an optimality gap of zero to obtain the optimal solution
    +p30 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_gurobi_solver(gap = 0)
    +
    +# print problem
    +print(p30)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         Gurobi [first_feasible (0), gap (0), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (1)]
    +
      +
    • IBM CPLEX solver: IBM CPLEX is a commercial optimization software. It is faster than the open source solvers available for generating prioritizations (see below), however, it is not freely available. Similar to the Gurobi software, special licenses are available to academics at no cost.
    • +
    +
    # create a problem and specify that IBM CPLEX should be used to solve the
    +# problem and specify an optimality gap of zero to obtain the optimal solution
    +p31 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_cplex_solver(gap = 0)
    +
    +# print problem
    +print(p31)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         CPLEX [gap (0), presolve (1), threads (1), time_limit (2147483647), verbose (1)]
    +
      +
    • CBC solver: CBC is an open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks have yet to be completed, preliminary analyses suggest that it is the fastest open-source solver available for generating prioritizations. It requires the rcbc R package, which is currently only available on GitHub.
    • +
    +
    # create a problem and specify that CBC should be used to solve the
    +# problem and specify an optimality gap of zero to obtain the optimal solution
    +p32 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_cbc_solver(gap = 0)
    +
    +# print problem
    +print(p32)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         CBC [first_feasible (0), gap (0), presolve (1), threads (1), time_limit (2147483647), verbose (1)]
    +
      +
    • lpsymphony solver: SYMPHONY is an open-source integer programming solver that is also part of the COIN-OR project. This solver uses the lpsymphony R package (available on Bioconductor) to interface with the SYMPHONY software.
    • +
    +
    # create a problem and specify that lpsymphony should be used to solve the
    +# problem and specify an optimality gap of zero to obtain the optimal solution
    +p33 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_lpsymphony_solver(gap = 0)
    +
    +# print problem
    +print(p33)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         Lpsymphony [first_feasible (0), gap (0), time_limit (2147483647), verbose (1)]
    +
      +
    • Rsymphony solver: This solver provides a different interface to the SYMPHONY software. It uses the Rsymphony R package which is available on The Comprehensive R Archive Network (CRAN). This solver is generally slower than the other solvers, because it cannot use parallel processing.
    • +
    +
    # create a problem and specify that Rsymphony should be used to solve the
    +# problem and specify an optimality gap of zero to obtain the optimal solution
    +p34 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_rsymphony_solver(gap = 0)
    +
    +# print problem
    +print(p34)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      default
    +##   solver:         Rsymphony [first_feasible (0), gap (0), time_limit (2147483647), verbose (1)]
    +
    +
    +

    Add a portfolio

    +

    Many conservation planning exercises require a portfolio of solutions. For example, real-world exercises can involve presenting decision makers with a range of near-optimal decisions. Additionally, the number of times that different planning units are selected in different solutions can provide insight into their relative importance. The following portfolio methods are available.

    +
      +
    • Gap portfolio: Generate a portfolio of solutions by finding a certain number of solutions that are all within a pre-specified optimality gap. This method is especially useful for generating multiple solutions that can used be to calculate selection frequencies (similar to Marxan). Note that this method requires that the Gurobi optimization software is used to generate solutions.
    • +
    +
    # create a problem and specify that a portfolio should be created by
    +# finding five solutions within 10% of optimality
    +p35 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_gap_portfolio(number_solutions = 5, pool_gap = 0.2)
    +
    +# print problem
    +print(p35)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      Gap portfolio [number_solutions (5), pool_gap (0.2)]
    +##   solver:         default
    +
      +
    • Top portfolio: Generate a portfolio of solutions by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions). Note that this method requires that the Gurobi optimization software is used to generate solutions.
    • +
    +
    # create a problem and specify that a portfolio should be created using
    +# the top five solutions
    +p36 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_top_portfolio(number_solutions = 5)
    +
    +# print problem
    +print(p36)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      Top portfolio [number_solutions (5)]
    +##   solver:         default
    +
      +
    • Extra portfolio: Generate a portfolio of solutions by storing feasible solutions found during the optimization process. Note that this method requires that the Gurobi optimization software is used to generate solutions.
    • +
    +
    # create a problem and specify that a portfolio should be created using
    +# extra solutions found while solving the problem
    +p37 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_extra_portfolio()
    +
    +# print problem
    +print(p37)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      Extra portfolio
    +##   solver:         default
    +
      +
    • Cuts portfolio: Generate a portfolio of distinct solutions within a pre-specified optimality gap. This method is only recommended if the Gurobi optimization solver is not available.
    • +
    +
    # create a problem and specify that a portfolio containing 10 solutions
    +# should be created using using Bender's cuts
    +p38 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_cuts_portfolio(number_solutions = 10)
    +
    +# print problem
    +print(p38)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      Cuts portfolio [number_solutions (10)]
    +##   solver:         default
    +
      +
    • Shuffle portfolio: Generate a portfolio of solutions by randomly reordering the data prior to attempting to solve the problem. If the Gurobi optimization solver is not available, this method is the fastest method for generating a set number of solutions within a specified distance from optimality.
    • +
    +
    # create a problem and specify a portfolio should be created that contains
    +# 10 solutions and that any duplicate solutions should not be removed
    +p39 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions() %>%
    +       add_shuffle_portfolio(number_solutions = 10, remove_duplicates = FALSE)
    +
    +# print problem
    +print(p39)
    +
    ## Conservation Problem
    +##   planning units: RasterLayer (90 units)
    +##   cost:           min: 190.13276, max: 215.86384
    +##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   decisions:      Binary decision 
    +##   constraints:    <none>
    +##   penalties:      <none>
    +##   portfolio:      Shuffle portfolio [number_solutions (10), remove_duplicates (0), threads (1)]
    +##   solver:         default
    +
    +
    +

    Solve the problem

    +

    After formulating our conservation planning problem and specifying how the problem should be solved, we can use the solve function to obtain a solution. Note that the solver will typically print out some information describing the size of the problem and report its progress when searching for a suitable solution.

    +
    # formulate the problem
    +p40 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_boundary_penalties(penalty = 500, edge_factor = 0.5) %>%
    +       add_binary_decisions()
    +
    +# solve the problem (using the default solver)
    +s40 <- solve(p40)
    +
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
    +## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    +## Optimize a model with 293 rows, 234 columns and 1026 nonzeros
    +## Model fingerprint: 0xbd38144b
    +## Variable types: 0 continuous, 234 integer (234 binary)
    +## Coefficient statistics:
    +##   Matrix range     [2e-01, 1e+00]
    +##   Objective range  [1e+02, 4e+02]
    +##   Bounds range     [1e+00, 1e+00]
    +##   RHS range        [3e+00, 8e+00]
    +## Found heuristic solution: objective 20287.196992
    +## Found heuristic solution: objective 3087.9617505
    +## Presolve time: 0.00s
    +## Presolved: 293 rows, 234 columns, 1026 nonzeros
    +## Variable types: 0 continuous, 234 integer (234 binary)
    +## Root relaxation presolved: 293 rows, 234 columns, 1026 nonzeros
    +## 
    +## 
    +## Root relaxation: objective 2.265862e+03, 220 iterations, 0.00 seconds (0.00 work units)
    +## 
    +##     Nodes    |    Current Node    |     Objective Bounds      |     Work
    +##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    +## 
    +##      0     0 2265.86242    0  234 3087.96175 2265.86242  26.6%     -    0s
    +##      0     0 2325.53449    0  232 3087.96175 2325.53449  24.7%     -    0s
    +##      0     0 2361.03063    0  223 3087.96175 2361.03063  23.5%     -    0s
    +##      0     0 2371.27288    0  222 3087.96175 2371.27288  23.2%     -    0s
    +##      0     0 2371.27288    0  222 3087.96175 2371.27288  23.2%     -    0s
    +## H    0     0                    2790.8740409 2371.27288  15.0%     -    0s
    +## H    0     0                    2730.9524342 2371.27288  13.2%     -    0s
    +##      0     2 2372.02804    0  222 2730.95243 2372.02804  13.1%     -    0s
    +## H    6     6                    2604.7426048 2375.46233  8.80%  21.0    0s
    +## 
    +## Cutting planes:
    +##   Gomory: 3
    +## 
    +## Explored 7 nodes (489 simplex iterations) in 0.09 seconds (0.10 work units)
    +## Thread count was 1 (of 8 available processors)
    +## 
    +## Solution count 5: 2604.74 2730.95 2790.87 ... 20287.2
    +## 
    +## Optimal solution found (tolerance 1.00e-01)
    +## Best objective 2.604742604847e+03, best bound 2.375462328932e+03, gap 8.8024%
    +
    # plot solution
    +plot(s40, col = c("grey90", "darkgreen"), main = "Solution",
    +     xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1))
    +

    +

    We can plot this solution because the planning unit input data are spatially referenced in a raster format. The output format will always match the planning unit data used to initialize the problem. For example, the solution to a problem with planning units in a spatial vector (shapefile) format would also be in a spatial vector format. Similarly, if the planning units were in a tabular format (i.e., data.frame), the solution would also be returned in a tabular format.

    +

    We can also extract attributes from the solution that describe the quality of the solution and the optimization process.

    +
    # extract the objective (numerical value being minimized or maximized)
    +print(attr(s40, "objective"))
    +
    ## solution_1 
    +##   2604.743
    +
    # extract time spent solving solution
    +print(attr(s40, "runtime"))
    +
    ## solution_1 
    +##       0.11
    +
    # extract state message from the solver that describes why this specific
    +# solution was returned
    +print(attr(s40, "status"))
    +
    ## solution_1 
    +##  "OPTIMAL"
    +
    +
    +
    +

    Evaluate the solution

    +

    Conservation planning involves making trade-offs between different criteria (e.g. overall cost, feature representation, connectivity). After obtaining a solution to a conservation planning problem, it is important to evaluate it to help understand the trade-offs made by the prioritization. This is also useful to compare different solutions with each other.

    +
    +

    Evaluating performance

    +

    Summary statistics can be computed to evaluate the overall performance of a solution based on certain criteria. The following summaries can be computed.

    +

    The following functions are available to summarize a solution:

    +
      +
    • Number summary: Calculate the number of planning units selected by a solution.
    • +
    +
    # calculate statistic
    +eval_n_summary(p40, s40)
    +
    ## # A tibble: 1 × 2
    +##   summary  cost
    +##   <chr>   <dbl>
    +## 1 overall    10
    +
      +
    • Cost summary: Calculate the total cost of a solution.
    • +
    +
    # calculate statistic
    +eval_cost_summary(p40, s40)
    +
    ## # A tibble: 1 × 2
    +##   summary  cost
    +##   <chr>   <dbl>
    +## 1 overall 2005.
    +
      +
    • Feature representation summary: Calculate how well features are represented by a solution. This function can be used for any type of problem.
    • +
    +
    # calculate statistics
    +eval_feature_representation_summary(p40, s40)
    +
    ## # A tibble: 5 × 5
    +##   summary feature total_amount absolute_held relative_held
    +##   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +## 1 overall layer.1         83.3          8.95         0.107
    +## 2 overall layer.2         31.2          3.22         0.103
    +## 3 overall layer.3         72.0          7.59         0.106
    +## 4 overall layer.4         42.7          4.34         0.102
    +## 5 overall layer.5         56.7          5.89         0.104
    +
      +
    • Target coverage summary: Calculate how well representation targets are met by a solution. This function can only be used with problems contain targets.
    • +
    +
    # calculate statistics
    +eval_target_coverage_summary(p40, s40)
    +
    ## # A tibble: 5 × 9
    +##   feature met   total_amount absolute_target absolute_held absolute_shortfall
    +##   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    +## 1 layer.1 TRUE          83.3            8.33          8.95                  0
    +## 2 layer.2 TRUE          31.2            3.12          3.22                  0
    +## 3 layer.3 TRUE          72.0            7.20          7.59                  0
    +## 4 layer.4 TRUE          42.7            4.27          4.34                  0
    +## 5 layer.5 TRUE          56.7            5.67          5.89                  0
    +##   relative_target relative_held relative_shortfall
    +##             <dbl>         <dbl>              <dbl>
    +## 1             0.1         0.107                  0
    +## 2             0.1         0.103                  0
    +## 3             0.1         0.106                  0
    +## 4             0.1         0.102                  0
    +## 5             0.1         0.104                  0
    +
      +
    • Boundary summary: Calculate the total exposed boundary length (perimeter) associated with a solution.
    • +
    +
    # calculate statistic
    +eval_boundary_summary(p40, s40)
    +
    ## # A tibble: 1 × 2
    +##   summary boundary
    +##   <chr>      <dbl>
    +## 1 overall      1.2
    +
      +
    • Connectivity summary: Calculate the connectivity facilitated within a solution.
    • +
    +
    # calculate statistic
    +# here we use the raster data for the first feature as an example
    +# to parametrize pair-wise connectivity between different planning units
    +eval_connectivity_summary(
    +  p40, s40, data = connectivity_matrix(sim_pu_raster, sim_features[[1]]))
    +
    ## # A tibble: 1 × 2
    +##   summary connectivity
    +##   <chr>          <dbl>
    +## 1 overall         1.97
    +
    +
    +

    Evaluating relative importance

    +

    Conservation plans can take a long time to implement. Since funding availability and habitat quality can decline over time, it is critical that the most important places in a prioritization are scheduled for management as early as possible. For instance, some planning units in a solution might contain many rare species which do not occur in any other planning units. Alternatively, some planning units might offer an especially high return on investment that reduces costs considerably. As a consequence, conservation planners often need information on which planning units selected in a prioritization are most important to the overall success of the prioritization. To achieve this, conservation planners can use importance scores for each planning unit selected by a solution.

    +

    Let’s generate a prioritization so that can compare the different importance methods.

    +
    # formulate the problem
    +p41 <- problem(sim_pu_raster, sim_features) %>%
    +       add_min_set_objective() %>%
    +       add_relative_targets(0.1) %>%
    +       add_binary_decisions()
    +
    +# solve the problem
    +s41 <- solve(p41)
    +
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
    +## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    +## Optimize a model with 5 rows, 90 columns and 450 nonzeros
    +## Model fingerprint: 0x6442bf6e
    +## Variable types: 0 continuous, 90 integer (90 binary)
    +## Coefficient statistics:
    +##   Matrix range     [2e-01, 9e-01]
    +##   Objective range  [2e+02, 2e+02]
    +##   Bounds range     [1e+00, 1e+00]
    +##   RHS range        [3e+00, 8e+00]
    +## Found heuristic solution: objective 2337.9617505
    +## Presolve time: 0.00s
    +## Presolved: 5 rows, 90 columns, 450 nonzeros
    +## Variable types: 0 continuous, 90 integer (90 binary)
    +## Found heuristic solution: objective 2332.1003790
    +## Root relaxation presolved: 5 rows, 90 columns, 450 nonzeros
    +## 
    +## 
    +## Root relaxation: objective 1.931582e+03, 12 iterations, 0.00 seconds (0.00 work units)
    +## 
    +##     Nodes    |    Current Node    |     Objective Bounds      |     Work
    +##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    +## 
    +##      0     0 1931.58191    0    4 2332.10038 1931.58191  17.2%     -    0s
    +## H    0     0                    1987.3985265 1931.58191  2.81%     -    0s
    +## 
    +## Explored 1 nodes (12 simplex iterations) in 0.00 seconds (0.00 work units)
    +## Thread count was 1 (of 8 available processors)
    +## 
    +## Solution count 3: 1987.4 2332.1 2337.96 
    +## 
    +## Optimal solution found (tolerance 1.00e-01)
    +## Best objective 1.987398526526e+03, best bound 1.931581908865e+03, gap 2.8085%
    +
    # plot solution
    +plot(s41, col = c("grey90", "darkgreen"), main = "Solution",
    +     xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1))
    +

    +

    The following methods are available for computing importance scores.

    +
      +
    • Replacement cost: Evaluate importance using the replacement cost method (Cabeza & Moilanen 2006). This method quantifies the importance of a given planning unit as the decrease in the performance of the solution (based on the objective function) if the planning unit cannot be acquired (e.g., in terms of the additional costs required to meet feature targets). The advantages of this method are that it (i) accounts for the costs of the planning units, (ii) can be applied to multiple management zones, (iii) can be applied to any objective function, and (iv) can identify truly irreplaceable planning units (denoted with infinite values). However, the key disadvantage of this method, is that can take an infeasible amount of time to complete for large and complex problems.
    • +
    +
    # calculate replacement cost scores and make the solver quiet
    +rc41 <- p41 %>%
    +        add_default_solver(gap = 0, verbose = FALSE) %>%
    +        eval_replacement_importance(s41)
    +
    +# plot replacement cost scores
    +plot(rc41, main = "Replacement cost scores")
    +

    * Ferrier method: Evaluate importance by computing irreplaceability scores following Ferrier et al. (r-34?). The advantages of this method are that it (i) can be computed relatively quickly for moderate and large-sized problems, and (ii) calculates a score for each feature within each planning unit to provide insight into why certain planning units are more important than others. The disadvatange with this method is that it can only be applied to conservation problems with a minimum set objective and a single zone (i.e., similar to Marxan-type problems).

    +
    # calculate Ferrier scores and extract total score
    +fs41 <- eval_ferrier_importance(p40, s40)[["total"]]
    +
    +# plot Ferrier scores
    +plot(fs41, main = "Ferrier scores")
    +

    * Rarity weighted richness: Evaluate importance by computing rarity weighted richness scores (Williams et al. 1996). The only advantage with this method is that it can be computed very quickly for very large problems. The key disadvantage with this approach is that it merely describes the spatial patterns of biodiversity, and does not consider any of the goals that underpin conservation planning exercise. For instance, it does not account for planning costs, management zones, objective functions, or feature representation targets.

    +
    # calculate rarity weighted richness scores
    +rwr41 <- eval_rare_richness_importance(p40, s40)
    +
    +# plot rarity weighted richness scores
    +plot(rwr41, main = "Rarity weighted richness")
    +

    +

    In general, we recommend using replacement cost scores for small and moderate sized problems (e.g., less than 30,000 planning units) when it is feasible to do so. It can take a very long time to compute replacement cost scores, and so it is simply not feasible to compute these scores for particularly large problems. For moderate and large sized problems (e.g., more than 30,000 planning units), we recommend using the Ferrier method. This is because it explicitly accounts for representation targets, unlike the rarity weighted richness scores. We almost never recommend using the rarity weighted richness scores. This is because they do not consider criteria needed to inform conservation decision making (Brown et al. 2015).

    +
    +
    +
    +

    Marxan problems

    +

    Although we encourage users to build and tailor conservation planning problems to suit their own needs, sometimes it just simply easier to use something you’re already familiar with. The marxan_problem() function is provided as a convenient wrapper for building and solving Marxan-style conservation problems. If users already have their conservation planning data formatted for use with Marxan, this function can also read Marxan data files and solve the Marxan-style problems using exact algorithm solvers. Please note that problems built using the marxan_problem() function are still solved the same way as a problem initialized using the problem() function, and therefore still require the installation of one of the solver packages.

    +

    Here is a short example showing how the marxan_problem() function can be used to read Marxan input files and the solve function can be used to solve the problem.

    +
    # set file path for Marxan input file
    +minput <- system.file("extdata/input.dat", package = "prioritizr")
    +
    +# read Marxan input file
    +mp <- marxan_problem(minput)
    +
    +# print problem
    +print(mp)
    +
    ## Conservation Problem
    +##   planning units: data.frame (1751 units)
    +##   cost:           min: 0, max: 41569219.38232
    +##   features:       bird1, nvis2, nvis8, ... (17 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.3, max: 0.3)]
    +##   decisions:      default
    +##   constraints:    <Locked out planning units [1 locked units]
    +##                    Locked in planning units [317 locked units]>
    +##   penalties:      <Boundary penalties [edge factor (min: 1, max: 1), penalty (1), zones]>
    +##   portfolio:      default
    +##   solver:         default
    +
    # solve the problem
    +ms <- solve(mp)
    +
    +# since the Marxan data was in a tabular format, the solution is also returned
    +# in a tabular format, so we will print the first six rows of the table
    +# containing the solution
    +head(ms)
    +

    Alternatively, rather then using a Marxan input file to construct the problem, we can manually read in the Marxan data files and input these to the marxan_problem() function.

    +
    # load data
    +pu <- system.file("extdata/input/pu.dat", package = "prioritizr") %>%
    +      read.table(sep = ",", header = TRUE)
    +features <- system.file("extdata/input/spec.dat", package = "prioritizr") %>%
    +            read.table(sep = ",", header = TRUE)
    +bound <- system.file("extdata/input/bound.dat", package = "prioritizr") %>%
    +         read.table(sep = "\t", header = TRUE)
    +rij <- system.file("extdata/input/puvspr.dat", package = "prioritizr") %>%
    +       read.table(sep = ",", header = TRUE)
    +
    +# build Marxan problem using data.frame objects
    +mp2 <- marxan_problem(x = pu, spec = features, puvspr = rij, bound = bound,
    +                      blm = 0)
    +
    +# print problem
    +print(mp2)
    +
    ## Conservation Problem
    +##   planning units: data.frame (1751 units)
    +##   cost:           min: 0, max: 41569219.38232
    +##   features:       bird1, nvis2, nvis8, ... (17 features)
    +##   objective:      Minimum set objective 
    +##   targets:        Relative targets [targets (min: 0.3, max: 0.3)]
    +##   decisions:      default
    +##   constraints:    <Locked out planning units [1 locked units]
    +##                    Locked in planning units [317 locked units]>
    +##   penalties:      <Boundary penalties [edge factor (min: 1, max: 1), penalty (0), zones]>
    +##   portfolio:      default
    +##   solver:         default
    +

    Conservation planning problems that are built using the marxan_problem() function can also be customized. For example, we could change the decision type for mp2 to involve selecting a proportion of each planing unit (using the add_proportion_decisions() function).

    +
    +
    +

    Conclusion

    +

    Hopefully, this vignette has provided an informative overview of the prioritizr R package. For more examples using the package, please see the other vignettes. Perhaps, one of the best ways to learn a new piece of software is to just try it out. Test it, try breaking it, make mistakes, and learn from them. We would recommend trying to build conservation planning problems that resemble those you face in your own work—but using the built-in example data sets. This way you can quickly verify that the problems you build actually mean what you think they mean. For instance, you can try playing around with the targets and see what effect they have on the solutions, or try playing around with penalties and see what effect they have on the solutions. Finally, if you have any questions about using the package or suggestions for it, please post an issue on the package’s online coding repository.

    +
    +
    +

    References

    +
    +
    +Achterberg, T. & Wunderling, R. (2013). Mixed Integer Programming: Analyzing 12 Years of Progress. Facets of combinatorial optimization: Festschrift for martin grötschel (eds M. Jünger & G. Reinelt), pp. 449–481. Springer, Berlin, Heidelberg. +
    +
    +Ball, I.R., Possingham, H. & Watts, M.E. (2009). Marxan and relatives: Software for spatial conservation prioritisation. Spatial Conservation Prioritisation: Quantitative Methods & Computational Tools (eds A. Moilanen, K.A. Wilson & H. Possingham), pp. 185–189. Oxford University Press, Oxford, UK. +
    +
    +Beyer, H.L., Dujardin, Y., Watts, M.E. & Possingham, H.P. (2016). Solving conservation planning problems with integer linear programming. Ecological Modelling, 328, 14–22. +
    +
    +Billionnet, A. (2013). Mathematical optimization ideas for biodiversity conservation. European Journal of Operational Research, 231, 514–534. +
    +
    +Brown, C.J., Bode, M., Venter, O., Barnes, M.D., McGowan, J., Runge, C.A., Watson, J.E.M. & Possingham, H.P. (2015). Effective conservation requires clear objectives and prioritizing actions, not places or species. Proceedings of the National Academy of Sciences, 112, E4342. +
    +
    +Butchart, S.H., Clarke, M., Smith, R.J., Sykes, R.E., Scharlemann, J.P., Harfoot, M., Buchanan, G.M., Angulo, A., Balmford, A., Bertzky, B. & others. (2015). Shortfalls and solutions for meeting national and global conservation area targets. Conservation Letters, 8, 329–337. +
    +
    +Cabeza, M. & Moilanen, A. (2001). Design of reserve networks and the persistence of biodiversity. Trends in Ecology & Evolution, 16, 242–248. +
    +
    +Cabeza, M. & Moilanen, A. (2006). Replacement cost: A practical measure of site value for cost-effective reserve planning. Biological Conservation, 132, 336–342. +
    +
    +Church, R.L., Stoms, D.M. & Davis, F.W. (1996). Reserve selection as a maximal covering location problem. Biological conservation, 76, 105–112. +
    +
    +Faith, D.P. (1992). Conservation evaluation and phylogenetic diversity. Biological Conservation, 61, 1–10. +
    +
    +Fuller, R.A., McDonald-Madden, E., Wilson, K.A., Carwardine, J., Grantham, H.S., Watson, J.E., Klein, C.J., Green, D.C. & Possingham, H.P. (2010). Replacing underperforming protected areas achieves better conservation outcomes. Nature, 466, 365. +
    +
    +Kirkpatrick, J.B. (1983). An iterative method for establishing priorities for the selection of nature reserves: An example from Tasmania. Biological Conservation, 25, 127–134. +
    +
    +Kirkpatrick, S., Gelatt, C.D. & Vecchi, M.P. (1983). Optimization by simulated annealing. Science, 220, 671–680. +
    +
    +Klein, C., Wilson, K., Watts, M., Stein, J., Berry, S., Carwardine, J., Smith, M.S., Mackey, B. & Possingham, H. (2009). Incorporating ecological and evolutionary processes into continental-scale conservation planning. Ecological Applications, 19, 206–217. +
    +
    +Margules, C.R. & Pressey, R.L. (2000). Systematic conservation planning. Nature, 405, 243–253. +
    +
    +Moilanen, A. (2007). Landscape Zonation, benefit functions and target-based planning: Unifying reserve selection strategies. Biological Conservation, 134, 571–579. +
    +
    +Nicholls, A.O. & Margules, C.R. (1993). An upgraded reserve selection algorithm. Biological Conservation, 64, 165–169. +
    +
    +Pressey, R.L., Possingham, H.P. & Margules, C.R. (1996). Optimality in reserve selection algorithms: When does it matter and how much? Biological Conservation, 76, 259–267. +
    +
    +Rodrigues, A.S., Akcakaya, H.R., Andelman, S.J., Bakarr, M.I., Boitani, L., Brooks, T.M., Chanson, J.S., Fishpool, L.D., Da Fonseca, G.A., Gaston, K.J. & others. (2004). Global gap analysis: Priority regions for expanding the global protected-area network. BioScience, 54, 1092–1100. +
    +
    +Rodrigues, A.S. & Gaston, K.J. (2002). Maximising phylogenetic diversity in the selection of networks of conservation areas. Biological Conservation, 105, 103–111. +
    +
    +Rodrigues, A.S., Orestes Cerdeira, J. & Gaston, K.J. (2000). Flexibility, efficiency, and accountability: Adapting reserve selection algorithms to more complex conservation problems. Ecography, 23, 565–574. +
    +
    +Rosauer, D., Laffan, S.W., Crisp, M.D., Donnellan, S.C. & Cook, L.G. (2009). Phylogenetic endemism: A new approach for identifying geographical concentrations of evolutionary history. Molecular Ecology, 18, 4061–4072. +
    +
    +Stigner, M.G., Beyer, H.L., Klein, C.J. & Fuller, R.A. (2016). Reconciling recreational use and conservation values in a coastal protected area. Journal of Applied Ecology, 53, 1206–1214. +
    +
    +Watts, M.E., Ball, I.R., Stewart, R.S., Klein, C.J., Wilson, K., Steinback, C., Lourival, R., Kircher, L. & Possingham, H.P. (2009). Marxan with Zones: Software for optimal conservation based land- and sea-use zoning. Environmental Modelling & Software, 24, 1513–1521. +
    +
    +Williams, P., Gibbons, D., Margules, C., Rebelo, A., Humphries, C. & Pressey, R. (1996). A comparison of richness hotspots, rarity hotspots, and complementary areas for conserving diversity of British birds. Conservation Biology, 10, 155–174. +
    +
    +
    + + + + + + + + + + + diff --git a/inst/doc/prioritizr.Rmd b/inst/doc/prioritizr.Rmd index d6f896ebd..dc2883a47 100644 --- a/inst/doc/prioritizr.Rmd +++ b/inst/doc/prioritizr.Rmd @@ -1,5 +1,5 @@ --- -title: "prioritizr: Systematic Conservation Prioritization in R" +title: "Getting started" output: rmarkdown::html_vignette: toc: true @@ -10,900 +10,278 @@ documentclass: article bibliography: references.bib csl: reference-style.csl vignette: > - %\VignetteIndexEntry{prioritizr: Systematic Conservation Prioritization in R} + %\VignetteIndexEntry{Getting started} %\VignetteEngine{knitr::rmarkdown_notangle} --- ```{r, include = FALSE} -h = 3.5 -w = 3.5 +# define dummy variables so that vignette passes package checks +tas_features <- raster::raster(matrix(1)) +``` + +```{r, include = FALSE} +# define variables for vignette figures and code execution +h <- 3.5 +w <- 3.5 is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) -knitr::opts_chunk$set(fig.align = "center", eval = !is_check, - root.dir = normalizePath("../..")) +knitr::opts_chunk$set(fig.align = "center", eval = !is_check) ``` -## Summary - -The _prioritizr R_ package uses integer linear programming (ILP) techniques to provide a flexible interface for building and solving conservation planning problems [@r11; @r16]. It supports a broad range of objectives, constraints, and penalties that can be used to custom-tailor conservation planning problems to the specific needs of a conservation planning exercise. Once built, conservation planning problems can be solved using a variety of commercial and open-source exact algorithm solvers. In contrast to the algorithms conventionally used to solve conservation problems, such as heuristics or simulated annealing [@r3], the exact algorithms used here are guaranteed to find optimal solutions. Furthermore, conservation problems can be constructed to optimize the spatial allocation of different management zone (or actions), meaning that conservation practitioners can identify solutions that benefit multiple stakeholders. Finally, this package has the functionality to read input data formatted for the _Marxan_ conservation planning program [@r3], and find much cheaper solutions in a much shorter period of time than _Marxan_ [@r1]. - ## Introduction -Systematic conservation planning is a rigorous, repeatable, and structured approach to designing new protected areas that efficiently meet conservation objectives [@r4]. Historically, conservation decision-making has often evaluated parcels opportunistically as they became available for purchase, donation, or under threat. Although purchasing such areas may improve the _status quo_, such decisions may not substantially enhance the long-term persistence of target species or communities. Faced with this realization, conservation planners began using decision support tools to help simulate alternative reserve designs over a range of different biodiversity and management goals and, ultimately, guide protected area acquisitions and management actions. Due to the systematic, evidence-based nature of these tools, conservation prioritization can help contribute to a transparent, inclusive, and more defensible decision making process. - -A conservation planning exercise typically starts by defining a study area. This study area should encompass all the areas relevant to the decision maker or the hypothesis being tested. The extent of a study area could encompass a few important localities [e.g. @r18], a single state [e.g. @r17], an entire country [@r19], or the entire planet [@r20]. Next, the study area is divided into a set of discrete areas termed _planning units_. Each planning unit represents a discrete locality in the study area that can be managed independently of other areas. The general idea is that some combination of the planning units can be selected for conservation actions (e.g. protected area establishment, habitat restoration). Planning units are often created as square or hexagon cells that are sized according to the scale of the conservation actions, and the resolution of the data that underpin the planning exercise [but see @r5]. - -Cost data (or a surrogate thereof) are needed to inform the prioritization process. Specifically, these cost data describe the relative expenditure associated with managing each planning unit for conservation. For example, if the goal of the conservation planning exercise is to identify priority areas for expanding a local protected area system, then the cost data could represent the physical cost of purchasing the land. Alternatively, if such data are not available, then surrogate data could are often used instead (e.g. human population density, opportunity cost of foregone commercial activities, or planning unit size). - -Conservation planning exercises also require data on the biodiversity elements that are of conservation interest (termed _conservation features_). These features could be species (e.g. _Neofelis nebulosa_, the Clouded Leopard), populations, or habitats (e.g. mangroves or cloud forest). After identifying the set of relevant conservation features for a conservation planning exercise, spatially explicit data need to be obtained for each and every feature to describe their spatial distribution (e.g. habitat suitability data, probability of occurrence data, presence/absence data). This is important to ensure that conservation features are adequately covered (represented) by prioritizations. After assembling all the data, the next step is to define the conservation planning problem. - -The _prioritizr R_ package is designed to help you build and solve conservation planning problems. Specifically, prioritizations are generated by using formulating a mathematical optimization problem and then solving it to generate a solution. These mathematical optimization problems are formulated using the planning unit data, cost data, and feature data, and with information related to the overarching aim of the prioritization process. In general, the goal of an optimization problem is to minimize (or maximize) an _objective function_ that is calculated using a set of _decision variables_, subject to a series of constraints to ensure that solution exhibits specific properties. The objective function describes the quantity which we are trying to minimize (e.g. cost of the solution) or maximize (e.g. number of features conserved). The decision variables describe the entities that we can control, and indicate which planning units are selected for conservation management and which of those are not. The constraints can be thought of as rules that the need decision variables need to follow. For example, a commonly used constraint is specifying that the solution must not exceed a certain budget. - -A wide variety of approaches have been developed for solving optimization problems. Reserve design problems are frequently solved using simulated annealing [@r6] or heuristics [@r8; @r7]. These methods are conceptually simple and can be applied to a wide variety of optimization problems. However, they do not scale well for large or complex problems [@r1]. Additionally, these methods cannot tell you how close any given solution is to the optimal solution. The _prioritizr R_ package uses exact algorithms to efficiently solve conservation planning problems to within a pre-specified a optimality gap. In other words, you can specify that you need the optimal solution (i.e. a gap of 0%) and the algorithms will, given enough time, find a solution that meets this criteria. In the past, exact algorithms have been too slow for conservation planning exercises [@r9]. However, improvements over the last decade mean that they are now much faster [@r23; @r1]. - -In this package, optimization problems are expressed using _integer linear programming_ (ILP) so that they can be solved using (linear) exact algorithm solvers. The general form of an integer programming problem can be expressed in matrix notation using the following equation. - -$$\text{Minimize} \space \boldsymbol{c}^\text{T} \boldsymbol{x} \space -\space \text{subject to} \space A\boldsymbol{x} -\space \Box \space \boldsymbol{b}$$ - -Here, where $x$ is a vector of decision variables, $c$ and $b$ are vectors of known coefficients, and $A$ is the constraint matrix. The final term specifies a series of structural constants and the $\Box$ symbol is used to indicate that the relational operators for the constraints can be either $\geq$, $=$, or $\leq$. In the context of a conservation planning problem, $c$ could be used to represent the planning unit costs, $A$ could be used to store the data showing the presence / absence (or amount) of each feature in each planning unit, $b$ could be used to represent minimum amount of habitat required for each species in the solution, the $\Box$ could be set to $\geq$ symbols to indicate that the total amount of each feature in the solution must exceed the quantities in $b$. But there are many other ways of formulating the reserve selection problem [@r11]. - -## Package overview - -The _prioritizr R_ package contains eight main types of functions. These -functions are used to: - -* create a new conservation planning [problem](https://prioritizr.net/reference/problem.html) by specifying the planning units, features, and management zones of conservation interest (e.g. species, ecosystems). -* add an [objective](https://prioritizr.net/reference/objectives.html) to a conservation planning problem. -* add [targets](https://prioritizr.net/reference/targets.html) to a problem to specify how much of each feature is desired or required to be conserved in the solutions. -* add [constraints](https://prioritizr.net/reference/constraints.html) to a conservation planning problem to ensure that solutions exhibit specific properties (e.g. select specific planning units for protection). -* add [penalties](https://prioritizr.net/reference/penalties.html) to a problem to penalize solutions according to specific metric (e.g. connectivity). -* add [decisions](https://prioritizr.net/reference/decisions.html) to a problem to specify the nature of the decisions in the problem. -* add methods to generate a [portfolio](https://prioritizr.net/reference/portfolios.html) of solutions. -* add a [solver](https://prioritizr.net/reference/solvers.html) to a conservation problem to specify which software should be used to generate solutions and customize the optimization process. -* [solve](https://prioritizr.net/reference/solve.html) a conservation problem. -* evaluate a solution by computing [summary](https://prioritizr.net/reference/summaries.html) statistics. -* evaluate the relative [importance](https://prioritizr.net/reference/importance.html) (irreplaceability) of planning units selected in a solution. +The aim of this tutorial is to provide a short introduction to the _prioritizr R_ package. It is also intended for helping conservation planners familiar the _Marxan_ decision support tool [@r3] to start using the package for their work. -## Package workflow +## Data -The general workflow when using the _prioritizr R_ package starts with creating a new conservation planning `problem` object using data. Specifically, the `problem` object should be constructed using data that specify the planning units, biodiversity features, management zones (if applicable), and costs. After creating a new `problem` object, it can be customized---by adding objectives, penalties, constraints, and other information---to build a precise representation of the conservation planning problem required, and then solved to obtain a solutions. +Let's load the packages and data used in this tutorial. Since this tutorial uses data from the _prioritizrdata R_ package, please ensure that it is installed. The data used in this tutorial were obtained from the _Introduction to Marxan_ course. Specifically, the data were originally a subset of a larger spatial prioritization project performed under contract to Australia's Department of Environment and Water Resources [@r30]. -All conservation planning problems require an objective. An objective specifies the property which is used to compare different feasible solutions. Simply put, the objective is the property of the solution which should be maximized or minimized during the optimization process. For instance, with the minimum set objective (specified using `add_min_set_objective`), we are seeking to minimize the cost of the solution (similar to _Marxan_). On the other hand, with the maximum coverage objective (specified using `add_max_cover_objective`), we are seeking to maximize the number of different features represented in the solution. - -Many objectives require `targets` (e.g. the minimum set objective). Targets are a specialized set of constraints that relate to the total quantity of each feature secured in the solution (e.g. amount of suitable habitat or number of individuals). In the case of the minimum set objective ( `add_min_set_objective`), they are used to ensure that solutions secure a sufficient quantity of each feature, and in other objectives, such as the maximum features objective ( `add_max_features_objective`) they are used to assess whether a feature has been adequately conserved by a candidate solution. Targets can be expressed numerically as the total amount required for a given feature (using `add_absolute_targets`), or as a proportion of the total amount found in the planning units (using `add_relative_targets`). Note that not all objectives require targets, and a warning will be thrown if an attempt is made to add targets to a problem with an objective that does not use them. - -Constraints and penalties can be added to a conservation planning problem to ensure that solutions exhibit a specific property or penalize solutions which don't exhibit a specific property (respectively). The difference between constraints and penalties, strictly speaking, is constraints are used to rule out potential solutions that don't exhibit a specific property. For instance, constraints can be used to ensure that specific planning units are selected in the solution for prioritization (using `add_locked_in_constraints`) or not selected in the solution for prioritization (using `add_locked_out_constraints`). On the other hand, penalties are combined with the objective of a problem, with a penalty factor, and the overall objective of the problem then becomes to minimize (or maximize) the primary objective function and the penalty function. For example, penalties can be added to a problem to penalize solutions that are excessively fragmented (using `add_boundary_penalties`). These penalties have a `penalty` argument that specifies the relative importance of having spatially clustered solutions. When the argument to `penalty` is high, then solutions which are less fragmented are valued more highly---even if they cost more---and when the argument to `penalty` is low, then the solutions which are more fragmented are valued less highly. - -After building a conservation problem, it can then be solved to obtain a solution (or portfolio of solutions if desired). The solution is returned in the same format as the planning unit data used to construct the problem. This means that if raster or shapefile / vector data was used when initializing the problem, then the solution will also be in raster or shapefile / vector data. This can be very helpful when it comes to interpreting and visualizing solutions because it means that the solution data does not first have to be merged with spatial data before they can be plotted on a map. - -## Usage - -Here we will provide an introduction to using the _prioritizr R_ package to build and solve a conservation planning problem. Please note that we will not discuss conservation planning with multiple zones in this vignette, for more information on working with multiple management zones please see the [zones vignette](zones.html). - -First, we will load the _prioritizr_ package. - -```{r, results = "hide", message = FALSE} -# load package +```{r, message = FALSE} +# load packages +library(prioritizrdata) library(prioritizr) +library(vegan) +library(cluster) -# set default options for printing tabular data -options(tibble.width = Inf) -``` - -### Data - -Now we will load some built-in data sets that are distributed with the _prioritizr R_ package. This package contains several different planning unit data sets. To provide a comprehensive overview of the different ways that we can initialize a conservation planning problem, we will load each of them. - -First, we will load the raster planning unit data (`sim_pu_raster`). Here, the planning units are represented as a raster (i.e. a `RasterLayer` object) and each pixel corresponds to the spatial extent of each panning unit. The pixel values correspond to the acquisition costs of each planning unit. - -```{r} -# load raster planning unit data -data(sim_pu_raster) - -# print description of the data -print(sim_pu_raster) - -# plot the data -plot(sim_pu_raster) -``` - -Secondly, we will load one of the spatial vector planning unit data sets (`sim_pu_polygons`). Here, each polygon (i.e. feature using ArcGIS terminology) corresponds to a different planning unit. This data set has an attribute table that contains additional information about each polygon. Namely, the `cost` field (column) in the attribute table contains the acquisition cost for each planning unit. - -```{r} -# load polygon planning unit data -data(sim_pu_polygons) - -# print first six rows of attribute table -head(sim_pu_polygons@data) - -# plot the planning units -spplot(sim_pu_polygons, zcol = "cost") -``` - -Thirdly, we will load some planning unit data stored in tabular format (i.e. `data.frame` format). For those familiar with _Marxan_ or dealing with very large conservation planning problems (> 10 million planning units), it may be useful to work with data in this format because it does not contain any spatial information which will reduce computational burden. When using tabular data to initialize conservation planning problems, the data must follow the conventions used by _Marxan_. Specifically, each row in the planning unit table must correspond to a different planning unit. The table must also have an "id" column to provide a unique integer identifier for each planning unit, and it must also have a column that indicates the cost of each planning unit. For more information, please see the [official _Marxan_ documentation](https://marxansolutions.org/). - -```{r} -# specify file path for planning unit data -pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr") - -# load in the tabular planning unit data -# note that we use the data.table::fread function, as opposed to the read.csv -# function, because it is much faster -pu_dat <- data.table::fread(pu_path, data.table = FALSE) - -# preview first six rows of the tabular planning unit data -# note that it has some extra columns other than id and cost as per the -# Marxan format -head(pu_dat) -``` - -Finally, we will load data showing the spatial distribution of the conservation features. Our conservation features (`sim_features`) are represented as a stack of raster objects (i.e. a `RasterStack` object) where each layer corresponds to a different feature (e.g. a multi-band GeoTIFF where each band corresponds to a different feature). The pixel values in each layer correspond to the amount of suitable habitat available in a given planning unit. Note that our planning unit raster layer and our conservation feature stack have exactly the same spatial properties (i.e. resolution, extent, coordinate reference system) so their pixels line up perfectly. +# load planning unit data +data(tas_pu) -```{r, fig.width = 4, fig.height = 3} # load feature data -data(sim_features) - -# plot the distribution of suitable habitat for each feature -plot(sim_features, main = paste("Feature", seq_len(nlayers(sim_features))), - nr = 2, box = FALSE, axes = FALSE) -``` - -### Initialize a problem - -After having loaded our planning unit and feature data, we will now try initializing the some conservation planning problems. There are a lot of different ways to initialize a conservation planning problem, so here we will just showcase a few of the more commonly used methods. For an exhaustive description of all the ways you can initialize a conservation problem, see the help file for the `problem` function (which you can open using the code `?problem`). First off, we will initialize a conservation planning problem using the raster data. - -```{r} -# create problem -p1 <- problem(sim_pu_raster, sim_features) - -# print problem -print(p1) - -# print number of planning units -number_of_planning_units(p1) - -# print number of features -number_of_features(p1) -``` - -Generally, we recommend initializing problems using raster data where possible. This is because the `problem` function needs to calculate the amount of each feature in each planning unit, and by providing both the planning unit and feature data in raster format with the same spatial resolution, extents, and coordinate systems, this means that the `problem` function does not need to do any geo-processing behind the scenes. But sometimes we can't use raster planning unit data because our planning units aren't equal-sized grid cells. So, below is an example showing how we can initialize a conservation planning problem using planning units that are formatted as spatial vector data. Note that if we had pre-computed the amount of each feature in each planning unit and stored the data in the attribute table, we could pass in the names of the columns as an argument to the `problem` function. - -```{r} -# create problem with spatial vector data -# note that we have to specify which column in the attribute table contains -# the cost data -p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") - -# print problem -print(p2) -``` - -We can also initialize a conservation planning problem using tabular planning unit data (i.e. a `data.frame`). Since the tabular planning unit data does not contain any spatial information, we also have to provide the feature data in tabular format (i.e. a `data.frame`) and data showing the amount of each feature in each planning unit in tabular format (i.e. a `data.frame`). The feature data must have an "id" column containing a unique integer identifier for each feature, and the planning unit by feature data must contain the following three columns: "pu" corresponding to the planning unit identifiers, "species" corresponding to the feature identifiers, and "amount" showing the amount of a given feature in a given planning unit. - -```{r} -# set file path for feature data -spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr") - -# load in feature data -spec_dat <- data.table::fread(spec_path, data.table = FALSE) - -# print first six rows of the data -# note that it contains extra columns -head(spec_dat) - -# set file path for planning unit vs. feature data -puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr") - -# load in planning unit vs feature data -puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE) - -# print first six rows of the data -head(puvspr_dat) - -# create problem -p3 <- problem(pu_dat, spec_dat, cost_column = "cost", rij = puvspr_dat) - -# print problem -print(p3) +data(tas_features) ``` -For more information on initializing problems, please see the help page for the `problem` function (which you can open using the code `?problem`). Now that we have initialized a conservation planning problem, we will show you how you can customize it to suit the exact needs of your conservation planning scenario. Although we initialized the conservation planning problems using several different methods, moving forward, we will only use raster-based planning unit data to keep things simple. +The `tas_pu` object contains planning units represented as spatial polygons (i.e., a `SpatialPolygonsDataFrame` object). This object has three columns that denote the following information for each planning unit: a unique identifier (`id`), unimproved land value (`cost`), and current conservation status (`locked_in`). Planning units that have at least half of their area overlapping with existing protected areas are denoted with a locked in value of 1, otherwise they are denoted with a value of 0. If you are familiar with the _Marxan_ decision support tool, then you will notice that some of these columns are formatted similar conventions. -### Add an objective +Now, let's have a look at the planning unit data. We can see that the planning units correspond to hexagonal land parcels. We can also see that is a clear spatial pattern in the cost and conservation status the planning units. -The next step is to add an objective to the problem. A problem objective is used to specify the primary goal of the problem (i.e. the quantity that is to be maximized or minimized). All conservation planning problems involve minimizing or maximizing some kind of objective. For instance, we might require a solution that conserves enough habitat for each species while minimizing the overall cost of the reserve network. Alternatively, we might require a solution that maximizes the number of conserved species while ensuring that the cost of the reserve network does not exceed the budget. Please note that objectives are added in the same way regardless of the type of data used to initialize the problem. +```{r, fig.width = w, fig.height = h} +# print planning unit data +print(tas_pu) -The _prioritizr R_ package supports a variety of different objective functions. +# plot map of planning unit costs +plot(st_as_sf(tas_pu[, "cost"]), main = "Planning unit costs") -* __Minimum set objective__: Minimize the cost of the solution whilst ensuring that all targets are met [@r11]. This objective is similar to that used in _Marxan_ [@r3]. For example, we can add a minimum set objective to a problem using the following code. -```{r} -# create a new problem that has the minimum set objective -p3 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() - -# print the problem -print(p3) +# plot map of planning unit coverage by protected areas +plot(st_as_sf(tas_pu[, "locked_in"]), main = "Protected area coverage") ``` -* __Maximum cover objective__: Represent at least one instance of as many features as possible within a given budget [@r12]. -```{r} -# create a new problem that has the maximum coverage objective and a budget -# of 5000 -p4 <- problem(sim_pu_raster, sim_features) %>% - add_max_cover_objective(5000) -# print the problem -print(p4) -``` -* __Maximum features objective__: Fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget [inspired by @r10]. This object is similar to the maximum cover objective except that we have the option of later specifying targets for each feature. In practice, this objective is more useful than the maximum cover objective because features often require a certain amount of area for them to persist and simply capturing a single instance of habitat for each feature is generally unlikely to enhance their long-term persistence. -```{r} -# create a new problem that has the maximum features objective and a budget -# of 5000 -p5 <- problem(sim_pu_raster, sim_features) %>% - add_max_features_objective(budget = 5000) +The `tas_features` object describes the spatial distribution of the features. Specifically, the feature data are expressed as a stack of `r raster::nlayers(tas_features)` rasters (i.e., a `RasterStack` object). Each layer in the stack corresponds to one of `r raster::nlayers(tas_features)` different vegetation communities. To describe the spatial distribution of a given vegetation community, each layer contains a spatially referenced grid of rectangular cells and each of these grid cells is associated with information on the distribution of the a given vegetation community. Specifically, these grid cells are assigned values that indicate if a given vegetation community is present (using value of 1) or absent (using value of 0) within the spatial extent of each grid cell. -# print the problem -print(p5) -``` -* __Minimum shortfall objective__: Minimize the overall (weighted sum) shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when there is a large amount of left-over budget when using the maximum feature representation objective and the remaining funds need to be allocated to places that will enhance the representation of features with unmet targets. -```{r} -# create a new problem that has the minimum shortfall objective and a budget -# of 5000 -p6 <- problem(sim_pu_raster, sim_features) %>% - add_min_shortfall_objective(budget = 5000) +Next, let's examine the feature data. Here we will only plot the first four features as an example. The pixel values denote the presence (denoted by a value of 1) or absence (denoted by a value of zero) of each feature within the extent of the study area. -# print the problem -print(p6) -``` -* __Minimum largest shortfall objective__: Minimize the largest (maximum) shortfall while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when the minimum shortfall objective returns solutions that focus too much on representing a small number of features (e.g. because they occur in much cheaper planning units), and solutions are needed to spread conservation effort out more evenly amongst all features---even if it means that all features will have (relatively) poor representation. -```{r} -# create a new problem that has the minimum largest shortfall objective and a -# budget of 5000 -p7 <- problem(sim_pu_raster, sim_features) %>% - add_min_largest_shortfall_objective(budget = 5000) +```{r, fig.width = 4.5, fig.height = 4.5} +# print planning unit data +print(tas_features) -# print the problem -print(p7) +# plot map of the first four vegetation classes +plot(tas_features[[1:4]], main = paste("Feature", 1:4)) ``` -* __Maximum phylogenetic diversity objective__: Maximize the phylogenetic diversity of the features represented in the solution subject to a budget [inspired by @r13; @r14]. This objective is similar to the maximum features objective except that emphasis is placed on protecting features which are associated with a diverse range of evolutionary histories. The _prioritizr R_ package contains a simulated phylogeny that can be used with the simulated feature data (`sim_phylogny`). -```{r} -# load simulated phylogeny data -data(sim_phylogeny) -# create a new problem that has the maximum phylogenetic diversity -# objective and a budget of 5000 -p8 <- problem(sim_pu_raster, sim_features) %>% - add_max_phylo_div_objective(budget = 5000, tree = sim_phylogeny) +The planning units in this tutorial are stored as spatial polygons. Although spatial polygons provide considerable flexibility in the shape and size of the planning units, such flexibility comes at a cost. This is because the spatial data processing routines needed to combine spatial polygon data and raster data for optimization can be very computationally expensive (e.g., calculating zonal statistics). As a consequence, we generally recommend using raster-based planning unit data where possible to reduce processing time. Another strategy is to complete spatial data processing routines manually using other software (e.g., _ESRI ArcGIS_) and use the pre-processed data directly with the _prioritizr R_ package. -# print the problem -print(p8) -``` -* __Maximum phylogenetic endemism objective__: Maximize the phylogenetic endemism of the features represented in the solution subject to a budget [inspired by @r13; @r14; @r32]. This objective is similar to the maximum phylogenetic diversity except that emphasis is placed conserving features that are associated with geographically restricted periods of evolutionary history rather than a diverse range of evolutionary histories. -```{r} -# load simulated phylogeny data -data(sim_phylogeny) +## Problem formulation -# create a new problem that has the maximum phylogenetic diversity -# objective and a budget of 5000 -p9 <- problem(sim_pu_raster, sim_features) %>% - add_max_phylo_end_objective(budget = 5000, tree = sim_phylogeny) +Now we will formulate a conservation planing problem. To achieve this, we first specify which objects contain the planning unit and feature data (using the `problem()` function). Next, we specify that we want to use the minimum set objective function (using the `add_min_set_objective()` function). This objective function indicates that we wish to minimize the total cost of planning units selected by the prioritization. We then specify boundary penalties reduce spatial fragmentation in the resulting prioritization (using the `add_boundary_penalties()` function; see the [_Calibrating trade-offs_ vignette](calibrating_trade-offs.html) for details on calibrating the penalty value). We also specify representation targets to ensure the resulting prioritization provides adequate coverage of each vegetation community (using the `add_relative_targets()` function). Specifically, we specify targets to ensure at least 17% of the spatial extent of each vegetation community (based on the [Aichi Target 11](https://www.cbd.int/sp/targets/)). Additionally, we set constraints to ensure that planning units predominately covered by existing protected areas are selected by the prioritization (using the `add_locked_in_constraints()` function). Finally, we specify that the prioritization should either select -- or not select -- planning units for prioritization (using the `add_binary_decisions()` function). -# print the problem -print(p9) -``` -* __Maximum utility objective__: Secure as much of the features as possible without exceeding a budget. This objective is functionally equivalent to selecting the planning units with the greatest amounts of each feature (e.g. species richness). Generally, we don't encourage the use of this objective because it will only rarely identify complementary solutions---solutions which adequately conserve a range of different features---except perhaps to explore trade-offs or provide a baseline solution with which to compare other solutions. -```{r} -# create a new problem that has the maximum utility objective and a budget -# of 5000 -p10 <- problem(sim_pu_raster, sim_features) %>% - add_max_utility_objective(budget = 5000) +```{r, fig.width = w, fig.height = h} +# build problem +p1 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 0.005) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() # print the problem -print(p10) -``` - -### Add targets - -Most conservation planning problems require targets. Targets are used to specify the minimum amount or proportion of a feature's distribution that needs to be protected in the solution. For example, we may want to develop a reserve network that will secure 20% of the distribution for each feature for minimal cost. - -There are four ways for specifying targets in the _prioritizr R_ package: - -* __Absolute targets__: Targets are expressed as the total amount of each feature in the study area that need to be secured. For example, if we had binary feature data that showed the absence or presence of suitable habitat across the study area, we could set an absolute target as 5 to mean that we require 5 planning units with suitable habitat in the solution. -```{r} -# create a problem with targets which specify that the solution must conserve -# a need a sum total of 3 units of suitable habitat for each feature -p11 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_absolute_targets(3) - -# print problem -print(p11) -``` -* __Relative targets__: Targets are set as a proportion (between 0 and 1) of the total amount of each feature in the study area. For example, if we had binary feature data and the feature occupied a total of 20 planning units in the study area, we could set a relative target of 50 % to specify that the solution must secure 10 planning units for the feature. We could alternatively specify an absolute target of 10 to achieve the same result, but sometimes proportions are easier to work with. -```{r} -# create a problem with the minimum set objective and relative targets of 10 % -# for each feature -p12 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) - -# print problem -print(p12) - -# create a problem with targets which specify that we need 10 % of the habitat -# for the first feature, 15 % for the second feature, 20 % for the third feature -# 25 % for the fourth feature and 30 % of the habitat for the fifth feature -targets <- c(0.1, 0.15, 0.2, 0.25, 0.3) -p13 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(targets) - -# print problem -print(p13) -``` -* __Log-linear targets__: Targets are expressed using scaling factors and log-linear interpolation. This method for specifying targets is commonly used for global prioritization analyses [@r15]. -```{r} -# create problem with added log-linear targets -p14 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_loglinear_targets(10, 0.9, 100, 0.2) - -# print problem -print(p14) -``` -* __Manual targets__: Targets are manually specified. This is only really recommended for advanced users or problems that involve multiple management zones. See the [zones vignette](zones.html) for more information on these targets. - -As with the functions for specifying the objective of a problem, if we try adding multiple targets to a problem, only the most recently added set of targets are used. - -### Add constraints - -A constraint can be added to a conservation planning problem to ensure that all solutions exhibit a specific property. For example, they can be used to make sure that all solutions select a specific planning unit or that all selected planning units in the solution follow a certain configuration. - -The following constraints can be added to conservation planning problems in the _prioritizr R_ package. - -* __Locked in constraints__: Add constraints to ensure that certain planning units are prioritized in the solution. For example, it may be desirable to lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network. -```{r} -# create problem with constraints which specify that the first planning unit -# must be selected in the solution -p15 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints(1) - -# print problem -print(p15) -``` -* __Locked out constraints__: Add constraints to ensure that certain planning units are not prioritized in the solution. For example, it may be useful to lock out planning units that have been degraded and are not suitable for conserving species. -```{r} -# create problem with constraints which specify that the second planning unit -# must not be selected in the solution -p16 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_out_constraints(2) - -# print problem -print(p16) -``` -* __Neighbor constraints__: Add constraints to a conservation problem to ensure that all selected planning units have at least a certain number of neighbors. -```{r} -# create problem with constraints which specify that all selected planning units -# in the solution must have at least 1 neighbor -p17 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_neighbor_constraints(1) - -# print problem -print(p17) -``` -* __Contiguity constraints__: Add constraints to a conservation problem to ensure that all selected planning units are spatially connected to each other and form spatially contiguous unit. -```{r} -# create problem with constraints which specify that all selected planning units -# in the solution must form a single contiguous unit -p18 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_contiguity_constraints() - -# print problem -print(p18) -``` -* __Feature contiguity constraints__: Add constraints to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of those implemented in the `add_contiguity_constraints` function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit. -```{r} -# create problem with constraints which specify that the planning units used -# to conserve each feature must form a contiguous unit -p19 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_feature_contiguity_constraints() - -# print problem -print(p19) -``` - -* __Linear constraints__: Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study region (e.g. different countries). - -```{r} -# create problem with constraints which specify that the sum of -# values in sim_features[[1]] among selected planning units must not exceed a -#' threshold value of 190. -p20 <- problem(sim_pu_raster, sim_features) %>% - add_min_shortfall_objective(budget = 1800) %>% - add_relative_targets(0.1) %>% - add_linear_constraints(190, "<=", sim_features[[1]]) - -# print problem -print(p20) +print(p1) ``` -* __Mandatory allocation constraints__: Add constraints to ensure that every planning unit is allocated to a management zone in the solution. Please note that this function can only be used with problems that contain multiple zones. For more information on problems with multiple zones and an example using this function, see the Management Zones vignette. +## Prioritization -In particular, The `add_locked_in_constraints` and `add_locked_out_constraints` functions are incredibly useful for real-world conservation planning exercises, so it's worth pointing out that there are several ways we can specify which planning units should be locked in or out of the solutions. If we use raster planning unit data, we can also use raster data to specify which planning units should be locked in our locked out. +We can now solve the problem formulation (`p1`) to generate a prioritization (using the `solve()` function). The _prioritizr_ R package supports a range of different exact algorithm solvers, including _Gurobi_, _IBM CPLEX_, _CBC_, _Rsymphony_, and _lpsymphony_. Although there are benefits and limitations associated with each of these different solvers, they should return similar results. Note that you will need at least one solver installed on your system to generate prioritizations. Since we did not specify a solver when building the problem, the _prioritizr R_ package will automatically select the best available solver installed. We recommend using the _Gurobi_ solver if possible, and have used it for this tutorial (see the _Gurobi Installation Guide_ vignette for installation instructions). After solving the problem, the prioritization will be stored in the `solution_1` column of the `s1` object. -```{r, fig.width = 6.5, fig.height = 2.5} -# load data to lock in or lock out planning units -data(sim_locked_in_raster) -data(sim_locked_out_raster) +```{r, fig.width = w, fig.height = h} +# solve problem +s1 <- solve(p1) -# plot the locked data -plot(stack(sim_locked_in_raster, sim_locked_out_raster), - main = c("Locked In", "Locked Out")) - -# create a problem using raster planning unit data and use the locked raster -# data to lock in some planning units and lock out some other planning units -p21 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints(sim_locked_in_raster) %>% - add_locked_out_constraints(sim_locked_out_raster) - -# print problem -print(p21) +# plot map of prioritization +plot(st_as_sf(s1[, "solution_1"]), main = "Prioritization", + pal = c("grey90", "darkgreen")) ``` -If our planning unit data are in a spatial vector format (similar to the `sim_pu_polygons` data) or a tabular format (similar to `pu_dat`), we can use the field names in the data to refer to which planning units should be locked in and / or out. For example, the `sim_pu_polygons` object has `TRUE` / `FALSE` values in the "locked_in" field which indicate which planning units should be selected in the solution. We could use the data in this field to specify that those planning units with `TRUE` values should be locked in using the following methods. - -```{r} -# preview first six rows of the attribute table for sim_pu_polygons -head(sim_pu_polygons@data) +## Feature representation -# specify locked in data using the field name -p22 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints("locked_in") +Let's examine how well the vegetation communities are represented by existing protected areas and the prioritization. -# print problem -print(p22) +```{r, fig.width = 7} +# create column with existing protected areas +tas_pu$pa <- round(tas_pu$locked_in) -# specify locked in data using the values in the field -p23 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints(which(sim_pu_polygons$locked_in)) +# calculate feature representation statistics based on existing protected areas +tc_pa <- eval_target_coverage_summary(p1, tas_pu[, "pa"]) +print(tc_pa) -# print problem -print(p23) -``` +# calculate feature representation statistics based on the prioritization +tc_s1 <- eval_target_coverage_summary(p1, s1[, "solution_1"]) +print(tc_s1) -### Add penalties +# explore representation by existing protected areas +## calculate number of features adequately represented by existing protected +## areas +sum(tc_pa$met) -We can also add penalties to a problem to favor or penalize solutions according to a secondary objective. Unlike the constraint functions, these functions will add extra information to the objective function of the optimization function to penalize solutions that do not exhibit specific characteristics. For example, penalties can be added to a problem to avoid highly fragmented solutions at the expense of accepting slightly more expensive solutions. All penalty functions have a `penalty` argument that controls the relative importance of the secondary penalty function compared to the primary objective function. It is worth noting that incredibly low or incredibly high `penalty` values---relative to the main objective function---can cause problems to take a very long time to solve, so when trying out a range of different penalty values it can be helpful to limit the solver to run for a set period of time. +## summarize representation (values show percent coverage) +summary(tc_pa$relative_held * 100) -The _prioritizr R_ package currently offers only two methods for adding penalties to a conservation planning problem. +## visualize representation (values show percent coverage) +hist(tc_pa$relative_held * 100, + main = "Feature representation by existing protected areas", + xlim = c(0, 100), + xlab = "Percent coverage of features (%)") -* __Boundary penalties__: Add penalties to penalize solutions that are excessively fragmented. These penalties are similar to those used in _Marxan_ [@r3; @r1]. -```{r} -# create problem with penalties that penalize fragmented solutions with a -# penalty factor of 0.01 -p24 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_boundary_penalties(penalty = 0.01) -# print problem -print(p24) -``` -* __Connectivity penalties__: Add penalties to favor solutions that select combinations of planning units with high connectivity between them. These penalties are similar to those used in _Marxan with Zones_ [@r2; @r1]. This function supports both symmetric and asymmetric connectivities among planning units. -```{r} -# create problem with penalties that favor combinations of planning units with -# high connectivity, here we will use only the first four layers in -# sim_features for the features and we will use the fifth layer in sim_features -# to represent the connectivity data, where the connectivity_matrix function -# will create a matrix showing the average strength of connectivity between -# adjacent planning units using the data in the fifth layer of sim_features -p25 <- problem(sim_pu_raster, sim_features[[1:4]]) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_boundary_penalties( - penalty = 5, - data = connectivity_matrix(sim_pu_raster, sim_features[[5]])) +# explore representation by prioritization +## summarize representation (values show percent coverage) +summary(tc_s1$relative_held * 100) -# print problem -print(p25) -``` -* __Linear penalties__: Add penalties to penalize solutions that select planning units according to a certain variable (e.g. anthropogenic pressure). -```{r} -# create data for penalizing planning units -# (note this requires the RandomFields package to be installed) -pen_raster <- simulate_cost(sim_pu_raster) - -# create problem with penalties that penalize solutions that select -# planning units with high values in the pen_raster object, -# here we will use a penalty value of 5 to indicate the trade-off (scaling) -# between the penalty values (in the sim_pu_raster) and the main objective -# (i.e. the cost of the solution) -p26 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_linear_penalties(penalty = 5, data = pen_raster) +## calculate number of features adequately represented by the prioritization +sum(tc_s1$met) -# print problem -print(p26) +## visualize representation (values show percent coverage) +hist(tc_s1$relative_held * 100, + main = "Feature representation by prioritization", + xlim = c(0, 100), + xlab = "Percent coverage of features (%)") ``` -### Add the decision types +We can see that representation of the vegetation communities by existing protected areas is remarkably poor. For example, many of the vegetation communities have nearly zero coverage by existing protected areas. In other words, are almost entirely absent from existing protected areas. We can also see that all vegetation communities have at least 17% coverage by the prioritization -- meaning that it meets the representation targets for all of the features. -Conservation planning problems involve making decisions on how planning units will be managed. These decisions are then associated with management actions (e.g. turning a planning unit into a protected area). The type of decision describes how the action is applied to planning units. For instance, the default decision-type is a binary decision type, meaning that we are either selecting or not selecting planning units for management. +## Irreplaceability -The _prioritizr R_ package currently offers the following types of decisions for customizing problems. +After generating the prioritization, we can examine the relative importance of planning units selected by the prioritization. This can be useful to identify critically important planning units for conservation -- in other words, places that contain biodiversity features which cannot be represented anywhere else -- and schedule implementation of the prioritization. To achieve this, we will use the Ferrier metric [@r34]. -* __Binary decisions__: Add a binary decision to a conservation planning problem. This is the classic decision of either prioritizing or not prioritizing a planning unit. Typically, this decision has the assumed action of buying the planning unit to include in a protected area network. If no decision is added to a problem object, then this decision class will be used by default. -```{r} -# add binary decisions to a problem -p27 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() - -# print problem -print(p27) -``` -* __Proportion decisions__: Add a proportion decision to a problem. This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the default of the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in a protected area network. Generally, problems can be solved much faster with proportion-type decisions than binary-type decisions, so they can be very useful when commercial solvers are not available. -```{r} -# add proportion decisions to a problem -p28 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_proportion_decisions() +```{r, fig.width = w, fig.height = h} +# calculate irreplaceability +irrep_s1 <- eval_ferrier_importance(p1, s1["solution_1"]) +print(irrep_s1) -# print problem -print(p28) -``` -* __Semi-continuous decisions__: Add a semi-continuous decision to a problem. This decision is similar to proportion decisions except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction (e.g. 80%) of a planning unit can be purchased. This type of decision may be useful when it is not practical to conserve the entire area indicated by a planning unit. -```{r} -# add semi-continuous decisions to a problem, where we can only manage at most -# 50 % of the area encompassed by a planning unit -p29 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_semicontinuous_decisions(0.5) +# manually coerce values for planning units not selected in prioritization +# to NA, so that they are shown in white +irrep_s1$plot_total <- irrep_s1$total +irrep_s1$plot_total[s1$solution_1 < 0.5] <- NA_real_ -# print problem -print(p29) +# plot map of overall importance scores +plot(st_as_sf(irrep_s1[, "plot_total"]), main = "Overall importance") ``` -### Add a solver - -Next, after specifying the mathematical formulation that underpins your conservation planning problem, you can specify how the problem should be solved. If you do not specify this information, the _prioritizr R_ package will automatically use the best solver currently installed on your system with some reasonable defaults. __We strongly recommend installing the [Gurobi software suite and the _gurobi_ _R_ package](https://www.gurobi.com/) to solve problems, and for more information on this topic please refer to the [Gurobi Installation Guide](gurobi_installation.html)__. - -Currently, the _prioritizr R_ package supports five different solvers. - -* __*Gurobi* solver__: [_Gurobi_](https://www.gurobi.com/) is a state of the art commercial optimization software. It is by far the fastest of the solvers that can be used to solve conservation problems. However, it is not freely available. That said, special licenses are available to academics at no cost. -```{r} -# create a problem and specify that Gurobi should be used to solve the problem -# and specify an optimality gap of zero to obtain the optimal solution -p30 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_gurobi_solver(gap = 0) - -# print problem -print(p30) -``` -* __*IBM CPLEX* solver__: [_IBM CPLEX_](https://www.ibm.com/analytics/cplex-optimizer) is a commercial optimization software. It is faster than the open source solvers available for generating prioritizations (see below), however, it is not freely available. Similar to the _Gurobi_ software, special licenses are available to academics at no cost. -```{r} -# create a problem and specify that IBM CPLEX should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p31 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_cplex_solver(gap = 0) +## Portfolios -# print problem -print(p31) -``` -* __*CBC* solver__: [*CBC*](https://projects.coin-or.org/Cbc) is an -open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks have yet to be completed, preliminary analyses suggest that it is -the fastest open-source solver available for generating prioritizations. It requires the _rcbc R_ package, which is currently only available on [GitHub](https://github.com/dirkschumacher/rcbc). -```{r} -# create a problem and specify that CBC should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p32 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_cbc_solver(gap = 0) +Conservation planning exercises often involve generating multiple different prioritizations. This can help decision makers consider different options, and provide starting points for building consensus among stakeholders. To generate a range of different prioritizations given the same problem formulation, we can use portfolio functions. Here we will use the gap portfolio to generate 1000 solutions that are within 30% of optimality. Please note that you will need to have the *Gurobi* solver installed to use this specific portfolio. If you don'thave access to *Gurobi*, you could try using the shuffle portfolio instead (using the `add_shuffle_portfolio()` function). -# print problem -print(p32) -``` -* __*lpsymphony* solver__: [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) is an open-source integer programming solver that is also part of the COIN-OR project. This solver uses the _lpsymphony R_ package (available on [Bioconductor](http://bioconductor.org/packages/release/bioc/html/lpsymphony.html)) to interface with the _SYMPHONY_ software. ```{r} -# create a problem and specify that lpsymphony should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p33 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_lpsymphony_solver(gap = 0) +# create new problem with a portfolio added to it +p2 <- p1 %>% + add_gap_portfolio(number_solutions = 1000, pool_gap = 0.2) # print problem -print(p33) -``` -* __*Rsymphony* solver__: This solver provides a different interface to the [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) software. It uses the _Rsymphony R_ package which is available on The Comprehensive R Archive Network (CRAN). This solver is generally slower than the other solvers, because it cannot use parallel processing. -```{r} -# create a problem and specify that Rsymphony should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p34 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_rsymphony_solver(gap = 0) +print(p2) -# print problem -print(p34) +# generate prioritizations +prt <- solve(p2) +print(prt) ``` -### Add a portfolio - -Many conservation planning exercises require a portfolio of solutions. For example, real-world exercises can involve presenting decision makers with a range of near-optimal decisions. Additionally, the number of times that different planning units are selected in different solutions can provide insight into their relative importance. - -The following methods are available for generating a portfolio of solutions. - -* __Extra portfolio__: Generate a portfolio of solutions by storing feasible solutions found during the optimization process. Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. -```{r} -# create a problem and specify that a portfolio should be created using -# extra solutions found while solving the problem -p35 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_extra_portfolio() - -# print problem -print(p35) -``` -* __Top portfolio__: Generate a portfolio of solutions by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. -```{r} -# create a problem and specify that a portfolio should be created using -# the top five solutions -p36 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_top_portfolio(number_solutions = 5) +After generating all these prioritizations, we now want some way to visualize them. Because it would be onerous to look at each and every prioritization individually, we will use statistical analyses to help us. We can visualize the differences between these different prioritizations -- based on which planning units they selected -- using a hierarchical cluster analysis [@r35]. -# print problem -print(p36) -``` -* __Gap portfolio__: Generate a portfolio of solutions by finding a certain number of solutions that are all within a pre-specified optimality gap. This method is especially useful for generating multiple solutions that can used be to calculate selection frequencies (similar to _Marxan_). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. -```{r} -# create a problem and specify that a portfolio should be created by -# finding five solutions within 10% of optimality -p37 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) +```{r, fig.height = 4.5, fig.width = 7, fig.show = "hold"} +# extract solutions +prt_results <- prt@data[, startsWith(names(prt), "solution_"), ] -# print problem -print(p37) -``` -* __Cuts portfolio__: Generate a portfolio of distinct solutions within a pre-specified optimality gap. This method is only recommended if the _Gurobi_ optimization solver is not available. -```{r} -# create a problem and specify that a portfolio containing 10 solutions -# should be created using using Bender's cuts -p38 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_cuts_portfolio(number_solutions = 10) +# calculate pair-wise distances between different prioritizations for analysis +prt_dists <- vegan::vegdist(t(prt_results), method = "jaccard", binary = TRUE) -# print problem -print(p38) -``` -* __Shuffle portfolio__: Generate a portfolio of solutions by randomly reordering the data prior to attempting to solve the problem. If the _Gurobi_ optimization solver is not available, this method is the fastest method for generating a set number of solutions within a specified distance from optimality. -```{r} -# create a problem and specify a portfolio should be created that contains -# 10 solutions and that any duplicate solutions should not be removed -p39 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_shuffle_portfolio(number_solutions = 10, remove_duplicates = FALSE) +# run cluster analysis +prt_clust <- hclust(as.dist(prt_dists), method = "average") -# print problem -print(p39) +# visualize clusters +opar <- par() +par(oma = c(0, 0, 0, 0), mar= c(0, 4.1, 1.5, 2.1)) +plot(prt_clust, labels = FALSE, sub = NA, xlab = "", + main = "Different prioritizations in portfolio") +suppressWarnings(par(opar)) ``` -### Solve the problem +We can see that there are approximately six main groups of prioritizations in the portfolio. To explore these different groups, let's conduct another cluster analysis (i.e., a _k_-medoids analysis) to extract the most representative prioritization from each of these groups. In other words, we will run another statistical analysis to find the most central prioritization within each group. -Finally, after formulating our conservation planning problem and specifying how the problem should be solved, we can use the `solve` function to obtain a solution. Note that the solver will typically print out some information describing the size of the problem and report its progress when searching for a suitable solution. +```{r, fig.width = 7, fig.height = 5} +# run k-medoids analysis +prt_med <- pam(prt_dists, k = 6) -```{r, fig.height = h, fig.width = w} -# formulate the problem -p40 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_boundary_penalties(penalty = 500, edge_factor = 0.5) %>% - add_binary_decisions() +# extract names of prioritizations that are most central for each group. +prt_med_names <- prt_med$medoids +print(prt_med_names) -# solve the problem (using the default solver) -s40 <- solve(p40) +# create a copy of prt and set values for locked in planning units to -1 +# so we can easily visualize differences between prioritizations +prt2 <- prt[, prt_med_names] +prt2@data[which(tas_pu$locked_in > 0.5), prt_med_names] <- -1 -# plot solution -plot(s40, col = c("grey90", "darkgreen"), main = "Solution", - xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) +# plot a map showing main different prioritizations +# dark grey: locked in planning units +# grey: planning units not selected +# green: selected planning units +plot(st_as_sf(prt2), pal = c("grey60", "grey90", "darkgreen")) ``` -We can plot this solution because the planning unit input data are spatially referenced in a raster format. The output format will always match the planning unit data used to initialize the problem. For example, the solution to a problem with planning units in a spatial vector (shapefile) format would also be in a spatial vector format. Similarly, if the planning units were in a tabular format (i.e. `data.frame`), the solution would also be returned in a tabular format. - -We can also extract attributes from the solution that describe the quality of the solution and the optimization process. - -```{r} -# extract the objective (numerical value being minimized or maximized) -print(attr(s40, "objective")) - -# extract time spent solving solution -print(attr(s40, "runtime")) +## _Marxan_ compatibility -# extract state message from the solver that describes why this specific -# solution was returned -print(attr(s40, "status")) -``` - -### Evaluate the performance of a solution +The _prioritizr R_ package provides functionality to help _Marxan_ users generate prioritizations. Specifically, it can import conservation planning data prepared for _Marxan_, and can generate prioritizations using a similar problem formulation as _Marxan_ [based on @r1]. Indeed, the problem formulation presented earlier in this vignette is very similar to that used by _Marxan_. The key difference is that the problem formulation we specified earlier uses "hard constraints" for feature representation, and _Marxan_ uses "soft constraints" for feature representation. This means that prioritization we generated earlier was mathematically guaranteed to reach the targets for all features. However, if we used _Marxan_ to generate the prioritization, then we could have produced a prioritization that would fail to reach targets (depending the _Species Penalty Factors_ used to generate the prioritization). In addition to these differences in terms problem formulation, the _prioritizr R_ package uses exact algorithms -- instead of the simulated annealing algorithm -- which ensures that we obtain prioritizations that are near optimal. -After obtaining a solution to a conservation planning problem, it can be useful to calculate various summary statistics to understand its performance. The following functions are available to summarize a solution: +Here we will show the _prioritizr R_ package can import _Marxan_ data and generate a prioritization. To begin with, let's import a conservation planning data prepared for _Marxan_. -* Calculate the number of planning units selected within a solution. -```{r} -# calculate statistic -eval_n_summary(p40, s40) -``` -* Calculate the total cost of a solution. -```{r} -# calculate statistic -eval_cost_summary(p40, s40) -``` -* Calculate how well features are represented by a solution. This function can be used for problems that are built using targets and those that are not built using targets. -```{r} -# calculate statistics -eval_feature_representation_summary(p40, s40) -``` -* Calculate how well feature representation targets are met by a solution. This function can only be used with problems contain targets. ```{r} -# calculate statistics -eval_target_coverage_summary(p40, s40) -``` -* Calculate the exposed boundary length (perimeter) associated with a solution. -```{r} -# calculate statistic -eval_boundary_summary(p40, s40) -``` -* Calculate the connectivity held within a solution. -```{r} -# calculate statistic -# here we use the raster data for the first feature as an example -# to parametrize pair-wise connectivity between different planning units -eval_connectivity_summary( - p40, s40, data = connectivity_matrix(sim_pu_raster, sim_features[[1]])) -``` - -### _Marxan_ problems - -Although users are encouraged to build and tailor conservation planning problems to suit their own needs using the `problem` function, sometimes it just simply easier to use a more familiar formulation. The `marxan_problem` function is provided as a convenient wrapper for building and solving _Marxan_-style conservation problems. If users already have their conservation planning data formatted for use with _Marxan_, this function can also read _Marxan_ data files and solve the _Marxan_-style problems using exact algorithm solvers. __Please note that problems built using the `marxan_problem` function are still solved the same way as a problem initialized using the `problem` function, and therefore still require the installation of one of the solver packages.__ - -Here is a short example showing how the `marxan_problem` function can be used to read _Marxan_ input files and the `solve` function can be used to solve the problem. -```{r, results = "hide"} -# set file path for Marxan input file -minput <- system.file("extdata/input.dat", package = "prioritizr") - -# read Marxan input file -mp <- marxan_problem(minput) +# import data +## planning unit data +pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr") +pu_data <- read.csv(pu_path, header = TRUE, stringsAsFactors = FALSE) +print(head(pu_data)) -# print problem -print(mp) +## feature data +spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr") +spec_data <- read.csv(spec_path, header = TRUE, stringsAsFactors = FALSE) +print(head(spec_data)) -# solve the problem -ms <- solve(mp) +## amount of each feature within each planning unit data +puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr") +puvspr_data <- read.csv(puvspr_path, header = TRUE, stringsAsFactors = FALSE) +print(head(puvspr_data)) -# since the Marxan data was in a tabular format, the solution is also returned -# in a tabular format, so we will print the first six rows of the table -# containing the solution -head(ms) +## boundary data +bound_path <- system.file("extdata/input/bound.dat", package = "prioritizr") +bound_data <- read.table(bound_path, header = TRUE, stringsAsFactors = FALSE) +print(head(bound_data)) ``` -Alternatively, rather then using a _Marxan_ input file to construct the problem, we can manually read in the _Marxan_ data files and input these to the `marxan_problem` function. +After importing the data, we can now generate a prioritization based on the _Marxan_ problem formulation (using the `marxan_problem()` function). **Please note that this function does not generate prioritizations using _Marxan_.** Instead, it uses the data to create an optimization problem formulation similar to _Marxan_ -- using hard constraints instead of soft constraints -- and uses an exact algorithm solver to generate a prioritization. ```{r} -# load data -pu <- system.file("extdata/input/pu.dat", package = "prioritizr") %>% - read.table(sep = ",", header = TRUE) -features <- system.file("extdata/input/spec.dat", package = "prioritizr") %>% - read.table(sep = ",", header = TRUE) -bound <- system.file("extdata/input/bound.dat", package = "prioritizr") %>% - read.table(sep = "\t", header = TRUE) -rij <- system.file("extdata/input/puvspr.dat", package = "prioritizr") %>% - read.table(sep = ",", header = TRUE) - -# build Marxan problem using data.frame objects -mp2 <- marxan_problem(x = pu, spec = features, puvspr = rij, bound = bound, - blm = 0) +# create problem +p2 <- marxan_problem(pu_data, spec_data, puvspr_data, bound_data, + blm = 0.0005) # print problem -print(mp2) -``` - -### Importance (irreplaceability) - -Conservation plans can take a long time to implement. Since funding availability and habitat quality can decline over time, it is critical that the most important places in a prioritization are scheduled for protection as early as possible. For instance, some planning units in a solution might contain many rare species which do not occur in any other planning units. Alternatively, some planning units might offer an especially high return on investment that reduces costs considerably. As a consequence, conservation planners often need information on which planning units selected in a prioritization are most important to the overall success of the prioritization. To achieve this, conservation planners can use importance (irreplaceability) scores for each planning unit selected in a solution. - -The _prioritizr R_ package offers multiple methods for assessing importance. These includes scores calculated based on replacement costs [`eval_replacement_importance()`; @r31], Ferrier _et al._ [-@r34] (`eval_ferrier_importance()`), and rarity weighted richness scores [`eval_rare_richness_importance()`; @r33]. The replacement cost scores quantify the change in the objective function (e.g. additional costs required to meet feature targets) of the optimal solution if a given planning unit in a solution cannot be acquired. They can (i) account for the cost of different planning units, (ii) account for multiple management zones, (iii) apply to any objective function, and (iv) identify truly irreplaceable planning units (denoted with infinite values). The Ferrier scores quantify the importance of planning units for meeting feature targets. They can only be applied to conservation problems with a minimum set objective and a single zone (i.e. the classic _Marxan_-type problem). Furthermore---unlike the replacement cost scores---the Ferrier scores provide a score for each feature within each planning unit, providing insight into why certain planning units are more important than other planning units. The rarity weighted richness scores are simply a measure of biological diversity. They do not account for planning costs, multiple management zones, objective functions, or feature targets (or weightings). They merely describe the spatial patterns of biodiversity, and do not account for many of the factors needed to quantify the importance of a planning unit for achieving conservation goals. - -We recommend using replacement cost scores for small and moderate sized problems (e.g. less than 30,000 planning units) when it is feasible to do so. It can take a very long time to compute replacement cost scores, and so it is simply not feasible to compute these scores for particularly large problems. For moderate and large sized problems (e.g. more than 30,000 planning units), we recommend using the Ferrier method. This is because it explicitly accounts for representation targets, unlike the rarity weighted richness scores. - -Below we will generate a solution, and then calculate importance scores for the planning units selected in the solution using the three different methods. - -```{r, fig.height = h, fig.width = w} -# formulate the problem -p41 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() - -# solve the problem -s41 <- solve(p41) - -# plot solution -plot(s41, col = c("grey90", "darkgreen"), main = "Solution", - xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) - -# calculate replacement cost scores and make the solver quiet -rc41 <- p41 %>% - add_default_solver(gap = 0, verbose = FALSE) %>% - eval_replacement_importance(s41) - -# plot replacement cost scores -plot(rc41, main = "replacement cost") -``` - -```{r, fig.height = h, fig.width = w} -# calculate Ferrier scores and extract total score -fs41 <- eval_ferrier_importance(p40, s40)[["total"]] - -# plot Ferrier scores -plot(fs41, main = "Ferrier scores") -``` +print(p2) -```{r, fig.height = h, fig.width = w} -# calculate rarity weighted richness scores -rwr41 <- eval_rare_richness_importance(p40, s40) +# solve problem +s2 <- solve(p2) -# plot replacement cost scores -plot(rwr41, main = "rarity weighted richness") +# print first six rows of solution object +print(head(s2)) ``` -Although rarity weighted richness scores can approximate scores derived from the other two methods in certain conservation planning exercises, we can see that the rarity weighted richness scores provide completely different results in this case. - ## Conclusion -Hopefully, this vignette has provided an informative introduction to the _prioritizr R_ package. For more worked examples using the _prioritizr R_ package, check out the [Tasmania](tasmania.html) and [Salt Spring Island](saltspring.html) vignettes. Perhaps, one of the best ways to learn a new piece of software is to just try it out. Test it, try breaking it, make mistakes, and learn from them. We would recommend trying to build conservation planning problems that resemble those you face in your own work---but using the built-in example data sets. This way you can quickly verify that the problems you build actually mean what you think they mean. For instance, you can try playing around with the targets and see what effect they have on the solutions, or try playing around with penalties and see what effect they have on the solutions. Finally, if you have any questions about using the _prioritizr R_ package or suggestions for it, please file an issue on the package's online coding repository (https://github.com/prioritizr/prioritizr/issues). +This tutorial shows how the _prioritizr R_ package can be used to build a conservation problem, generate a prioritization, and evaluate it. Although we explored just a few functions, the package provides many different functions so that you can build and custom-tailor conservation planning problems to suit your needs. To learn more about the package, please see the package vignettes for [an overview of the package](package_overview.html), [instructions for installing the _Gurobi_ optimization suite](gurobi_installation_guide.html), [benchmarks comparing the performance of different solvers](solver_benchmarks.html), and [a record of publications that have cited the package](publication_record.html). In addition to this tutorial, the package also provides tutorials on [incorporating connectivity into prioritizations](connectivity_tutorial.html), [calibrating trade-offs between different criteria](calibrating_trade-offs.html) (e.g., total cost and spatial fragmentation), and [creating prioritizations that have multiple management zones or management actions](management_zones_tutorial.html). ## References diff --git a/inst/doc/prioritizr.html b/inst/doc/prioritizr.html index fe7517b0f..c8a893e75 100644 --- a/inst/doc/prioritizr.html +++ b/inst/doc/prioritizr.html @@ -12,7 +12,7 @@ -prioritizr: Systematic Conservation Prioritization in R +Getting started @@ -159,1354 +159,498 @@ -

    prioritizr: Systematic Conservation Prioritization in R

    +

    Getting started

    -
    -

    Summary

    -

    The prioritizr R package uses integer linear programming (ILP) techniques to provide a flexible interface for building and solving conservation planning problems (Rodrigues et al. 2000; Billionnet 2013). It supports a broad range of objectives, constraints, and penalties that can be used to custom-tailor conservation planning problems to the specific needs of a conservation planning exercise. Once built, conservation planning problems can be solved using a variety of commercial and open-source exact algorithm solvers. In contrast to the algorithms conventionally used to solve conservation problems, such as heuristics or simulated annealing (Ball et al. 2009), the exact algorithms used here are guaranteed to find optimal solutions. Furthermore, conservation problems can be constructed to optimize the spatial allocation of different management zone (or actions), meaning that conservation practitioners can identify solutions that benefit multiple stakeholders. Finally, this package has the functionality to read input data formatted for the Marxan conservation planning program (Ball et al. 2009), and find much cheaper solutions in a much shorter period of time than Marxan (Beyer et al. 2016).

    -

    Introduction

    -

    Systematic conservation planning is a rigorous, repeatable, and structured approach to designing new protected areas that efficiently meet conservation objectives (Margules & Pressey 2000). Historically, conservation decision-making has often evaluated parcels opportunistically as they became available for purchase, donation, or under threat. Although purchasing such areas may improve the status quo, such decisions may not substantially enhance the long-term persistence of target species or communities. Faced with this realization, conservation planners began using decision support tools to help simulate alternative reserve designs over a range of different biodiversity and management goals and, ultimately, guide protected area acquisitions and management actions. Due to the systematic, evidence-based nature of these tools, conservation prioritization can help contribute to a transparent, inclusive, and more defensible decision making process.

    -

    A conservation planning exercise typically starts by defining a study area. This study area should encompass all the areas relevant to the decision maker or the hypothesis being tested. The extent of a study area could encompass a few important localities (e.g. Stigner et al. 2016), a single state (e.g. Kirkpatrick 1983), an entire country (Fuller et al. 2010), or the entire planet (Butchart et al. 2015). Next, the study area is divided into a set of discrete areas termed planning units. Each planning unit represents a discrete locality in the study area that can be managed independently of other areas. The general idea is that some combination of the planning units can be selected for conservation actions (e.g. protected area establishment, habitat restoration). Planning units are often created as square or hexagon cells that are sized according to the scale of the conservation actions, and the resolution of the data that underpin the planning exercise (but see Klein et al. 2009).

    -

    Cost data (or a surrogate thereof) are needed to inform the prioritization process. Specifically, these cost data describe the relative expenditure associated with managing each planning unit for conservation. For example, if the goal of the conservation planning exercise is to identify priority areas for expanding a local protected area system, then the cost data could represent the physical cost of purchasing the land. Alternatively, if such data are not available, then surrogate data could are often used instead (e.g. human population density, opportunity cost of foregone commercial activities, or planning unit size).

    -

    Conservation planning exercises also require data on the biodiversity elements that are of conservation interest (termed conservation features). These features could be species (e.g. Neofelis nebulosa, the Clouded Leopard), populations, or habitats (e.g. mangroves or cloud forest). After identifying the set of relevant conservation features for a conservation planning exercise, spatially explicit data need to be obtained for each and every feature to describe their spatial distribution (e.g. habitat suitability data, probability of occurrence data, presence/absence data). This is important to ensure that conservation features are adequately covered (represented) by prioritizations. After assembling all the data, the next step is to define the conservation planning problem.

    -

    The prioritizr R package is designed to help you build and solve conservation planning problems. Specifically, prioritizations are generated by using formulating a mathematical optimization problem and then solving it to generate a solution. These mathematical optimization problems are formulated using the planning unit data, cost data, and feature data, and with information related to the overarching aim of the prioritization process. In general, the goal of an optimization problem is to minimize (or maximize) an objective function that is calculated using a set of decision variables, subject to a series of constraints to ensure that solution exhibits specific properties. The objective function describes the quantity which we are trying to minimize (e.g. cost of the solution) or maximize (e.g. number of features conserved). The decision variables describe the entities that we can control, and indicate which planning units are selected for conservation management and which of those are not. The constraints can be thought of as rules that the need decision variables need to follow. For example, a commonly used constraint is specifying that the solution must not exceed a certain budget.

    -

    A wide variety of approaches have been developed for solving optimization problems. Reserve design problems are frequently solved using simulated annealing (Kirkpatrick et al. 1983) or heuristics (Nicholls & Margules 1993; Moilanen 2007). These methods are conceptually simple and can be applied to a wide variety of optimization problems. However, they do not scale well for large or complex problems (Beyer et al. 2016). Additionally, these methods cannot tell you how close any given solution is to the optimal solution. The prioritizr R package uses exact algorithms to efficiently solve conservation planning problems to within a pre-specified a optimality gap. In other words, you can specify that you need the optimal solution (i.e. a gap of 0%) and the algorithms will, given enough time, find a solution that meets this criteria. In the past, exact algorithms have been too slow for conservation planning exercises (Pressey et al. 1996). However, improvements over the last decade mean that they are now much faster (Achterberg & Wunderling 2013; Beyer et al. 2016).

    -

    In this package, optimization problems are expressed using integer linear programming (ILP) so that they can be solved using (linear) exact algorithm solvers. The general form of an integer programming problem can be expressed in matrix notation using the following equation.

    -

    \[\text{Minimize} \space \boldsymbol{c}^\text{T} \boldsymbol{x} \space -\space \text{subject to} \space A\boldsymbol{x} -\space \Box \space \boldsymbol{b}\]

    -

    Here, where \(x\) is a vector of decision variables, \(c\) and \(b\) are vectors of known coefficients, and \(A\) is the constraint matrix. The final term specifies a series of structural constants and the \(\Box\) symbol is used to indicate that the relational operators for the constraints can be either \(\geq\), \(=\), or \(\leq\). In the context of a conservation planning problem, \(c\) could be used to represent the planning unit costs, \(A\) could be used to store the data showing the presence / absence (or amount) of each feature in each planning unit, \(b\) could be used to represent minimum amount of habitat required for each species in the solution, the \(\Box\) could be set to \(\geq\) symbols to indicate that the total amount of each feature in the solution must exceed the quantities in \(b\). But there are many other ways of formulating the reserve selection problem (Rodrigues et al. 2000).

    -
    -
    -

    Package overview

    -

    The prioritizr R package contains eight main types of functions. These functions are used to:

    -
      -
    • create a new conservation planning problem by specifying the planning units, features, and management zones of conservation interest (e.g. species, ecosystems).
    • -
    • add an objective to a conservation planning problem.
    • -
    • add targets to a problem to specify how much of each feature is desired or required to be conserved in the solutions.
    • -
    • add constraints to a conservation planning problem to ensure that solutions exhibit specific properties (e.g. select specific planning units for protection).
    • -
    • add penalties to a problem to penalize solutions according to specific metric (e.g. connectivity).
    • -
    • add decisions to a problem to specify the nature of the decisions in the problem.
    • -
    • add methods to generate a portfolio of solutions.
    • -
    • add a solver to a conservation problem to specify which software should be used to generate solutions and customize the optimization process.
    • -
    • solve a conservation problem.
    • -
    • evaluate a solution by computing summary statistics.
    • -
    • evaluate the relative importance (irreplaceability) of planning units selected in a solution.
    • -
    -
    -
    -

    Package workflow

    -

    The general workflow when using the prioritizr R package starts with creating a new conservation planning problem object using data. Specifically, the problem object should be constructed using data that specify the planning units, biodiversity features, management zones (if applicable), and costs. After creating a new problem object, it can be customized—by adding objectives, penalties, constraints, and other information—to build a precise representation of the conservation planning problem required, and then solved to obtain a solutions.

    -

    All conservation planning problems require an objective. An objective specifies the property which is used to compare different feasible solutions. Simply put, the objective is the property of the solution which should be maximized or minimized during the optimization process. For instance, with the minimum set objective (specified using add_min_set_objective), we are seeking to minimize the cost of the solution (similar to Marxan). On the other hand, with the maximum coverage objective (specified using add_max_cover_objective), we are seeking to maximize the number of different features represented in the solution.

    -

    Many objectives require targets (e.g. the minimum set objective). Targets are a specialized set of constraints that relate to the total quantity of each feature secured in the solution (e.g. amount of suitable habitat or number of individuals). In the case of the minimum set objective ( add_min_set_objective), they are used to ensure that solutions secure a sufficient quantity of each feature, and in other objectives, such as the maximum features objective ( add_max_features_objective) they are used to assess whether a feature has been adequately conserved by a candidate solution. Targets can be expressed numerically as the total amount required for a given feature (using add_absolute_targets), or as a proportion of the total amount found in the planning units (using add_relative_targets). Note that not all objectives require targets, and a warning will be thrown if an attempt is made to add targets to a problem with an objective that does not use them.

    -

    Constraints and penalties can be added to a conservation planning problem to ensure that solutions exhibit a specific property or penalize solutions which don’t exhibit a specific property (respectively). The difference between constraints and penalties, strictly speaking, is constraints are used to rule out potential solutions that don’t exhibit a specific property. For instance, constraints can be used to ensure that specific planning units are selected in the solution for prioritization (using add_locked_in_constraints) or not selected in the solution for prioritization (using add_locked_out_constraints). On the other hand, penalties are combined with the objective of a problem, with a penalty factor, and the overall objective of the problem then becomes to minimize (or maximize) the primary objective function and the penalty function. For example, penalties can be added to a problem to penalize solutions that are excessively fragmented (using add_boundary_penalties). These penalties have a penalty argument that specifies the relative importance of having spatially clustered solutions. When the argument to penalty is high, then solutions which are less fragmented are valued more highly—even if they cost more—and when the argument to penalty is low, then the solutions which are more fragmented are valued less highly.

    -

    After building a conservation problem, it can then be solved to obtain a solution (or portfolio of solutions if desired). The solution is returned in the same format as the planning unit data used to construct the problem. This means that if raster or shapefile / vector data was used when initializing the problem, then the solution will also be in raster or shapefile / vector data. This can be very helpful when it comes to interpreting and visualizing solutions because it means that the solution data does not first have to be merged with spatial data before they can be plotted on a map.

    -
    -
    -

    Usage

    -

    Here we will provide an introduction to using the prioritizr R package to build and solve a conservation planning problem. Please note that we will not discuss conservation planning with multiple zones in this vignette, for more information on working with multiple management zones please see the zones vignette.

    -

    First, we will load the prioritizr package.

    -
    # load package
    -library(prioritizr)
    -
    -# set default options for printing tabular data
    -options(tibble.width = Inf)
    -
    -

    Data

    -

    Now we will load some built-in data sets that are distributed with the prioritizr R package. This package contains several different planning unit data sets. To provide a comprehensive overview of the different ways that we can initialize a conservation planning problem, we will load each of them.

    -

    First, we will load the raster planning unit data (sim_pu_raster). Here, the planning units are represented as a raster (i.e. a RasterLayer object) and each pixel corresponds to the spatial extent of each panning unit. The pixel values correspond to the acquisition costs of each planning unit.

    -
    # load raster planning unit data
    -data(sim_pu_raster)
    -
    -# print description of the data
    -print(sim_pu_raster)
    -
    ## class      : RasterLayer 
    -## dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -## resolution : 0.1, 0.1  (x, y)
    -## extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -## crs        : NA 
    -## source     : memory
    -## names      : layer 
    -## values     : 190.1328, 215.8638  (min, max)
    -
    # plot the data
    -plot(sim_pu_raster)
    -

    -

    Secondly, we will load one of the spatial vector planning unit data sets (sim_pu_polygons). Here, each polygon (i.e. feature using ArcGIS terminology) corresponds to a different planning unit. This data set has an attribute table that contains additional information about each polygon. Namely, the cost field (column) in the attribute table contains the acquisition cost for each planning unit.

    -
    # load polygon planning unit data
    -data(sim_pu_polygons)
    -
    -# print first six rows of attribute table
    -head(sim_pu_polygons@data)
    -
    ##       cost locked_in locked_out
    -## 1 215.8638     FALSE      FALSE
    -## 2 212.7823     FALSE      FALSE
    -## 3 207.4962     FALSE      FALSE
    -## 4 208.9322     FALSE       TRUE
    -## 5 214.0419     FALSE      FALSE
    -## 6 213.7636     FALSE      FALSE
    -
    # plot the planning units
    -spplot(sim_pu_polygons, zcol = "cost")
    -

    -

    Thirdly, we will load some planning unit data stored in tabular format (i.e. data.frame format). For those familiar with Marxan or dealing with very large conservation planning problems (> 10 million planning units), it may be useful to work with data in this format because it does not contain any spatial information which will reduce computational burden. When using tabular data to initialize conservation planning problems, the data must follow the conventions used by Marxan. Specifically, each row in the planning unit table must correspond to a different planning unit. The table must also have an “id” column to provide a unique integer identifier for each planning unit, and it must also have a column that indicates the cost of each planning unit. For more information, please see the official Marxan documentation.

    -
    # specify file path for planning unit data
    -pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr")
    -
    -# load in the tabular planning unit data
    -# note that we use the data.table::fread function, as opposed to the read.csv
    -# function, because it is much faster
    -pu_dat <- data.table::fread(pu_path, data.table = FALSE)
    -
    -# preview first six rows of the tabular planning unit data
    -# note that it has some extra columns other than id and cost as per the
    -# Marxan format
    -head(pu_dat)
    -
    ##   id       cost status    xloc     yloc
    -## 1  3        0.0      0 1116623 -4493479
    -## 2 30   752727.5      3 1110623 -4496943
    -## 3 56  3734907.5      0 1092623 -4500408
    -## 4 58  1695902.1      0 1116623 -4500408
    -## 5 84  3422025.6      0 1098623 -4503872
    -## 6 85 17890758.4      0 1110623 -4503872
    -

    Finally, we will load data showing the spatial distribution of the conservation features. Our conservation features (sim_features) are represented as a stack of raster objects (i.e. a RasterStack object) where each layer corresponds to a different feature (e.g. a multi-band GeoTIFF where each band corresponds to a different feature). The pixel values in each layer correspond to the amount of suitable habitat available in a given planning unit. Note that our planning unit raster layer and our conservation feature stack have exactly the same spatial properties (i.e. resolution, extent, coordinate reference system) so their pixels line up perfectly.

    -
    # load feature data
    -data(sim_features)
    -
    -# plot the distribution of suitable habitat for each feature
    -plot(sim_features, main = paste("Feature", seq_len(nlayers(sim_features))),
    -     nr = 2, box = FALSE, axes = FALSE)
    -

    -
    -
    -

    Initialize a problem

    -

    After having loaded our planning unit and feature data, we will now try initializing the some conservation planning problems. There are a lot of different ways to initialize a conservation planning problem, so here we will just showcase a few of the more commonly used methods. For an exhaustive description of all the ways you can initialize a conservation problem, see the help file for the problem function (which you can open using the code ?problem). First off, we will initialize a conservation planning problem using the raster data.

    -
    # create problem
    -p1 <- problem(sim_pu_raster, sim_features)
    -
    -# print problem
    -print(p1)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      none
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
    # print number of planning units
    -number_of_planning_units(p1)
    -
    ## [1] 90
    -
    # print number of features
    -number_of_features(p1)
    -
    ## [1] 5
    -

    Generally, we recommend initializing problems using raster data where possible. This is because the problem function needs to calculate the amount of each feature in each planning unit, and by providing both the planning unit and feature data in raster format with the same spatial resolution, extents, and coordinate systems, this means that the problem function does not need to do any geo-processing behind the scenes. But sometimes we can’t use raster planning unit data because our planning units aren’t equal-sized grid cells. So, below is an example showing how we can initialize a conservation planning problem using planning units that are formatted as spatial vector data. Note that if we had pre-computed the amount of each feature in each planning unit and stored the data in the attribute table, we could pass in the names of the columns as an argument to the problem function.

    -
    # create problem with spatial vector data
    -# note that we have to specify which column in the attribute table contains
    -# the cost data
    -p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost")
    -
    -# print problem
    -print(p2)
    -
    ## Conservation Problem
    -##   planning units: SpatialPolygonsDataFrame (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      none
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -

    We can also initialize a conservation planning problem using tabular planning unit data (i.e. a data.frame). Since the tabular planning unit data does not contain any spatial information, we also have to provide the feature data in tabular format (i.e. a data.frame) and data showing the amount of each feature in each planning unit in tabular format (i.e. a data.frame). The feature data must have an “id” column containing a unique integer identifier for each feature, and the planning unit by feature data must contain the following three columns: “pu” corresponding to the planning unit identifiers, “species” corresponding to the feature identifiers, and “amount” showing the amount of a given feature in a given planning unit.

    -
    # set file path for feature data
    -spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr")
    -
    -# load in feature data
    -spec_dat <- data.table::fread(spec_path, data.table = FALSE)
    -
    -# print first six rows of the data
    -# note that it contains extra columns
    -head(spec_dat)
    -
    ##   id prop spf   name
    -## 1 10  0.3   1  bird1
    -## 2 11  0.3   1  nvis2
    -## 3 12  0.3   1  nvis8
    -## 4 13  0.3   1  nvis9
    -## 5 14  0.3   1 nvis14
    -## 6 15  0.3   1 nvis20
    -
    # set file path for planning unit vs. feature data
    -puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr")
    -
    -# load in planning unit vs feature data
    -puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE)
    -
    -# print first six rows of the data
    -head(puvspr_dat)
    -
    ##   species  pu     amount
    -## 1      26  56 1203448.84
    -## 2      26  58  451670.10
    -## 3      26  84  680473.75
    -## 4      26  85   97356.24
    -## 5      26  86   78034.76
    -## 6      26 111 4783274.17
    -
    # create problem
    -p3 <- problem(pu_dat, spec_dat, cost_column = "cost", rij = puvspr_dat)
    -
    -# print problem
    -print(p3)
    -
    ## Conservation Problem
    -##   planning units: data.frame (1751 units)
    -##   cost:           min: 0, max: 41569219.38232
    -##   features:       bird1, nvis2, nvis8, ... (17 features)
    -##   objective:      none
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -

    For more information on initializing problems, please see the help page for the problem function (which you can open using the code ?problem). Now that we have initialized a conservation planning problem, we will show you how you can customize it to suit the exact needs of your conservation planning scenario. Although we initialized the conservation planning problems using several different methods, moving forward, we will only use raster-based planning unit data to keep things simple.

    -
    -
    -

    Add an objective

    -

    The next step is to add an objective to the problem. A problem objective is used to specify the primary goal of the problem (i.e. the quantity that is to be maximized or minimized). All conservation planning problems involve minimizing or maximizing some kind of objective. For instance, we might require a solution that conserves enough habitat for each species while minimizing the overall cost of the reserve network. Alternatively, we might require a solution that maximizes the number of conserved species while ensuring that the cost of the reserve network does not exceed the budget. Please note that objectives are added in the same way regardless of the type of data used to initialize the problem.

    -

    The prioritizr R package supports a variety of different objective functions.

    -
      -
    • Minimum set objective: Minimize the cost of the solution whilst ensuring that all targets are met (Rodrigues et al. 2000). This objective is similar to that used in Marxan (Ball et al. 2009). For example, we can add a minimum set objective to a problem using the following code.
    • -
    -
    # create a new problem that has the minimum set objective
    -p3 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective()
    -
    -# print the problem
    -print(p3)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Maximum cover objective: Represent at least one instance of as many features as possible within a given budget (Church et al. 1996).
    • -
    -
    # create a new problem that has the maximum coverage objective and a budget
    -# of 5000
    -p4 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_cover_objective(5000)
    -
    -# print the problem
    -print(p4)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Maximum coverage objective [budget (5000)]
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Maximum features objective: Fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget (inspired by Cabeza & Moilanen 2001). This object is similar to the maximum cover objective except that we have the option of later specifying targets for each feature. In practice, this objective is more useful than the maximum cover objective because features often require a certain amount of area for them to persist and simply capturing a single instance of habitat for each feature is generally unlikely to enhance their long-term persistence.
    • -
    -
    # create a new problem that has the maximum features objective and a budget
    -# of 5000
    -p5 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_features_objective(budget = 5000)
    -
    -# print the problem
    -print(p5)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Maximum representation objective [budget (5000)]
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Minimum shortfall objective: Minimize the overall (weighted sum) shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when there is a large amount of left-over budget when using the maximum feature representation objective and the remaining funds need to be allocated to places that will enhance the representation of features with unmet targets.
    • -
    -
    # create a new problem that has the minimum shortfall objective and a budget
    -# of 5000
    -p6 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_shortfall_objective(budget = 5000)
    -
    -# print the problem
    -print(p6)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum shortfall objective [budget (5000)]
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Minimum largest shortfall objective: Minimize the largest (maximum) shortfall while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when the minimum shortfall objective returns solutions that focus too much on representing a small number of features (e.g. because they occur in much cheaper planning units), and solutions are needed to spread conservation effort out more evenly amongst all features—even if it means that all features will have (relatively) poor representation.
    • -
    -
    # create a new problem that has the minimum largest shortfall objective and a
    -# budget of 5000
    -p7 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_largest_shortfall_objective(budget = 5000)
    -
    -# print the problem
    -print(p7)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum largest shortfall objective [budget (5000)]
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Maximum phylogenetic diversity objective: Maximize the phylogenetic diversity of the features represented in the solution subject to a budget (inspired by Faith 1992; Rodrigues & Gaston 2002). This objective is similar to the maximum features objective except that emphasis is placed on protecting features which are associated with a diverse range of evolutionary histories. The prioritizr R package contains a simulated phylogeny that can be used with the simulated feature data (sim_phylogny).
    • -
    -
    # load simulated phylogeny data
    -data(sim_phylogeny)
    -
    -# create a new problem that has the maximum phylogenetic diversity
    -# objective and a budget of 5000
    -p8 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_phylo_div_objective(budget = 5000, tree = sim_phylogeny)
    -
    -# print the problem
    -print(p8)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Phylogenetic diversity objective [budget (5000)]
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Maximum phylogenetic endemism objective: Maximize the phylogenetic endemism of the features represented in the solution subject to a budget (inspired by Faith 1992; Rodrigues & Gaston 2002; Rosauer et al. 2009). This objective is similar to the maximum phylogenetic diversity except that emphasis is placed conserving features that are associated with geographically restricted periods of evolutionary history rather than a diverse range of evolutionary histories.
    • -
    -
    # load simulated phylogeny data
    -data(sim_phylogeny)
    -
    -# create a new problem that has the maximum phylogenetic diversity
    -# objective and a budget of 5000
    -p9 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_phylo_end_objective(budget = 5000, tree = sim_phylogeny)
    -
    -# print the problem
    -print(p9)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Phylogenetic endemism objective [budget (5000)]
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Maximum utility objective: Secure as much of the features as possible without exceeding a budget. This objective is functionally equivalent to selecting the planning units with the greatest amounts of each feature (e.g. species richness). Generally, we don’t encourage the use of this objective because it will only rarely identify complementary solutions—solutions which adequately conserve a range of different features—except perhaps to explore trade-offs or provide a baseline solution with which to compare other solutions.
    • -
    -
    # create a new problem that has the maximum utility objective and a budget
    -# of 5000
    -p10 <- problem(sim_pu_raster, sim_features) %>%
    -      add_max_utility_objective(budget = 5000)
    -
    -# print the problem
    -print(p10)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Maximum utility objective [budget (5000)]
    -##   targets:        none
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
    -
    -

    Add targets

    -

    Most conservation planning problems require targets. Targets are used to specify the minimum amount or proportion of a feature’s distribution that needs to be protected in the solution. For example, we may want to develop a reserve network that will secure 20% of the distribution for each feature for minimal cost.

    -

    There are four ways for specifying targets in the prioritizr R package:

    -
      -
    • Absolute targets: Targets are expressed as the total amount of each feature in the study area that need to be secured. For example, if we had binary feature data that showed the absence or presence of suitable habitat across the study area, we could set an absolute target as 5 to mean that we require 5 planning units with suitable habitat in the solution.
    • -
    -
    # create a problem with targets which specify that the solution must conserve
    -# a need a sum total of 3 units of suitable habitat for each feature
    -p11 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_absolute_targets(3)
    -
    -# print problem
    -print(p11)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Absolute targets [targets (min: 3, max: 3)]
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Relative targets: Targets are set as a proportion (between 0 and 1) of the total amount of each feature in the study area. For example, if we had binary feature data and the feature occupied a total of 20 planning units in the study area, we could set a relative target of 50 % to specify that the solution must secure 10 planning units for the feature. We could alternatively specify an absolute target of 10 to achieve the same result, but sometimes proportions are easier to work with.
    • -
    -
    # create a problem with the minimum set objective and relative targets of 10 %
    -# for each feature
    -p12 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1)
    -
    -# print problem
    -print(p12)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
    # create a problem with targets which specify that we need 10 % of the habitat
    -# for the first feature, 15 % for the second feature, 20 % for the third feature
    -# 25 % for the fourth feature and 30 % of the habitat for the fifth feature
    -targets <- c(0.1, 0.15, 0.2, 0.25, 0.3)
    -p13 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(targets)
    -
    -# print problem
    -print(p13)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.3)]
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Log-linear targets: Targets are expressed using scaling factors and log-linear interpolation. This method for specifying targets is commonly used for global prioritization analyses (Rodrigues et al. 2004).
    • -
    -
    # create problem with added log-linear targets
    -p14 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_loglinear_targets(10, 0.9, 100, 0.2)
    -
    -# print problem
    -print(p14)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Absolute targets [targets (min: 17.290505409161, max: 21.5906174426385)]
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Manual targets: Targets are manually specified. This is only really recommended for advanced users or problems that involve multiple management zones. See the zones vignette for more information on these targets.
    • -
    -

    As with the functions for specifying the objective of a problem, if we try adding multiple targets to a problem, only the most recently added set of targets are used.

    -
    -
    -

    Add constraints

    -

    A constraint can be added to a conservation planning problem to ensure that all solutions exhibit a specific property. For example, they can be used to make sure that all solutions select a specific planning unit or that all selected planning units in the solution follow a certain configuration.

    -

    The following constraints can be added to conservation planning problems in the prioritizr R package.

    -
      -
    • Locked in constraints: Add constraints to ensure that certain planning units are prioritized in the solution. For example, it may be desirable to lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network.
    • -
    -
    # create problem with constraints which specify that the first planning unit
    -# must be selected in the solution
    -p15 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_locked_in_constraints(1)
    -
    -# print problem
    -print(p15)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Locked in planning units [1 locked units]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Locked out constraints: Add constraints to ensure that certain planning units are not prioritized in the solution. For example, it may be useful to lock out planning units that have been degraded and are not suitable for conserving species.
    • -
    -
    # create problem with constraints which specify that the second planning unit
    -# must not be selected in the solution
    -p16 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_locked_out_constraints(2)
    -
    -# print problem
    -print(p16)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Locked out planning units [1 locked units]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Neighbor constraints: Add constraints to a conservation problem to ensure that all selected planning units have at least a certain number of neighbors.
    • -
    -
    # create problem with constraints which specify that all selected planning units
    -# in the solution must have at least 1 neighbor
    -p17 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_neighbor_constraints(1)
    -
    -# print problem
    -print(p17)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Neighbor constraint [number of neighbors (1), zones]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Contiguity constraints: Add constraints to a conservation problem to ensure that all selected planning units are spatially connected to each other and form spatially contiguous unit.
    • -
    -
    # create problem with constraints which specify that all selected planning units
    -# in the solution must form a single contiguous unit
    -p18 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_contiguity_constraints()
    -
    -# print problem
    -print(p18)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Contiguity constraints [apply constraints? (1), zones]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Feature contiguity constraints: Add constraints to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of those implemented in the add_contiguity_constraints function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit.
    • -
    -
    # create problem with constraints which specify that the planning units used
    -# to conserve each feature must form a contiguous unit
    -p19 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_feature_contiguity_constraints()
    -
    -# print problem
    -print(p19)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Feature contiguity constraints [apply constraints? (1), layer.1 zones, layer.2 zones, layer.3 zones, layer.4 zones, layer.5 zones]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Linear constraints: Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study region (e.g. different countries).
    • -
    -
    # create problem with constraints which specify that the sum of
    -# values in sim_features[[1]] among selected planning units must not exceed a
    -#' threshold value of 190.
    -p20 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_shortfall_objective(budget = 1800) %>%
    -       add_relative_targets(0.1) %>%
    -       add_linear_constraints(190, "<=", sim_features[[1]])
    -
    -# print problem
    -print(p20)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum shortfall objective [budget (1800)]
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Linear constraints [threshold (190)]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Mandatory allocation constraints: Add constraints to ensure that every planning unit is allocated to a management zone in the solution. Please note that this function can only be used with problems that contain multiple zones. For more information on problems with multiple zones and an example using this function, see the Management Zones vignette.
    • -
    -

    In particular, The add_locked_in_constraints and add_locked_out_constraints functions are incredibly useful for real-world conservation planning exercises, so it’s worth pointing out that there are several ways we can specify which planning units should be locked in or out of the solutions. If we use raster planning unit data, we can also use raster data to specify which planning units should be locked in our locked out.

    -
    # load data to lock in or lock out planning units
    -data(sim_locked_in_raster)
    -data(sim_locked_out_raster)
    -
    -# plot the locked data
    -plot(stack(sim_locked_in_raster, sim_locked_out_raster),
    -     main = c("Locked In", "Locked Out"))
    -

    -
    # create a problem using raster planning unit data and use the locked raster
    -# data to lock in some planning units and lock out some other planning units
    -p21 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_locked_in_constraints(sim_locked_in_raster) %>%
    -       add_locked_out_constraints(sim_locked_out_raster)
    -
    -# print problem
    -print(p21)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Locked out planning units [10 locked units]
    -##                    Locked in planning units [10 locked units]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -

    If our planning unit data are in a spatial vector format (similar to the sim_pu_polygons data) or a tabular format (similar to pu_dat), we can use the field names in the data to refer to which planning units should be locked in and / or out. For example, the sim_pu_polygons object has TRUE / FALSE values in the “locked_in” field which indicate which planning units should be selected in the solution. We could use the data in this field to specify that those planning units with TRUE values should be locked in using the following methods.

    -
    # preview first six rows of the attribute table for sim_pu_polygons
    -head(sim_pu_polygons@data)
    -
    ##       cost locked_in locked_out
    -## 1 215.8638     FALSE      FALSE
    -## 2 212.7823     FALSE      FALSE
    -## 3 207.4962     FALSE      FALSE
    -## 4 208.9322     FALSE       TRUE
    -## 5 214.0419     FALSE      FALSE
    -## 6 213.7636     FALSE      FALSE
    -
    # specify locked in data using the field name
    -p22 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_locked_in_constraints("locked_in")
    -
    -# print problem
    -print(p22)
    -
    ## Conservation Problem
    -##   planning units: SpatialPolygonsDataFrame (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Locked in planning units [10 locked units]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
    # specify locked in data using the values in the field
    -p23 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_locked_in_constraints(which(sim_pu_polygons$locked_in))
    -
    -# print problem
    -print(p23)
    -
    ## Conservation Problem
    -##   planning units: SpatialPolygonsDataFrame (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <Locked in planning units [10 locked units]>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
    -
    -

    Add penalties

    -

    We can also add penalties to a problem to favor or penalize solutions according to a secondary objective. Unlike the constraint functions, these functions will add extra information to the objective function of the optimization function to penalize solutions that do not exhibit specific characteristics. For example, penalties can be added to a problem to avoid highly fragmented solutions at the expense of accepting slightly more expensive solutions. All penalty functions have a penalty argument that controls the relative importance of the secondary penalty function compared to the primary objective function. It is worth noting that incredibly low or incredibly high penalty values—relative to the main objective function—can cause problems to take a very long time to solve, so when trying out a range of different penalty values it can be helpful to limit the solver to run for a set period of time.

    -

    The prioritizr R package currently offers only two methods for adding penalties to a conservation planning problem.

    -
      -
    • Boundary penalties: Add penalties to penalize solutions that are excessively fragmented. These penalties are similar to those used in Marxan (Ball et al. 2009; Beyer et al. 2016).
    • -
    -
    # create problem with penalties that penalize fragmented solutions with a
    -# penalty factor of 0.01
    -p24 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_boundary_penalties(penalty = 0.01)
    -
    -# print problem
    -print(p24)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (0.01), zones]>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Connectivity penalties: Add penalties to favor solutions that select combinations of planning units with high connectivity between them. These penalties are similar to those used in Marxan with Zones (Watts et al. 2009; Beyer et al. 2016). This function supports both symmetric and asymmetric connectivities among planning units.
    • -
    -
    # create problem with penalties that favor combinations of planning units with
    -# high connectivity, here we will use only the first four layers in
    -# sim_features for the features and we will use the fifth layer in sim_features
    -# to represent the connectivity data, where the connectivity_matrix function
    -# will create a matrix showing the average strength of connectivity between
    -# adjacent planning units using the data in the fifth layer of sim_features
    -p25 <- problem(sim_pu_raster, sim_features[[1:4]]) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_boundary_penalties(
    -         penalty = 5,
    -         data = connectivity_matrix(sim_pu_raster, sim_features[[5]]))
    -
    -# print problem
    -print(p25)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, layer.4 (4 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (5), zones]>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Linear penalties: Add penalties to penalize solutions that select planning units according to a certain variable (e.g. anthropogenic pressure).
    • -
    -
    # create data for penalizing planning units
    -# (note this requires the RandomFields package to be installed)
    -pen_raster <- simulate_cost(sim_pu_raster)
    -
    -# create problem with penalties that penalize solutions that select
    -# planning units with high values in the pen_raster object,
    -# here we will use a penalty value of 5 to indicate the trade-off (scaling)
    -# between the penalty values (in the sim_pu_raster) and the main objective
    -# (i.e. the cost of the solution)
    -p26 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_linear_penalties(penalty = 5, data = pen_raster)
    -
    -# print problem
    -print(p26)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      default
    -##   constraints:    <none>
    -##   penalties:      <Linear penalties [penalty (5)]>
    -##   portfolio:      default
    -##   solver:         default
    -
    -
    -

    Add the decision types

    -

    Conservation planning problems involve making decisions on how planning units will be managed. These decisions are then associated with management actions (e.g. turning a planning unit into a protected area). The type of decision describes how the action is applied to planning units. For instance, the default decision-type is a binary decision type, meaning that we are either selecting or not selecting planning units for management.

    -

    The prioritizr R package currently offers the following types of decisions for customizing problems.

    -
      -
    • Binary decisions: Add a binary decision to a conservation planning problem. This is the classic decision of either prioritizing or not prioritizing a planning unit. Typically, this decision has the assumed action of buying the planning unit to include in a protected area network. If no decision is added to a problem object, then this decision class will be used by default.
    • -
    -
    # add binary decisions to a problem
    -p27 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions()
    -
    -# print problem
    -print(p27)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +

    The aim of this tutorial is to provide a short introduction to the prioritizr R package. It is also intended for helping conservation planners familiar the Marxan decision support tool (Ball et al. 2009) to start using the package for their work.

    +
    +
    +

    Data

    +

    Let’s load the packages and data used in this tutorial. Since this tutorial uses data from the prioritizrdata R package, please ensure that it is installed. The data used in this tutorial were obtained from the Introduction to Marxan course. Specifically, the data were originally a subset of a larger spatial prioritization project performed under contract to Australia’s Department of Environment and Water Resources (Klein et al. 2007).

    +
    # load packages
    +library(prioritizrdata)
    +library(prioritizr)
    +library(vegan)
    +library(cluster)
    +
    +# load planning unit data
    +data(tas_pu)
    +
    +# load feature data
    +data(tas_features)
    +

    The tas_pu object contains planning units represented as spatial polygons (i.e., a SpatialPolygonsDataFrame object). This object has three columns that denote the following information for each planning unit: a unique identifier (id), unimproved land value (cost), and current conservation status (locked_in). Planning units that have at least half of their area overlapping with existing protected areas are denoted with a locked in value of 1, otherwise they are denoted with a value of 0. If you are familiar with the Marxan decision support tool, then you will notice that some of these columns are formatted similar conventions.

    +

    Now, let’s have a look at the planning unit data. We can see that the planning units correspond to hexagonal land parcels. We can also see that is a clear spatial pattern in the cost and conservation status the planning units.

    +
    # print planning unit data
    +print(tas_pu)
    +
    ## class       : SpatialPolygonsDataFrame 
    +## features    : 1130 
    +## extent      : 298809.6, 613818.8, 5167775, 5502544  (xmin, xmax, ymin, ymax)
    +## crs         : +proj=utm +zone=55 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## variables   : 5
    +## names       :   id,              cost, status, locked_in, locked_out 
    +## min values  :    1, 0.192488262910798,      0,         0,          0 
    +## max values  : 1130,  61.9272727272727,      2,         1,          0
    +
    # plot map of planning unit costs
    +plot(st_as_sf(tas_pu[, "cost"]), main = "Planning unit costs")
    +

    +
    # plot map of planning unit coverage by protected areas
    +plot(st_as_sf(tas_pu[, "locked_in"]), main = "Protected area coverage")
    +

    +

    The tas_features object describes the spatial distribution of the features. Specifically, the feature data are expressed as a stack of 62 rasters (i.e., a RasterStack object). Each layer in the stack corresponds to one of 62 different vegetation communities. To describe the spatial distribution of a given vegetation community, each layer contains a spatially referenced grid of rectangular cells and each of these grid cells is associated with information on the distribution of the a given vegetation community. Specifically, these grid cells are assigned values that indicate if a given vegetation community is present (using value of 1) or absent (using value of 0) within the spatial extent of each grid cell.

    +

    Next, let’s examine the feature data. Here we will only plot the first four features as an example. The pixel values denote the presence (denoted by a value of 1) or absence (denoted by a value of zero) of each feature within the extent of the study area.

    +
    # print planning unit data
    +print(tas_features)
    +
    ## class      : RasterStack 
    +## dimensions : 398, 359, 142882, 62  (nrow, ncol, ncell, nlayers)
    +## resolution : 1000, 1000  (x, y)
    +## extent     : 288801.7, 647801.7, 5142976, 5540976  (xmin, xmax, ymin, ymax)
    +## crs        : +proj=utm +zone=55 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## names      : tas_features.1, tas_features.2, tas_features.3, tas_features.4, tas_features.5, tas_features.6, tas_features.7, tas_features.8, tas_features.9, tas_features.10, tas_features.11, tas_features.12, tas_features.13, tas_features.14, tas_features.15, ... 
    +## min values :              0,              0,              0,              0,              0,              0,              0,              0,              0,               0,               0,               0,               0,               0,               0, ... 
    +## max values :              1,              1,              1,              1,              1,              1,              1,              1,              1,               1,               1,               1,               1,               1,               1, ...
    +
    # plot map of the first four vegetation classes
    +plot(tas_features[[1:4]], main = paste("Feature", 1:4))
    +

    +

    The planning units in this tutorial are stored as spatial polygons. Although spatial polygons provide considerable flexibility in the shape and size of the planning units, such flexibility comes at a cost. This is because the spatial data processing routines needed to combine spatial polygon data and raster data for optimization can be very computationally expensive (e.g., calculating zonal statistics). As a consequence, we generally recommend using raster-based planning unit data where possible to reduce processing time. Another strategy is to complete spatial data processing routines manually using other software (e.g., ESRI ArcGIS) and use the pre-processed data directly with the prioritizr R package.

    +
    +
    +

    Problem formulation

    +

    Now we will formulate a conservation planing problem. To achieve this, we first specify which objects contain the planning unit and feature data (using the problem() function). Next, we specify that we want to use the minimum set objective function (using the add_min_set_objective() function). This objective function indicates that we wish to minimize the total cost of planning units selected by the prioritization. We then specify boundary penalties reduce spatial fragmentation in the resulting prioritization (using the add_boundary_penalties() function; see the Calibrating trade-offs vignette for details on calibrating the penalty value). We also specify representation targets to ensure the resulting prioritization provides adequate coverage of each vegetation community (using the add_relative_targets() function). Specifically, we specify targets to ensure at least 17% of the spatial extent of each vegetation community (based on the Aichi Target 11). Additionally, we set constraints to ensure that planning units predominately covered by existing protected areas are selected by the prioritization (using the add_locked_in_constraints() function). Finally, we specify that the prioritization should either select – or not select – planning units for prioritization (using the add_binary_decisions() function).

    +
    # build problem
    +p1 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_boundary_penalties(penalty = 0.005) %>%
    +      add_relative_targets(0.17) %>%
    +      add_locked_in_constraints("locked_in") %>%
    +      add_binary_decisions()
    +
    +# print the problem
    +print(p1)
    +
    ## Conservation Problem
    +##   planning units: SpatialPolygonsDataFrame (1130 units)
    +##   cost:           min: 0.19249, max: 61.92727
    +##   features:       tas_features.1, tas_features.2, tas_features.3, ... (62 features)
     ##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
     ##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Proportion decisions: Add a proportion decision to a problem. This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the default of the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in a protected area network. Generally, problems can be solved much faster with proportion-type decisions than binary-type decisions, so they can be very useful when commercial solvers are not available.
    • -
    -
    # add proportion decisions to a problem
    -p28 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_proportion_decisions()
    -
    -# print problem
    -print(p28)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Proportion decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         default
    -
      -
    • Semi-continuous decisions: Add a semi-continuous decision to a problem. This decision is similar to proportion decisions except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction (e.g. 80%) of a planning unit can be purchased. This type of decision may be useful when it is not practical to conserve the entire area indicated by a planning unit.
    • -
    -
    # add semi-continuous decisions to a problem, where we can only manage at most
    -# 50 % of the area encompassed by a planning unit
    -p29 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_semicontinuous_decisions(0.5)
    -
    -# print problem
    -print(p29)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Semicontinuous decision [upper limit (0.5)]
    -##   constraints:    <none>
    -##   penalties:      <none>
    +##   constraints:    <Locked in planning units [257 locked units]>
    +##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (0.005), zones]>
     ##   portfolio:      default
     ##   solver:         default
    -
    -

    Add a solver

    -

    Next, after specifying the mathematical formulation that underpins your conservation planning problem, you can specify how the problem should be solved. If you do not specify this information, the prioritizr R package will automatically use the best solver currently installed on your system with some reasonable defaults. We strongly recommend installing the Gurobi software suite and the gurobi R package to solve problems, and for more information on this topic please refer to the Gurobi Installation Guide.

    -

    Currently, the prioritizr R package supports five different solvers.

    -
      -
    • Gurobi solver: Gurobi is a state of the art commercial optimization software. It is by far the fastest of the solvers that can be used to solve conservation problems. However, it is not freely available. That said, special licenses are available to academics at no cost.
    • -
    -
    # create a problem and specify that Gurobi should be used to solve the problem
    -# and specify an optimality gap of zero to obtain the optimal solution
    -p30 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_gurobi_solver(gap = 0)
    -
    -# print problem
    -print(p30)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         Gurobi [first_feasible (0), gap (0), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (1)]
    -
      -
    • IBM CPLEX solver: IBM CPLEX is a commercial optimization software. It is faster than the open source solvers available for generating prioritizations (see below), however, it is not freely available. Similar to the Gurobi software, special licenses are available to academics at no cost.
    • -
    -
    # create a problem and specify that IBM CPLEX should be used to solve the
    -# problem and specify an optimality gap of zero to obtain the optimal solution
    -p31 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_cplex_solver(gap = 0)
    -
    -# print problem
    -print(p31)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         CPLEX [gap (0), presolve (1), threads (1), time_limit (2147483647), verbose (1)]
    -
      -
    • CBC solver: CBC is an open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks have yet to be completed, preliminary analyses suggest that it is the fastest open-source solver available for generating prioritizations. It requires the rcbc R package, which is currently only available on GitHub.
    • -
    -
    # create a problem and specify that CBC should be used to solve the
    -# problem and specify an optimality gap of zero to obtain the optimal solution
    -p32 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_cbc_solver(gap = 0)
    -
    -# print problem
    -print(p32)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         CBC [first_feasible (0), gap (0), presolve (1), threads (1), time_limit (2147483647), verbose (1)]
    -
      -
    • lpsymphony solver: SYMPHONY is an open-source integer programming solver that is also part of the COIN-OR project. This solver uses the lpsymphony R package (available on Bioconductor) to interface with the SYMPHONY software.
    • -
    -
    # create a problem and specify that lpsymphony should be used to solve the
    -# problem and specify an optimality gap of zero to obtain the optimal solution
    -p33 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_lpsymphony_solver(gap = 0)
    -
    -# print problem
    -print(p33)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         Lpsymphony [first_feasible (0), gap (0), time_limit (2147483647), verbose (1)]
    -
      -
    • Rsymphony solver: This solver provides a different interface to the SYMPHONY software. It uses the Rsymphony R package which is available on The Comprehensive R Archive Network (CRAN). This solver is generally slower than the other solvers, because it cannot use parallel processing.
    • -
    -
    # create a problem and specify that Rsymphony should be used to solve the
    -# problem and specify an optimality gap of zero to obtain the optimal solution
    -p34 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_rsymphony_solver(gap = 0)
    -
    -# print problem
    -print(p34)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         Rsymphony [first_feasible (0), gap (0), time_limit (2147483647), verbose (1)]
    -
    -
    -

    Add a portfolio

    -

    Many conservation planning exercises require a portfolio of solutions. For example, real-world exercises can involve presenting decision makers with a range of near-optimal decisions. Additionally, the number of times that different planning units are selected in different solutions can provide insight into their relative importance.

    -

    The following methods are available for generating a portfolio of solutions.

    -
      -
    • Extra portfolio: Generate a portfolio of solutions by storing feasible solutions found during the optimization process. Note that this method requires that the Gurobi optimization software is used to generate solutions.
    • -
    -
    # create a problem and specify that a portfolio should be created using
    -# extra solutions found while solving the problem
    -p35 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_extra_portfolio()
    -
    -# print problem
    -print(p35)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      Extra portfolio
    -##   solver:         default
    -
      -
    • Top portfolio: Generate a portfolio of solutions by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions). Note that this method requires that the Gurobi optimization software is used to generate solutions.
    • -
    -
    # create a problem and specify that a portfolio should be created using
    -# the top five solutions
    -p36 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_top_portfolio(number_solutions = 5)
    -
    -# print problem
    -print(p36)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      Top portfolio [number_solutions (5)]
    -##   solver:         default
    -
      -
    • Gap portfolio: Generate a portfolio of solutions by finding a certain number of solutions that are all within a pre-specified optimality gap. This method is especially useful for generating multiple solutions that can used be to calculate selection frequencies (similar to Marxan). Note that this method requires that the Gurobi optimization software is used to generate solutions.
    • -
    -
    # create a problem and specify that a portfolio should be created by
    -# finding five solutions within 10% of optimality
    -p37 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_gap_portfolio(number_solutions = 5, pool_gap = 0.2)
    -
    -# print problem
    -print(p37)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      Gap portfolio [number_solutions (5), pool_gap (0.2)]
    -##   solver:         default
    -
      -
    • Cuts portfolio: Generate a portfolio of distinct solutions within a pre-specified optimality gap. This method is only recommended if the Gurobi optimization solver is not available.
    • -
    -
    # create a problem and specify that a portfolio containing 10 solutions
    -# should be created using using Bender's cuts
    -p38 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_cuts_portfolio(number_solutions = 10)
    -
    -# print problem
    -print(p38)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      Cuts portfolio [number_solutions (10)]
    -##   solver:         default
    -
      -
    • Shuffle portfolio: Generate a portfolio of solutions by randomly reordering the data prior to attempting to solve the problem. If the Gurobi optimization solver is not available, this method is the fastest method for generating a set number of solutions within a specified distance from optimality.
    • -
    -
    # create a problem and specify a portfolio should be created that contains
    -# 10 solutions and that any duplicate solutions should not be removed
    -p39 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions() %>%
    -       add_shuffle_portfolio(number_solutions = 10, remove_duplicates = FALSE)
    -
    -# print problem
    -print(p39)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (90 units)
    -##   cost:           min: 190.13276, max: 215.86384
    -##   features:       layer.1, layer.2, layer.3, ... (5 features)
    +
    +

    Prioritization

    +

    We can now solve the problem formulation (p1) to generate a prioritization (using the solve() function). The prioritizr R package supports a range of different exact algorithm solvers, including Gurobi, IBM CPLEX, CBC, Rsymphony, and lpsymphony. Although there are benefits and limitations associated with each of these different solvers, they should return similar results. Note that you will need at least one solver installed on your system to generate prioritizations. Since we did not specify a solver when building the problem, the prioritizr R package will automatically select the best available solver installed. We recommend using the Gurobi solver if possible, and have used it for this tutorial (see the Gurobi Installation Guide vignette for installation instructions). After solving the problem, the prioritization will be stored in the solution_1 column of the s1 object.

    +
    # solve problem
    +s1 <- solve(p1)
    +
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
    +## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    +## Optimize a model with 6358 rows, 4278 columns and 14496 nonzeros
    +## Model fingerprint: 0xc7d1c688
    +## Variable types: 0 continuous, 4278 integer (4278 binary)
    +## Coefficient statistics:
    +##   Matrix range     [2e-06, 4e+01]
    +##   Objective range  [5e-01, 1e+03]
    +##   Bounds range     [1e+00, 1e+00]
    +##   RHS range        [1e-01, 1e+02]
    +## Found heuristic solution: objective 37703.827706
    +## Found heuristic solution: objective 27817.085072
    +## Presolve removed 2077 rows and 1375 columns
    +## Presolve time: 0.04s
    +## Presolved: 4281 rows, 2903 columns, 9594 nonzeros
    +## Found heuristic solution: objective 24454.499456
    +## Variable types: 0 continuous, 2903 integer (2903 binary)
    +## Found heuristic solution: objective 23413.987477
    +## Root relaxation presolved: 4281 rows, 2903 columns, 9594 nonzeros
    +## 
    +## 
    +## Root relaxation: objective 1.955605e+04, 1793 iterations, 0.09 seconds (0.19 work units)
    +## 
    +##     Nodes    |    Current Node    |     Objective Bounds      |     Work
    +##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    +## 
    +##      0     0 19556.0531    0 1615 23413.9875 19556.0531  16.5%     -    0s
    +## H    0     0                    23045.733138 19556.0531  15.1%     -    0s
    +## H    0     0                    22977.771015 19556.0531  14.9%     -    0s
    +##      0     0 19836.0586    0 1855 22977.7710 19836.0586  13.7%     -    0s
    +## H    0     0                    21702.297403 19836.0586  8.60%     -    0s
    +## 
    +## Cutting planes:
    +##   Gomory: 2
    +##   Cover: 5
    +##   MIR: 4
    +##   GUB cover: 1
    +##   Zero half: 2
    +##   RLT: 10
    +## 
    +## Explored 1 nodes (2393 simplex iterations) in 0.34 seconds (0.60 work units)
    +## Thread count was 1 (of 8 available processors)
    +## 
    +## Solution count 7: 21702.3 22977.8 23045.7 ... 37703.8
    +## 
    +## Optimal solution found (tolerance 1.00e-01)
    +## Best objective 2.170229740250e+04, best bound 1.983605855899e+04, gap 8.5993%
    +
    # plot map of prioritization
    +plot(st_as_sf(s1[, "solution_1"]), main = "Prioritization",
    +     pal = c("grey90", "darkgreen"))
    +

    +
    +
    +

    Feature representation

    +

    Let’s examine how well the vegetation communities are represented by existing protected areas and the prioritization.

    +
    # create column with existing protected areas
    +tas_pu$pa <- round(tas_pu$locked_in)
    +
    +# calculate feature representation statistics based on existing protected areas
    +tc_pa <- eval_target_coverage_summary(p1, tas_pu[, "pa"])
    +print(tc_pa)
    +
    ## # A tibble: 62 × 9
    +##    feature         met   total_amount absolute_target absolute_held
    +##    <chr>           <lgl>        <dbl>           <dbl>         <dbl>
    +##  1 tas_features.1  FALSE         33.9            5.77         0.556
    +##  2 tas_features.2  FALSE        170.            28.9         13.5  
    +##  3 tas_features.3  FALSE         24.0            4.08         2.00 
    +##  4 tas_features.4  FALSE         32.8            5.57         1.37 
    +##  5 tas_features.5  FALSE         24.8            4.21         0    
    +##  6 tas_features.6  FALSE         22.0            3.74         0    
    +##  7 tas_features.7  FALSE         16.4            2.78         0    
    +##  8 tas_features.8  FALSE         43.0            7.31         5.12 
    +##  9 tas_features.9  FALSE        388.            66.0         22.4  
    +## 10 tas_features.10 FALSE         14.5            2.47         0    
    +##    absolute_shortfall relative_target relative_held relative_shortfall
    +##                 <dbl>           <dbl>         <dbl>              <dbl>
    +##  1               5.21            0.17        0.0164             0.154 
    +##  2              15.4             0.17        0.0796             0.0904
    +##  3               2.08            0.17        0.0832             0.0868
    +##  4               4.19            0.17        0.0420             0.128 
    +##  5               4.21            0.17        0                  0.17  
    +##  6               3.74            0.17        0                  0.17  
    +##  7               2.78            0.17        0                  0.17  
    +##  8               2.19            0.17        0.119              0.0510
    +##  9              43.5             0.17        0.0578             0.112 
    +## 10               2.47            0.17        0                  0.17  
    +## # … with 52 more rows
    +
    # calculate  feature representation statistics based on the prioritization
    +tc_s1 <- eval_target_coverage_summary(p1, s1[, "solution_1"])
    +print(tc_s1)
    +
    ## # A tibble: 62 × 9
    +##    feature         met   total_amount absolute_target absolute_held
    +##    <chr>           <lgl>        <dbl>           <dbl>         <dbl>
    +##  1 tas_features.1  TRUE          33.9            5.77          5.97
    +##  2 tas_features.2  TRUE         170.            28.9          43.8 
    +##  3 tas_features.3  TRUE          24.0            4.08          4.25
    +##  4 tas_features.4  TRUE          32.8            5.57          6.37
    +##  5 tas_features.5  TRUE          24.8            4.21          4.98
    +##  6 tas_features.6  TRUE          22.0            3.74          7.00
    +##  7 tas_features.7  TRUE          16.4            2.78          3.00
    +##  8 tas_features.8  TRUE          43.0            7.31          8.12
    +##  9 tas_features.9  TRUE         388.            66.0          70.8 
    +## 10 tas_features.10 TRUE          14.5            2.47          2.91
    +##    absolute_shortfall relative_target relative_held relative_shortfall
    +##                 <dbl>           <dbl>         <dbl>              <dbl>
    +##  1                  0            0.17         0.176                  0
    +##  2                  0            0.17         0.258                  0
    +##  3                  0            0.17         0.177                  0
    +##  4                  0            0.17         0.195                  0
    +##  5                  0            0.17         0.201                  0
    +##  6                  0            0.17         0.318                  0
    +##  7                  0            0.17         0.183                  0
    +##  8                  0            0.17         0.189                  0
    +##  9                  0            0.17         0.182                  0
    +## 10                  0            0.17         0.200                  0
    +## # … with 52 more rows
    +
    # explore representation by existing protected areas
    +## calculate number of features adequately represented by existing protected
    +## areas
    +sum(tc_pa$met)
    +
    ## [1] 16
    +
    ## summarize representation (values show percent coverage)
    +summary(tc_pa$relative_held * 100)
    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   0.000   0.000   4.845  13.909  16.995  58.488
    +
    ## visualize representation  (values show percent coverage)
    +hist(tc_pa$relative_held * 100,
    +     main = "Feature representation by existing protected areas",
    +     xlim = c(0, 100),
    +     xlab = "Percent coverage of features (%)")
    +

    +
    # explore representation by prioritization
    +## summarize representation (values show percent coverage)
    +summary(tc_s1$relative_held * 100)
    +
    ##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    +##   17.14   20.18   25.56   33.83   43.19  100.00
    +
    ## calculate number of features adequately represented by the prioritization
    +sum(tc_s1$met)
    +
    ## [1] 62
    +
    ## visualize representation  (values show percent coverage)
    +hist(tc_s1$relative_held * 100,
    +     main = "Feature representation by prioritization",
    +     xlim = c(0, 100),
    +     xlab = "Percent coverage of features (%)")
    +

    +

    We can see that representation of the vegetation communities by existing protected areas is remarkably poor. For example, many of the vegetation communities have nearly zero coverage by existing protected areas. In other words, are almost entirely absent from existing protected areas. We can also see that all vegetation communities have at least 17% coverage by the prioritization – meaning that it meets the representation targets for all of the features.

    +
    +
    +

    Irreplaceability

    +

    After generating the prioritization, we can examine the relative importance of planning units selected by the prioritization. This can be useful to identify critically important planning units for conservation – in other words, places that contain biodiversity features which cannot be represented anywhere else – and schedule implementation of the prioritization. To achieve this, we will use the Ferrier metric (Ferrier et al. 2000).

    +
    # calculate irreplaceability
    +irrep_s1 <- eval_ferrier_importance(p1, s1["solution_1"])
    +print(irrep_s1)
    +
    ## class       : SpatialPolygonsDataFrame 
    +## features    : 1130 
    +## extent      : 298809.6, 613818.8, 5167775, 5502544  (xmin, xmax, ymin, ymax)
    +## crs         : +proj=utm +zone=55 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## variables   : 63
    +## names       :      tas_features.1,     tas_features.2,      tas_features.3,      tas_features.4,      tas_features.5,    tas_features.6,      tas_features.7,      tas_features.8,      tas_features.9,     tas_features.10,   tas_features.11,     tas_features.12,    tas_features.13,   tas_features.14,     tas_features.15, ... 
    +## min values  :                   0,                  0,                   0,                   0,                   0,                 0,                   0,                   0,                   0,                   0,                 0,                   0,                  0,                 0,                   0, ... 
    +## max values  : 0.00701698358267939, 0.0066775439067078, 0.00248592654648536, 0.00594796984735507, 0.00219848915242862, 0.221777713215686, 0.00352460472775105, 0.00362070014565918, 0.00736044135306282, 0.00691379490000091, 0.225419698234888, 0.00465456428213494, 0.0025318990590772, 0.010045386228309, 0.00609443943530227, ...
    +
    # manually coerce values for planning units not selected in prioritization
    +# to NA, so that they are shown in white
    +irrep_s1$plot_total <- irrep_s1$total
    +irrep_s1$plot_total[s1$solution_1 < 0.5] <- NA_real_
    +
    +# plot map of overall importance scores
    +plot(st_as_sf(irrep_s1[, "plot_total"]), main = "Overall importance")
    +

    +
    +
    +

    Portfolios

    +

    Conservation planning exercises often involve generating multiple different prioritizations. This can help decision makers consider different options, and provide starting points for building consensus among stakeholders. To generate a range of different prioritizations given the same problem formulation, we can use portfolio functions. Here we will use the gap portfolio to generate 1000 solutions that are within 30% of optimality. Please note that you will need to have the Gurobi solver installed to use this specific portfolio. If you don’thave access to Gurobi, you could try using the shuffle portfolio instead (using the add_shuffle_portfolio() function).

    +
    # create new problem with a portfolio added to it
    +p2 <- p1 %>%
    +      add_gap_portfolio(number_solutions = 1000, pool_gap = 0.2)
    +
    +# print problem
    +print(p2)
    +
    ## Conservation Problem
    +##   planning units: SpatialPolygonsDataFrame (1130 units)
    +##   cost:           min: 0.19249, max: 61.92727
    +##   features:       tas_features.1, tas_features.2, tas_features.3, ... (62 features)
     ##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
     ##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      Shuffle portfolio [number_solutions (10), remove_duplicates (0), threads (1)]
    +##   constraints:    <Locked in planning units [257 locked units]>
    +##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (0.005), zones]>
    +##   portfolio:      Gap portfolio [number_solutions (1000), pool_gap (0.2)]
     ##   solver:         default
    -
    -
    -

    Solve the problem

    -

    Finally, after formulating our conservation planning problem and specifying how the problem should be solved, we can use the solve function to obtain a solution. Note that the solver will typically print out some information describing the size of the problem and report its progress when searching for a suitable solution.

    -
    # formulate the problem
    -p40 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_boundary_penalties(penalty = 500, edge_factor = 0.5) %>%
    -       add_binary_decisions()
    -
    -# solve the problem (using the default solver)
    -s40 <- solve(p40)
    +
    # generate prioritizations
    +prt <- solve(p2)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    -## Optimize a model with 293 rows, 234 columns and 1026 nonzeros
    -## Model fingerprint: 0xbd38144b
    -## Variable types: 0 continuous, 234 integer (234 binary)
    +## Optimize a model with 6358 rows, 4278 columns and 14496 nonzeros
    +## Model fingerprint: 0xc7d1c688
    +## Variable types: 0 continuous, 4278 integer (4278 binary)
     ## Coefficient statistics:
    -##   Matrix range     [2e-01, 1e+00]
    -##   Objective range  [1e+02, 4e+02]
    +##   Matrix range     [2e-06, 4e+01]
    +##   Objective range  [5e-01, 1e+03]
     ##   Bounds range     [1e+00, 1e+00]
    -##   RHS range        [3e+00, 8e+00]
    -## Found heuristic solution: objective 20287.196992
    -## Found heuristic solution: objective 3087.9617505
    -## Presolve time: 0.00s
    -## Presolved: 293 rows, 234 columns, 1026 nonzeros
    -## Variable types: 0 continuous, 234 integer (234 binary)
    -## Root relaxation presolved: 293 rows, 234 columns, 1026 nonzeros
    +##   RHS range        [1e-01, 1e+02]
    +## Found heuristic solution: objective 37703.827706
    +## Found heuristic solution: objective 27817.085072
    +## Presolve removed 1434 rows and 258 columns
    +## Presolve time: 0.02s
    +## Presolved: 4924 rows, 4020 columns, 10889 nonzeros
    +## Variable types: 0 continuous, 4020 integer (4020 binary)
    +## Found heuristic solution: objective 27660.082384
    +## Root relaxation presolved: 4924 rows, 4020 columns, 10889 nonzeros
     ## 
     ## 
    -## Root relaxation: objective 2.265862e+03, 220 iterations, 0.00 seconds (0.00 work units)
    +## Root relaxation: objective 1.954728e+04, 1905 iterations, 0.10 seconds (0.18 work units)
     ## 
     ##     Nodes    |    Current Node    |     Objective Bounds      |     Work
     ##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
     ## 
    -##      0     0 2265.86242    0  234 3087.96175 2265.86242  26.6%     -    0s
    -##      0     0 2325.53449    0  232 3087.96175 2325.53449  24.7%     -    0s
    -##      0     0 2361.03063    0  223 3087.96175 2361.03063  23.5%     -    0s
    -##      0     0 2371.27288    0  222 3087.96175 2371.27288  23.2%     -    0s
    -##      0     0 2371.27288    0  222 3087.96175 2371.27288  23.2%     -    0s
    -## H    0     0                    2790.8740409 2371.27288  15.0%     -    0s
    -## H    0     0                    2730.9524342 2371.27288  13.2%     -    0s
    -##      0     2 2372.02804    0  222 2730.95243 2372.02804  13.1%     -    0s
    -## H    6     6                    2604.7426048 2375.46233  8.80%  21.0    0s
    +##      0     0 19547.2818    0 1731 27660.0824 19547.2818  29.3%     -    0s
    +## H    0     0                    26145.941445 19547.2818  25.2%     -    0s
    +## H    0     0                    24520.062189 19547.2818  20.3%     -    0s
    +##      0     0 19820.3758    0 1966 24520.0622 19820.3758  19.2%     -    0s
    +## H    0     0                    24498.644854 19820.3758  19.1%     -    0s
    +## H    0     0                    23263.485453 19820.3758  14.8%     -    0s
    +## H    0     0                    22049.956515 19820.3758  10.1%     -    0s
    +##      0     0 19847.8388    0 1956 22049.9565 19847.8388  10.0%     -    0s
    +##      0     0 19854.3259    0 1960 22049.9565 19854.3259  10.0%     -    0s
    +##      0     0 19854.3439    0 1958 22049.9565 19854.3439  10.0%     -    0s
    +##      0     0 19924.6889    0 1682 22049.9565 19924.6889  9.64%     -    0s
    +##      0     0 19936.2410    0 1978 22049.9565 19936.2410  9.59%     -    0s
    +##      0     0 19937.4882    0 2005 22049.9565 19937.4882  9.58%     -    0s
    +##      0     0 19937.6761    0 2005 22049.9565 19937.6761  9.58%     -    0s
    +##      0     0 19968.7333    0 1691 22049.9565 19968.7333  9.44%     -    0s
    +##      0     0 19970.0998    0 1714 22049.9565 19970.0998  9.43%     -    0s
    +##      0     0 19970.4702    0 1714 22049.9565 19970.4702  9.43%     -    0s
    +##      0     0 19983.7957    0 1748 22049.9565 19983.7957  9.37%     -    0s
    +##      0     0 19984.9623    0 1754 22049.9565 19984.9623  9.37%     -    0s
    +##      0     0 19985.0928    0 1754 22049.9565 19985.0928  9.36%     -    0s
    +##      0     0 19990.8490    0 2040 22049.9565 19990.8490  9.34%     -    1s
    +##      0     0 19991.1771    0 2041 22049.9565 19991.1771  9.34%     -    1s
    +##      0     0 19991.9721    0 2039 22049.9565 19991.9721  9.33%     -    1s
    +##      0     0 19992.1795    0 2040 22049.9565 19992.1795  9.33%     -    1s
    +##      0     0 19995.3797    0 1784 22049.9565 19995.3797  9.32%     -    1s
    +##      0     0 19995.3847    0 1788 22049.9565 19995.3847  9.32%     -    1s
    +##      0     0 19995.5716    0 2038 22049.9565 19995.5716  9.32%     -    1s
    +##      0     0 19995.8374    0 2037 22049.9565 19995.8374  9.32%     -    1s
    +##      0     0 19996.1519    0 2039 22049.9565 19996.1519  9.31%     -    1s
    +##      0     0 19996.1519    0 2039 22049.9565 19996.1519  9.31%     -    1s
    +##      0     2 19996.6237    0 2039 22049.9565 19996.6237  9.31%     -    1s
    +## H   27    27                    21471.431466 19996.6237  6.87%   117    2s
    +## H   52    52                    21371.814002 19996.6237  6.43%   115    2s
    +## H   54    54                    21254.060213 19996.6237  5.92%   117    2s
    +## H  108   108                    21204.105802 19996.6237  5.69%   123    3s
    +## H  162   162                    21202.636805 19996.6237  5.69%   104    4s
    +## H  286   286                    21166.904542 19996.6237  5.53%  69.2    4s
    +## H  606   599                    20993.604430 19996.6237  4.75%  32.8    5s
    +##    631    15 20993.6044  182 1965 20993.6044 20027.8276  4.60%  31.5   10s
    +##    652     7 20993.6044  492 1964 20993.6044 20029.9069  4.59%  37.6   15s
    +##    673    15 20091.4377   24 1974 20993.6044 20082.6816  4.34%  45.4   20s
    +## H  750    81                    20939.026645 20082.6816  4.09%  61.7   24s
    +##    818   151 21249.5497  111   22 20939.0266 20082.6816  4.09%  64.7   25s
    +## H 1231   560                    20924.385117 20082.6816  4.02%  44.7   27s
    +##   1308   639 20794.8335   63 1319 20924.3851 20084.4398  4.01%  57.3   30s
    +## H 1975  1297                    20813.687049 20092.6890  3.46%  50.6   32s
    +##   2454  1776 20457.8715   40 1754 20813.6870 20099.7124  3.43%  45.6   35s
    +##   3179  2499 21128.9121   88  183 20813.6870 20109.8128  3.38%  46.0   40s
    +##   4699  3645 20485.1487   37 1861 20813.6870 20148.3760  3.20%  38.3   45s
    +##   5194  3776 20246.3125   28 1773 20813.6870 20155.9610  3.16%  40.3   50s
    +##   6021  4504 20367.1172   32 1919 20813.6870 20167.5494  3.10%  40.7   55s
    +##   6667  4755 20279.8697   29 1791 20813.6870 20193.4219  2.98%  42.1   60s
    +##   7228  4711 20514.1843   33 1904 20813.6870 20195.9413  2.97%  43.7   65s
     ## 
     ## Cutting planes:
    -##   Gomory: 3
    +##   Gomory: 7
    +##   Cover: 4
    +##   MIR: 33
    +##   StrongCG: 1
    +##   Flow cover: 10
    +##   GUB cover: 2
    +##   Zero half: 22
    +##   RLT: 24
     ## 
    -## Explored 7 nodes (489 simplex iterations) in 0.08 seconds (0.10 work units)
    +## Explored 7780 nodes (344017 simplex iterations) in 68.14 seconds (80.68 work units)
     ## Thread count was 1 (of 8 available processors)
     ## 
    -## Solution count 5: 2604.74 2730.95 2790.87 ... 20287.2
    +## Solution count 1000: 20813.7 20924.4 20939 ... 22439.4
     ## 
     ## Optimal solution found (tolerance 1.00e-01)
    -## Best objective 2.604742604847e+03, best bound 2.375462328932e+03, gap 8.8024%
    -
    # plot solution
    -plot(s40, col = c("grey90", "darkgreen"), main = "Solution",
    -     xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1))
    -

    -

    We can plot this solution because the planning unit input data are spatially referenced in a raster format. The output format will always match the planning unit data used to initialize the problem. For example, the solution to a problem with planning units in a spatial vector (shapefile) format would also be in a spatial vector format. Similarly, if the planning units were in a tabular format (i.e. data.frame), the solution would also be returned in a tabular format.

    -

    We can also extract attributes from the solution that describe the quality of the solution and the optimization process.

    -
    # extract the objective (numerical value being minimized or maximized)
    -print(attr(s40, "objective"))
    -
    ## solution_1 
    -##   2604.743
    -
    # extract time spent solving solution
    -print(attr(s40, "runtime"))
    -
    ## solution_1 
    -##      0.099
    -
    # extract state message from the solver that describes why this specific
    -# solution was returned
    -print(attr(s40, "status"))
    -
    ## solution_1 
    -##  "OPTIMAL"
    -
    -
    -

    Evaluate the performance of a solution

    -

    After obtaining a solution to a conservation planning problem, it can be useful to calculate various summary statistics to understand its performance. The following functions are available to summarize a solution:

    -
      -
    • Calculate the number of planning units selected within a solution.
    • -
    -
    # calculate statistic
    -eval_n_summary(p40, s40)
    -
    ## # A tibble: 1 × 2
    -##   summary  cost
    -##   <chr>   <dbl>
    -## 1 overall    10
    -
      -
    • Calculate the total cost of a solution.
    • -
    -
    # calculate statistic
    -eval_cost_summary(p40, s40)
    -
    ## # A tibble: 1 × 2
    -##   summary  cost
    -##   <chr>   <dbl>
    -## 1 overall 2005.
    -
      -
    • Calculate how well features are represented by a solution. This function can be used for problems that are built using targets and those that are not built using targets.
    • -
    -
    # calculate statistics
    -eval_feature_representation_summary(p40, s40)
    -
    ## # A tibble: 5 × 5
    -##   summary feature total_amount absolute_held relative_held
    -##   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -## 1 overall layer.1         83.3          8.95         0.107
    -## 2 overall layer.2         31.2          3.22         0.103
    -## 3 overall layer.3         72.0          7.59         0.106
    -## 4 overall layer.4         42.7          4.34         0.102
    -## 5 overall layer.5         56.7          5.89         0.104
    -
      -
    • Calculate how well feature representation targets are met by a solution. This function can only be used with problems contain targets.
    • -
    -
    # calculate statistics
    -eval_target_coverage_summary(p40, s40)
    -
    ## # A tibble: 5 × 9
    -##   feature met   total_amount absolute_target absolute_held absolute_shortfall
    -##   <chr>   <lgl>        <dbl>           <dbl>         <dbl>              <dbl>
    -## 1 layer.1 TRUE          83.3            8.33          8.95                  0
    -## 2 layer.2 TRUE          31.2            3.12          3.22                  0
    -## 3 layer.3 TRUE          72.0            7.20          7.59                  0
    -## 4 layer.4 TRUE          42.7            4.27          4.34                  0
    -## 5 layer.5 TRUE          56.7            5.67          5.89                  0
    -##   relative_target relative_held relative_shortfall
    -##             <dbl>         <dbl>              <dbl>
    -## 1             0.1         0.107                  0
    -## 2             0.1         0.103                  0
    -## 3             0.1         0.106                  0
    -## 4             0.1         0.102                  0
    -## 5             0.1         0.104                  0
    -
      -
    • Calculate the exposed boundary length (perimeter) associated with a solution.
    • -
    -
    # calculate statistic
    -eval_boundary_summary(p40, s40)
    -
    ## # A tibble: 1 × 2
    -##   summary boundary
    -##   <chr>      <dbl>
    -## 1 overall      1.2
    -
      -
    • Calculate the connectivity held within a solution.
    • -
    -
    # calculate statistic
    -# here we use the raster data for the first feature as an example
    -# to parametrize pair-wise connectivity between different planning units
    -eval_connectivity_summary(
    -  p40, s40, data = connectivity_matrix(sim_pu_raster, sim_features[[1]]))
    -
    ## # A tibble: 1 × 2
    -##   summary connectivity
    -##   <chr>          <dbl>
    -## 1 overall         1.97
    -
    -
    -

    Marxan problems

    -

    Although users are encouraged to build and tailor conservation planning problems to suit their own needs using the problem function, sometimes it just simply easier to use a more familiar formulation. The marxan_problem function is provided as a convenient wrapper for building and solving Marxan-style conservation problems. If users already have their conservation planning data formatted for use with Marxan, this function can also read Marxan data files and solve the Marxan-style problems using exact algorithm solvers. Please note that problems built using the marxan_problem function are still solved the same way as a problem initialized using the problem function, and therefore still require the installation of one of the solver packages.

    -

    Here is a short example showing how the marxan_problem function can be used to read Marxan input files and the solve function can be used to solve the problem.

    -
    # set file path for Marxan input file
    -minput <- system.file("extdata/input.dat", package = "prioritizr")
    -
    -# read Marxan input file
    -mp <- marxan_problem(minput)
    -
    -# print problem
    -print(mp)
    -
    ## Conservation Problem
    -##   planning units: data.frame (1751 units)
    -##   cost:           min: 0, max: 41569219.38232
    -##   features:       bird1, nvis2, nvis8, ... (17 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.3, max: 0.3)]
    -##   decisions:      default
    -##   constraints:    <Locked in planning units [317 locked units]
    -##                    Locked out planning units [1 locked units]>
    -##   penalties:      <Boundary penalties [edge factor (min: 1, max: 1), penalty (1), zones]>
    -##   portfolio:      default
    -##   solver:         default
    -
    # solve the problem
    -ms <- solve(mp)
    -
    -# since the Marxan data was in a tabular format, the solution is also returned
    -# in a tabular format, so we will print the first six rows of the table
    -# containing the solution
    -head(ms)
    -

    Alternatively, rather then using a Marxan input file to construct the problem, we can manually read in the Marxan data files and input these to the marxan_problem function.

    -
    # load data
    -pu <- system.file("extdata/input/pu.dat", package = "prioritizr") %>%
    -      read.table(sep = ",", header = TRUE)
    -features <- system.file("extdata/input/spec.dat", package = "prioritizr") %>%
    -            read.table(sep = ",", header = TRUE)
    -bound <- system.file("extdata/input/bound.dat", package = "prioritizr") %>%
    -         read.table(sep = "\t", header = TRUE)
    -rij <- system.file("extdata/input/puvspr.dat", package = "prioritizr") %>%
    -       read.table(sep = ",", header = TRUE)
    -
    -# build Marxan problem using data.frame objects
    -mp2 <- marxan_problem(x = pu, spec = features, puvspr = rij, bound = bound,
    -                      blm = 0)
    -
    -# print problem
    -print(mp2)
    +## Best objective 2.081368704941e+04, best bound 2.019762823706e+04, gap 2.9599%
    +
    print(prt)
    +
    ## class       : SpatialPolygonsDataFrame 
    +## features    : 1130 
    +## extent      : 298809.6, 613818.8, 5167775, 5502544  (xmin, xmax, ymin, ymax)
    +## crs         : +proj=utm +zone=55 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    +## variables   : 1005
    +## names       :   id,              cost, status, locked_in, locked_out, solution_1, solution_2, solution_3, solution_4, solution_5, solution_6, solution_7, solution_8, solution_9, solution_10, ... 
    +## min values  :    1, 0.192488262910798,      0,         0,          0,          0,          0,          0,          0,          0,          0,          0,          0,          0,           0, ... 
    +## max values  : 1130,  61.9272727272727,      2,         1,          0,          1,          1,          1,          1,          1,          1,          1,          1,          1,           1, ...
    +

    After generating all these prioritizations, we now want some way to visualize them. Because it would be onerous to look at each and every prioritization individually, we will use statistical analyses to help us. We can visualize the differences between these different prioritizations – based on which planning units they selected – using a hierarchical cluster analysis (Harris et al. 2014).

    +
    # extract solutions
    +prt_results <- prt@data[, startsWith(names(prt), "solution_"), ]
    +
    +# calculate pair-wise distances between different prioritizations for analysis
    +prt_dists <- vegan::vegdist(t(prt_results), method = "jaccard", binary = TRUE)
    +
    +# run cluster analysis
    +prt_clust <- hclust(as.dist(prt_dists), method = "average")
    +
    +# visualize clusters
    +opar <- par()
    +par(oma = c(0, 0, 0, 0), mar= c(0, 4.1, 1.5, 2.1))
    +plot(prt_clust, labels = FALSE, sub = NA, xlab = "",
    +     main = "Different prioritizations in portfolio")
    +suppressWarnings(par(opar))
    +

    +

    We can see that there are approximately six main groups of prioritizations in the portfolio. To explore these different groups, let’s conduct another cluster analysis (i.e., a k-medoids analysis) to extract the most representative prioritization from each of these groups. In other words, we will run another statistical analysis to find the most central prioritization within each group.

    +
    # run k-medoids analysis
    +prt_med <- pam(prt_dists, k = 6)
    +
    +# extract names of prioritizations that are most central for each group.
    +prt_med_names <- prt_med$medoids
    +print(prt_med_names)
    +
    ## [1] "solution_29"  "solution_228" "solution_41"  "solution_242" "solution_663"
    +## [6] "solution_447"
    +
    # create a copy of prt and set values for locked in planning units to -1
    +# so we can easily visualize differences between prioritizations
    +prt2 <- prt[, prt_med_names]
    +prt2@data[which(tas_pu$locked_in > 0.5), prt_med_names] <- -1
    +
    +# plot a map showing main different prioritizations
    +# dark grey: locked in planning units
    +# grey: planning units not selected
    +# green: selected planning units
    +plot(st_as_sf(prt2), pal = c("grey60", "grey90", "darkgreen"))
    +

    +
    +
    +

    Marxan compatibility

    +

    The prioritizr R package provides functionality to help Marxan users generate prioritizations. Specifically, it can import conservation planning data prepared for Marxan, and can generate prioritizations using a similar problem formulation as Marxan (based on Beyer et al. 2016). Indeed, the problem formulation presented earlier in this vignette is very similar to that used by Marxan. The key difference is that the problem formulation we specified earlier uses “hard constraints” for feature representation, and Marxan uses “soft constraints” for feature representation. This means that prioritization we generated earlier was mathematically guaranteed to reach the targets for all features. However, if we used Marxan to generate the prioritization, then we could have produced a prioritization that would fail to reach targets (depending the Species Penalty Factors used to generate the prioritization). In addition to these differences in terms problem formulation, the prioritizr R package uses exact algorithms – instead of the simulated annealing algorithm – which ensures that we obtain prioritizations that are near optimal.

    +

    Here we will show the prioritizr R package can import Marxan data and generate a prioritization. To begin with, let’s import a conservation planning data prepared for Marxan.

    +
    # import data
    +## planning unit data
    +pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr")
    +pu_data <- read.csv(pu_path, header = TRUE, stringsAsFactors = FALSE)
    +print(head(pu_data))
    +
    ##   id       cost status    xloc     yloc
    +## 1  3        0.0      0 1116623 -4493479
    +## 2 30   752727.5      3 1110623 -4496943
    +## 3 56  3734907.5      0 1092623 -4500408
    +## 4 58  1695902.1      0 1116623 -4500408
    +## 5 84  3422025.6      0 1098623 -4503872
    +## 6 85 17890758.4      0 1110623 -4503872
    +
    ## feature data
    +spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr")
    +spec_data <- read.csv(spec_path, header = TRUE, stringsAsFactors = FALSE)
    +print(head(spec_data))
    +
    ##   id prop spf   name
    +## 1 10  0.3   1  bird1
    +## 2 11  0.3   1  nvis2
    +## 3 12  0.3   1  nvis8
    +## 4 13  0.3   1  nvis9
    +## 5 14  0.3   1 nvis14
    +## 6 15  0.3   1 nvis20
    +
    ## amount of each feature within each planning unit data
    +puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr")
    +puvspr_data <- read.csv(puvspr_path, header = TRUE, stringsAsFactors = FALSE)
    +print(head(puvspr_data))
    +
    ##   species  pu     amount
    +## 1      26  56 1203448.84
    +## 2      26  58  451670.10
    +## 3      26  84  680473.75
    +## 4      26  85   97356.24
    +## 5      26  86   78034.76
    +## 6      26 111 4783274.17
    +
    ## boundary data
    +bound_path <- system.file("extdata/input/bound.dat", package = "prioritizr")
    +bound_data <- read.table(bound_path, header = TRUE, stringsAsFactors = FALSE)
    +print(head(bound_data))
    +
    ##   id1 id2 boundary
    +## 1   3   3    16000
    +## 2   3  30     4000
    +## 3   3  58     4000
    +## 4  30  30    12000
    +## 5  30  58     4000
    +## 6  30  85     4000
    +

    After importing the data, we can now generate a prioritization based on the Marxan problem formulation (using the marxan_problem() function). Please note that this function does not generate prioritizations using Marxan. Instead, it uses the data to create an optimization problem formulation similar to Marxan – using hard constraints instead of soft constraints – and uses an exact algorithm solver to generate a prioritization.

    +
    # create problem
    +p2 <- marxan_problem(pu_data, spec_data, puvspr_data, bound_data,
    +                     blm = 0.0005)
    +
    +# print problem
    +print(p2)
    ## Conservation Problem
     ##   planning units: data.frame (1751 units)
     ##   cost:           min: 0, max: 41569219.38232
    @@ -1514,167 +658,80 @@ 

    Marxan problems

    ## objective: Minimum set objective ## targets: Relative targets [targets (min: 0.3, max: 0.3)] ## decisions: default -## constraints: <Locked in planning units [317 locked units] -## Locked out planning units [1 locked units]> -## penalties: <Boundary penalties [edge factor (min: 1, max: 1), penalty (0), zones]> +## constraints: <Locked out planning units [1 locked units] +## Locked in planning units [317 locked units]> +## penalties: <Boundary penalties [edge factor (min: 1, max: 1), penalty (5e-04), zones]> ## portfolio: default ## solver: default
    -
    -
    -

    Importance (irreplaceability)

    -

    Conservation plans can take a long time to implement. Since funding availability and habitat quality can decline over time, it is critical that the most important places in a prioritization are scheduled for protection as early as possible. For instance, some planning units in a solution might contain many rare species which do not occur in any other planning units. Alternatively, some planning units might offer an especially high return on investment that reduces costs considerably. As a consequence, conservation planners often need information on which planning units selected in a prioritization are most important to the overall success of the prioritization. To achieve this, conservation planners can use importance (irreplaceability) scores for each planning unit selected in a solution.

    -

    The prioritizr R package offers multiple methods for assessing importance. These includes scores calculated based on replacement costs [eval_replacement_importance(); Cabeza & Moilanen (2006)], Ferrier et al. (2000) (eval_ferrier_importance()), and rarity weighted richness scores [eval_rare_richness_importance(); Williams et al. (1996)]. The replacement cost scores quantify the change in the objective function (e.g. additional costs required to meet feature targets) of the optimal solution if a given planning unit in a solution cannot be acquired. They can (i) account for the cost of different planning units, (ii) account for multiple management zones, (iii) apply to any objective function, and (iv) identify truly irreplaceable planning units (denoted with infinite values). The Ferrier scores quantify the importance of planning units for meeting feature targets. They can only be applied to conservation problems with a minimum set objective and a single zone (i.e. the classic Marxan-type problem). Furthermore—unlike the replacement cost scores—the Ferrier scores provide a score for each feature within each planning unit, providing insight into why certain planning units are more important than other planning units. The rarity weighted richness scores are simply a measure of biological diversity. They do not account for planning costs, multiple management zones, objective functions, or feature targets (or weightings). They merely describe the spatial patterns of biodiversity, and do not account for many of the factors needed to quantify the importance of a planning unit for achieving conservation goals.

    -

    We recommend using replacement cost scores for small and moderate sized problems (e.g. less than 30,000 planning units) when it is feasible to do so. It can take a very long time to compute replacement cost scores, and so it is simply not feasible to compute these scores for particularly large problems. For moderate and large sized problems (e.g. more than 30,000 planning units), we recommend using the Ferrier method. This is because it explicitly accounts for representation targets, unlike the rarity weighted richness scores.

    -

    Below we will generate a solution, and then calculate importance scores for the planning units selected in the solution using the three different methods.

    -
    # formulate the problem
    -p41 <- problem(sim_pu_raster, sim_features) %>%
    -       add_min_set_objective() %>%
    -       add_relative_targets(0.1) %>%
    -       add_binary_decisions()
    -
    -# solve the problem
    -s41 <- solve(p41)
    +
    # solve problem
    +s2 <- solve(p2)
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
     ## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    -## Optimize a model with 5 rows, 90 columns and 450 nonzeros
    -## Model fingerprint: 0x6442bf6e
    -## Variable types: 0 continuous, 90 integer (90 binary)
    +## Optimize a model with 10075 rows, 6780 columns and 24778 nonzeros
    +## Model fingerprint: 0x2652a1c2
    +## Variable types: 0 continuous, 6780 integer (6780 binary)
     ## Coefficient statistics:
    -##   Matrix range     [2e-01, 9e-01]
    -##   Objective range  [2e+02, 2e+02]
    +##   Matrix range     [5e-01, 4e+07]
    +##   Objective range  [4e+00, 4e+07]
     ##   Bounds range     [1e+00, 1e+00]
    -##   RHS range        [3e+00, 8e+00]
    -## Found heuristic solution: objective 2337.9617505
    -## Presolve time: 0.00s
    -## Presolved: 5 rows, 90 columns, 450 nonzeros
    -## Variable types: 0 continuous, 90 integer (90 binary)
    -## Found heuristic solution: objective 2332.1003790
    -## Root relaxation presolved: 5 rows, 90 columns, 450 nonzeros
    +##   RHS range        [5e+07, 3e+09]
    +## Warning: Model contains large rhs
    +##          Consider reformulating model or setting NumericFocus parameter
    +##          to avoid numerical issues.
    +## Found heuristic solution: objective 1.221185e+10
    +## Presolve removed 4707 rows and 3103 columns
    +## Presolve time: 0.04s
    +## Presolved: 5368 rows, 3677 columns, 12704 nonzeros
    +## Variable types: 0 continuous, 3677 integer (3677 binary)
    +## Found heuristic solution: objective 1.009922e+10
    +## Root relaxation presolved: 5368 rows, 3677 columns, 12704 nonzeros
     ## 
     ## 
    -## Root relaxation: objective 1.931582e+03, 12 iterations, 0.00 seconds (0.00 work units)
    +## Root relaxation: objective 9.564577e+09, 493 iterations, 0.01 seconds (0.01 work units)
     ## 
     ##     Nodes    |    Current Node    |     Objective Bounds      |     Work
     ##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
     ## 
    -##      0     0 1931.58191    0    4 2332.10038 1931.58191  17.2%     -    0s
    -## H    0     0                    1987.3985265 1931.58191  2.81%     -    0s
    +##      0     0 9.5646e+09    0   20 1.0099e+10 9.5646e+09  5.29%     -    0s
     ## 
    -## Explored 1 nodes (12 simplex iterations) in 0.00 seconds (0.00 work units)
    +## Explored 1 nodes (493 simplex iterations) in 0.06 seconds (0.10 work units)
     ## Thread count was 1 (of 8 available processors)
     ## 
    -## Solution count 3: 1987.4 2332.1 2337.96 
    +## Solution count 2: 1.00992e+10 1.22118e+10 
     ## 
     ## Optimal solution found (tolerance 1.00e-01)
    -## Best objective 1.987398526526e+03, best bound 1.931581908865e+03, gap 2.8085%
    -
    # plot solution
    -plot(s41, col = c("grey90", "darkgreen"), main = "Solution",
    -     xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1))
    -

    -
    # calculate replacement cost scores and make the solver quiet
    -rc41 <- p41 %>%
    -        add_default_solver(gap = 0, verbose = FALSE) %>%
    -        eval_replacement_importance(s41)
    -
    -# plot replacement cost scores
    -plot(rc41, main = "replacement cost")
    -

    -
    # calculate Ferrier scores and extract total score
    -fs41 <- eval_ferrier_importance(p40, s40)[["total"]]
    -
    -# plot Ferrier scores
    -plot(fs41, main = "Ferrier scores")
    -

    -
    # calculate rarity weighted richness scores
    -rwr41 <- eval_rare_richness_importance(p40, s40)
    -
    -# plot replacement cost scores
    -plot(rwr41, main = "rarity weighted richness")
    -

    -

    Although rarity weighted richness scores can approximate scores derived from the other two methods in certain conservation planning exercises, we can see that the rarity weighted richness scores provide completely different results in this case.

    -
    +## Best objective 1.009922392231e+10, best bound 9.564577117609e+09, gap 5.2939%
    +
    # print first six rows of solution object
    +print(head(s2))
    +
    ##   id       cost status    xloc     yloc locked_in locked_out solution_1
    +## 1  3        0.0      0 1116623 -4493479     FALSE      FALSE          0
    +## 2 30   752727.5      3 1110623 -4496943     FALSE       TRUE          0
    +## 3 56  3734907.5      0 1092623 -4500408     FALSE      FALSE          0
    +## 4 58  1695902.1      0 1116623 -4500408     FALSE      FALSE          0
    +## 5 84  3422025.6      0 1098623 -4503872     FALSE      FALSE          0
    +## 6 85 17890758.4      0 1110623 -4503872     FALSE      FALSE          0

    Conclusion

    -

    Hopefully, this vignette has provided an informative introduction to the prioritizr R package. For more worked examples using the prioritizr R package, check out the Tasmania and Salt Spring Island vignettes. Perhaps, one of the best ways to learn a new piece of software is to just try it out. Test it, try breaking it, make mistakes, and learn from them. We would recommend trying to build conservation planning problems that resemble those you face in your own work—but using the built-in example data sets. This way you can quickly verify that the problems you build actually mean what you think they mean. For instance, you can try playing around with the targets and see what effect they have on the solutions, or try playing around with penalties and see what effect they have on the solutions. Finally, if you have any questions about using the prioritizr R package or suggestions for it, please file an issue on the package’s online coding repository (https://github.com/prioritizr/prioritizr/issues).

    +

    This tutorial shows how the prioritizr R package can be used to build a conservation problem, generate a prioritization, and evaluate it. Although we explored just a few functions, the package provides many different functions so that you can build and custom-tailor conservation planning problems to suit your needs. To learn more about the package, please see the package vignettes for an overview of the package, instructions for installing the Gurobi optimization suite, benchmarks comparing the performance of different solvers, and a record of publications that have cited the package. In addition to this tutorial, the package also provides tutorials on incorporating connectivity into prioritizations, calibrating trade-offs between different criteria (e.g., total cost and spatial fragmentation), and creating prioritizations that have multiple management zones or management actions.

    References

    -
    -Achterberg, T. & Wunderling, R. (2013). Mixed Integer Programming: Analyzing 12 Years of Progress. Facets of combinatorial optimization: Festschrift for martin grötschel (eds M. Jünger & G. Reinelt), pp. 449–481. Springer, Berlin, Heidelberg. -
    Ball, I.R., Possingham, H. & Watts, M.E. (2009). Marxan and relatives: Software for spatial conservation prioritisation. Spatial Conservation Prioritisation: Quantitative Methods & Computational Tools (eds A. Moilanen, K.A. Wilson & H. Possingham), pp. 185–189. Oxford University Press, Oxford, UK.
    Beyer, H.L., Dujardin, Y., Watts, M.E. & Possingham, H.P. (2016). Solving conservation planning problems with integer linear programming. Ecological Modelling, 328, 14–22.
    -
    -Billionnet, A. (2013). Mathematical optimization ideas for biodiversity conservation. European Journal of Operational Research, 231, 514–534. -
    -
    -Butchart, S.H., Clarke, M., Smith, R.J., Sykes, R.E., Scharlemann, J.P., Harfoot, M., Buchanan, G.M., Angulo, A., Balmford, A., Bertzky, B. & others. (2015). Shortfalls and solutions for meeting national and global conservation area targets. Conservation Letters, 8, 329–337. -
    -
    -Cabeza, M. & Moilanen, A. (2001). Design of reserve networks and the persistence of biodiversity. Trends in Ecology & Evolution, 16, 242–248. -
    -
    -Cabeza, M. & Moilanen, A. (2006). Replacement cost: A practical measure of site value for cost-effective reserve planning. Biological Conservation, 132, 336–342. -
    -
    -Church, R.L., Stoms, D.M. & Davis, F.W. (1996). Reserve selection as a maximal covering location problem. Biological conservation, 76, 105–112. -
    -
    -Faith, D.P. (1992). Conservation evaluation and phylogenetic diversity. Biological Conservation, 61, 1–10. -
    Ferrier, S., Pressey, R.L. & Barrett, T.W. (2000). A new predictor of the irreplaceability of areas for achieving a conservation goal, its application to real-world planning, and a research agenda for further refinement. Biological Conservation, 93, 303–325.
    -
    -Fuller, R.A., McDonald-Madden, E., Wilson, K.A., Carwardine, J., Grantham, H.S., Watson, J.E., Klein, C.J., Green, D.C. & Possingham, H.P. (2010). Replacing underperforming protected areas achieves better conservation outcomes. Nature, 466, 365. -
    -
    -Kirkpatrick, J.B. (1983). An iterative method for establishing priorities for the selection of nature reserves: An example from Tasmania. Biological Conservation, 25, 127–134. -
    -
    -Kirkpatrick, S., Gelatt, C.D. & Vecchi, M.P. (1983). Optimization by simulated annealing. Science, 220, 671–680. -
    -
    -Klein, C., Wilson, K., Watts, M., Stein, J., Berry, S., Carwardine, J., Smith, M.S., Mackey, B. & Possingham, H. (2009). Incorporating ecological and evolutionary processes into continental-scale conservation planning. Ecological Applications, 19, 206–217. -
    -
    -Margules, C.R. & Pressey, R.L. (2000). Systematic conservation planning. Nature, 405, 243–253. -
    -
    -Moilanen, A. (2007). Landscape Zonation, benefit functions and target-based planning: Unifying reserve selection strategies. Biological Conservation, 134, 571–579. -
    -
    -Nicholls, A.O. & Margules, C.R. (1993). An upgraded reserve selection algorithm. Biological Conservation, 64, 165–169. -
    -
    -Pressey, R.L., Possingham, H.P. & Margules, C.R. (1996). Optimality in reserve selection algorithms: When does it matter and how much? Biological Conservation, 76, 259–267. -
    -
    -Rodrigues, A.S., Akcakaya, H.R., Andelman, S.J., Bakarr, M.I., Boitani, L., Brooks, T.M., Chanson, J.S., Fishpool, L.D., Da Fonseca, G.A., Gaston, K.J. & others. (2004). Global gap analysis: Priority regions for expanding the global protected-area network. BioScience, 54, 1092–1100. -
    -
    -Rodrigues, A.S. & Gaston, K.J. (2002). Maximising phylogenetic diversity in the selection of networks of conservation areas. Biological Conservation, 105, 103–111. -
    -
    -Rodrigues, A.S., Orestes Cerdeira, J. & Gaston, K.J. (2000). Flexibility, efficiency, and accountability: Adapting reserve selection algorithms to more complex conservation problems. Ecography, 23, 565–574. -
    -
    -Rosauer, D., Laffan, S.W., Crisp, M.D., Donnellan, S.C. & Cook, L.G. (2009). Phylogenetic endemism: A new approach for identifying geographical concentrations of evolutionary history. Molecular Ecology, 18, 4061–4072. -
    -
    -Stigner, M.G., Beyer, H.L., Klein, C.J. & Fuller, R.A. (2016). Reconciling recreational use and conservation values in a coastal protected area. Journal of Applied Ecology, 53, 1206–1214. -
    -
    -Watts, M.E., Ball, I.R., Stewart, R.S., Klein, C.J., Wilson, K., Steinback, C., Lourival, R., Kircher, L. & Possingham, H.P. (2009). Marxan with Zones: Software for optimal conservation based land- and sea-use zoning. Environmental Modelling & Software, 24, 1513–1521. +
    +Harris, L.R., Watts, M.E., Nel, R., Schoeman, D.S. & Possingham, H.P. (2014). Using multivariate statistics to explore trade-offs among spatial planning scenarios (P. Armsworth, Ed.). Journal of Applied Ecology, 51, 1504–1514.
    -
    -Williams, P., Gibbons, D., Margules, C., Rebelo, A., Humphries, C. & Pressey, R. (1996). A comparison of richness hotspots, rarity hotspots, and complementary areas for conserving diversity of British birds. Conservation Biology, 10, 155–174. +
    +Klein, C., Carwardine, J., Wilson, K., Watts, M. & Possingham, H. (2007). Spatial Prioritization Approaches for the Conservation of Biodiversity in Australia: Considering Conservation Costs, Ecological & Evolutionary Processes, and Large-Intact Areas. Report to the Department of Environment; Water Resources.
    diff --git a/inst/doc/publication_record.Rmd b/inst/doc/publication_record.Rmd index 7768b00fc..f7ca8b88d 100644 --- a/inst/doc/publication_record.Rmd +++ b/inst/doc/publication_record.Rmd @@ -1,5 +1,5 @@ --- -title: "Publication Record" +title: "Publication record" output: rmarkdown::html_vignette: toc: true @@ -8,7 +8,7 @@ output: fontsize: 11pt documentclass: article vignette: > - %\VignetteIndexEntry{Publication Record} + %\VignetteIndexEntry{Publication record} %\VignetteEngine{knitr::rmarkdown_notangle} --- diff --git a/inst/doc/publication_record.html b/inst/doc/publication_record.html index 624dc224f..28a637e22 100644 --- a/inst/doc/publication_record.html +++ b/inst/doc/publication_record.html @@ -12,7 +12,7 @@ -Publication Record +Publication record @@ -42,7 +42,7 @@ -

    Publication Record

    +

    Publication record

    diff --git a/inst/doc/saltspring.Rmd b/inst/doc/saltspring.Rmd deleted file mode 100644 index 91284e3d2..000000000 --- a/inst/doc/saltspring.Rmd +++ /dev/null @@ -1,212 +0,0 @@ ---- -title: "Salt Spring Island Tutorial" -output: - rmarkdown::html_vignette: - toc: true - fig_caption: true - self_contained: yes -fontsize: 11pt -documentclass: article -bibliography: references.bib -csl: reference-style.csl -vignette: > - %\VignetteIndexEntry{Salt Spring Island Tutorial} - %\VignetteEngine{knitr::rmarkdown_notangle} ---- - -```{r, include = FALSE} -h <- 3.5 -w <- 3.5 -is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", - "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) -knitr::opts_chunk$set(fig.align = "center", eval = !is_check) -``` - -## Introduction - -This aim of this tutorial is to show how raster data can used to build conservation problems with the _prioritizr R_ package. The data used here is a subset of a much larger dataset for the Georgia Basin obtained as part of an online _Marxan_-based planning tool created for the Coastal Douglas-fir Conservation Partnership [CDFCP; @r29]. For simplicity, we focus only on Salt Spring Island, British Columbia. Salt Spring Island is central to the region and supports a diverse and globally unique mix of dry forest and savanna habitats. Today, these habitats are critically threatened due to land conversion, invasive species, and altered disturbance regimes. Known broadly as the Georgia Depression-Puget Lowlands, this region includes threatened Coastal Douglas-fir forest and Oak-Savannah habitats, also referred to as Garry oak ecosystems. Please note that this tutorial uses data from the _prioritizrdata R_ package, so ensure that it is installed before trying out the code yourself. For more information on the dataset refer to the [Marxan tool portal](https://arcese.forestry.ubc.ca/marxan-tool/) and the [tool tutorial](https://peter-arcese-lab.sites.olt.ubc.ca/files/2016/09/CDFCP_tutorial_2017_05.pdf). - -
    - -![_Extent of Coastal Douglas-fir Conservation Partnership Tool area and location of Salt Spring Island_](figures/map.jpg) - -
    - -## Exploring the data - -This dataset contains two items. First, a single-band raster planning unit layer where each one hectare pixel represents a planning unit and contains its corresponding cost [@r28]. Second, a raster stack containing ecological community feature data. Field and remote sensed data were used to calculate the probability of occurrence of five key ecological communities found on Salt Spring island. Each layer in the stack represents a different community type. In order these are; Old Forest, Savannah, Wetland, Shrub, and a layer representing the inverse probability of occurrence of human commensal species. For a given layer, the cell value indicates the composite probability of encountering the suite of bird species most commonly associated with that community type. - -First, load the required packages and the data. - -```{r, message = FALSE} -# load packages -library(prioritizrdata) -library(prioritizr) - -# load planning unit data -data(salt_pu) - -# load conservation feature data -data(salt_features) -``` - -Let's have a look at the planning unit data. Note that we log-transformed the raster to better visualize the variation in planning unit cost. - -```{r, fig.width = w, fig.height = h} -# print planning unit data -print(salt_pu) - -# plot histogram of the planning unit costs -hist(values(salt_pu), main = "Distribution of costs", - xlab = "Planning unit costs") -``` - -```{r, fig.width = w, fig.height = h} -# plot map showing the planning units costs on a log-scale -plot(log(salt_pu), main = "Planning unit costs (log)") -``` - -Next, let's look at the feature data. - -```{r, fig.width = 4.5, fig.height = 4.5} -# print features -print(salt_features) - -# plot map showing the distribution of the features -plot(salt_features, main = names(salt_features)) -``` - -## Formulating the Problem - -In this tutorial, we will only cover a few of the different ways that conservation planning problems can be formulated. The examples used here are provided to highlight how different parameters can substantially---or only slightly---alter solutions. Here, we use the minimum set objective to fulfill all targets and constraints for the smallest cost. This objective is similar to that used in the _Marxan_ decision support tool. To keep this simple, we will set biodiversity targets at 17 % to reflect the [Aichi Biodiversity Target 11](https://www.cbd.int/sp/targets/). Because properties on Salt Spring Island can either be acquired in their entirety or not at all, we leave the decision framework as the default; binary decision making. This means that planning units are either selected in the solution or not selected in the solution---planning units cannot be partially acquired. - -Now we will formulate the conservation planning problem. - -```{r} -# create problem -p1 <- problem(salt_pu, salt_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.17) %>% - add_binary_decisions() %>% - add_default_solver() - -# print problem -print(p1) -``` - -Note that the `%>%` notation is used to attach the objectives, targets, and decisions to the problem. Since binary-type decisions are the default decision-type, we don't have to explicitly specify the decision-type, but we specify it here for clarity. - -## Solving the problem - -The _prioritizr R_ package supports three different integer linear programming solvers: _gurobi_ (via the _gurobi_ package), _IBM CPLEX_ (via the _cplexAPI_ package), _SYMPHONY_ (via the _Rsymphony_ and _lpsymphony_ packages) and _CBC_ (via the _rcbc_ package). There are costs and benefits associated with each of these solvers, but the solver itself should have little impact on the actual solution returned (though certain solvers may take longer to return solutions than others). - -First, remember that the solvers must be installed. You can check if these packages are installed by running the code below. Some of these solvers are a bit complicated to install. For instance, the _gurobi_ package is distributed with the [_Gurobi_ commercial software suite](https://www.gurobi.com/) and is not available on the Comprehensive R Archive Network (CRAN). For more information on installing the _gurobi_ package, please refer to the [_Gurobi Installation Guide_](gurobi_installation.html) for more information on installing the _gurobi R_ package. Additionally, the _rcbc_ package is not available on CRAN, and must be installed from GitHub ([please refer to its documentation for installation instructions](https://dirkschumacher.github.io/rcbc/). Furthermore, although the _Rcplex_ package is available on CRAN, it cannot be successfully installed unless the [IBM CPLEX software](https://www.ibm.com/analytics/cplex-optimizer) has [already installed on your system](https://github.com/cran/cplexAPI/blob/master/inst/INSTALL). - -```{r, eval = FALSE} -print(require(gurobi)) -print(require(cplexAPI)) -print(require(Rsymphony)) -print(require(lpsymphony)) -print(require(rcbc)) -``` - -Now we will try solving the problem using the different solvers (see [`?solve`](https://prioritizr.net/reference/solve.html) for more information). We will also experiment with limiting the maximum amount of time that can be spent looking for each solution when solving the problem (using the `time_limit` parameter), and see how this alters the solutions. - -```{r, message = FALSE, results = "hide"} -titles <- c() # create vector to store plot titles -s1 <- stack() # create empty stack to store solutions - -# create new problem object with added solver -if (require("Rsymphony")) { - titles <- c(titles, "Rsymphony (5s)") - p2 <- p1 %>% add_rsymphony_solver(time_limit = 5) - s1 <- addLayer(s1, solve(p2)) -} - -if (require("Rsymphony")) { - titles <- c(titles, "Rsymphony (10s)") - p3 <- p1 %>% add_rsymphony_solver(time_limit = 10) - s1 <- addLayer(s1, solve(p3)) -} - -if (require("gurobi")) { - titles <- c(titles, "Gurobi (5s)") - p4 <- p1 %>% add_gurobi_solver(time_limit = 5) - s1 <- addLayer(s1, solve(p4)) -} - -if (require("cplexAPI")) { - titles <- c(titles, "IBM CPLEX (5s)") - p5 <- p1 %>% add_cplex_solver(time_limit = 5) - s1 <- addLayer(s1, solve(p5)) -} - -if (require("lpsymphony")) { - titles <- c(titles, "lpsymphony (10s)") - p6 <- p1 %>% add_lpsymphony_solver(time_limit = 10) - s1 <- addLayer(s1, solve(p6)) -} - -if (require("rcbc")) { - titles <- c(titles, "CBC (10s)") - p7 <- p1 %>% add_cbc_solver(time_limit = 10) - s1 <- addLayer(s1, solve(p7)) -} -``` - -Now let's visualize the solutions. - -```{r, fig.height = 5.5, fig.width = 5} -plot(s1, main = titles, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")) -``` - -We can see that all of the solutions are very similar. Therefore it would appear that for this particular problem, the solution is not highly sensitive to solver choice. For larger and more complex problems, however, we would expect the solution from _Gurobi_ to be far superior when setting time limits. At a glance, it also appears that the time limit settings did not largely impact the solutions (5 seconds vs. 10 seconds), but a more rigorous analysis is needed to investigate this. - -## Adding connectivity - -Isolated and fragmented populations are often more vulnerable to extinction. As a consequence, landscape connectivity is a key focus of many conservation planning exercises. There are a number of methods that can be used to increase connectivity in prioritizations. These methods typically involve adding constraints to a problem to ensure that solutions exhibit a specific property (e.g. selected planning units that form a contiguous reserve), or adding penalties to a problem to penalize solutions that exhibit specific properties (e.g. high levels of fragmentation). Here we will explore a couple different strategies for increasing connectivity in solutions. For brevity, we will use default solver which is automatically added to a problem if a solver is not manually specified. - -```{r} -# basic problem formulation -p6 <- problem(salt_pu, salt_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.17) %>% - add_binary_decisions() %>% - add_default_solver() - -# print problem -print(p6) -``` - -```{r, results = "hide"} -titles2 <- c() # create vector to store plot titles -s2 <- stack() # create empty stack to store solutions - -# no connectivity requirement -titles2 <- c(titles2, "No connectivity") -s2 <- addLayer(s2, solve(p6)) - -# require at least two for each selected planning unit -titles2 <- c(titles2, "Neighbor constraints (two)") -p7 <- p6 %>% add_neighbor_constraints(2) -s2 <- addLayer(s2, solve(p7)) - -# impose small penalty for fragmented solutions -titles2 <- c(titles2, "Boundary penalty (low)") -p8 <- p6 %>% add_boundary_penalties(0.0005, 0.5) -s2 <- addLayer(s2, solve(p8)) - -# impose high penalty for fragmented solutions -titles2 <- c(titles2, "Boundary penalty (high)") -p9 <- p6 %>% add_boundary_penalties(0.05, 0.5) -s2 <- addLayer(s2, solve(p9)) -``` - -```{r, fig.height = 5.5, fig.width = 5} -# plot solutions -plot(s2, main = titles2, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")) -``` - -Here we can see that adding the constraints and penalties to the problem has a small, but noticeable effect on the solutions. We would expect to see larger difference between the solutions for problems that contain more than five conservation features. You may also wish to explore the `add_connectivity_penalties` and `add_feature_contiguity_constraints` functions. These functions use additional data on landscape resistance to provide a more accurate parametrization of connectivity and, in turn, deliver more effective solutions. - -## References diff --git a/inst/doc/saltspring.html b/inst/doc/saltspring.html deleted file mode 100644 index 02c199cd5..000000000 --- a/inst/doc/saltspring.html +++ /dev/null @@ -1,390 +0,0 @@ - - - - - - - - - - - - - - -Salt Spring Island Tutorial - - - - - - - - - - - - - - - - - - - - - - - - - - -

    Salt Spring Island Tutorial

    - - - - -
    -

    Introduction

    -

    This aim of this tutorial is to show how raster data can used to build conservation problems with the prioritizr R package. The data used here is a subset of a much larger dataset for the Georgia Basin obtained as part of an online Marxan-based planning tool created for the Coastal Douglas-fir Conservation Partnership [CDFCP; Morrell et al. (2017)]. For simplicity, we focus only on Salt Spring Island, British Columbia. Salt Spring Island is central to the region and supports a diverse and globally unique mix of dry forest and savanna habitats. Today, these habitats are critically threatened due to land conversion, invasive species, and altered disturbance regimes. Known broadly as the Georgia Depression-Puget Lowlands, this region includes threatened Coastal Douglas-fir forest and Oak-Savannah habitats, also referred to as Garry oak ecosystems. Please note that this tutorial uses data from the prioritizrdata R package, so ensure that it is installed before trying out the code yourself. For more information on the dataset refer to the Marxan tool portal and the tool tutorial.

    -
    -
    - -

    Extent of Coastal Douglas-fir Conservation Partnership Tool area and location of Salt Spring Island

    -
    -
    -
    -
    -

    Exploring the data

    -

    This dataset contains two items. First, a single-band raster planning unit layer where each one hectare pixel represents a planning unit and contains its corresponding cost (BC Assessment 2015). Second, a raster stack containing ecological community feature data. Field and remote sensed data were used to calculate the probability of occurrence of five key ecological communities found on Salt Spring island. Each layer in the stack represents a different community type. In order these are; Old Forest, Savannah, Wetland, Shrub, and a layer representing the inverse probability of occurrence of human commensal species. For a given layer, the cell value indicates the composite probability of encountering the suite of bird species most commonly associated with that community type.

    -

    First, load the required packages and the data.

    -
    # load packages
    -library(prioritizrdata)
    -library(prioritizr)
    -
    -# load planning unit data
    -data(salt_pu)
    -
    -# load conservation feature data
    -data(salt_features)
    -

    Let’s have a look at the planning unit data. Note that we log-transformed the raster to better visualize the variation in planning unit cost.

    -
    # print planning unit data
    -print(salt_pu)
    -
    ## class      : RasterLayer 
    -## dimensions : 280, 200, 56000  (nrow, ncol, ncell)
    -## resolution : 100, 100  (x, y)
    -## extent     : 454589.9, 474589.9, 5394614, 5422614  (xmin, xmax, ymin, ymax)
    -## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    -## source     : memory
    -## names      : salt_pu 
    -## values     : 0.02552, 10000  (min, max)
    -
    # plot histogram of the planning unit costs
    -hist(values(salt_pu), main = "Distribution of costs",
    -     xlab = "Planning unit costs")
    -

    -
    # plot map showing the planning units costs on a log-scale
    -plot(log(salt_pu), main = "Planning unit costs (log)")
    -

    -

    Next, let’s look at the feature data.

    -
    # print features
    -print(salt_features)
    -
    ## class      : RasterStack 
    -## dimensions : 280, 200, 56000, 5  (nrow, ncol, ncell, nlayers)
    -## resolution : 100, 100  (x, y)
    -## extent     : 454589.9, 474589.9, 5394614, 5422614  (xmin, xmax, ymin, ymax)
    -## crs        : +proj=utm +zone=10 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    -## names      : salt_features.1, salt_features.2, salt_features.3, salt_features.4, salt_features.5 
    -## min values :       0.3595050,       0.2979212,       0.1132785,       0.4013101,       0.3703639 
    -## max values :       0.9312289,       0.6608167,       0.6434712,       0.8249719,       0.9032656
    -
    # plot map showing the distribution of the features
    -plot(salt_features, main = names(salt_features))
    -

    -
    -
    -

    Formulating the Problem

    -

    In this tutorial, we will only cover a few of the different ways that conservation planning problems can be formulated. The examples used here are provided to highlight how different parameters can substantially—or only slightly—alter solutions. Here, we use the minimum set objective to fulfill all targets and constraints for the smallest cost. This objective is similar to that used in the Marxan decision support tool. To keep this simple, we will set biodiversity targets at 17 % to reflect the Aichi Biodiversity Target 11. Because properties on Salt Spring Island can either be acquired in their entirety or not at all, we leave the decision framework as the default; binary decision making. This means that planning units are either selected in the solution or not selected in the solution—planning units cannot be partially acquired.

    -

    Now we will formulate the conservation planning problem.

    -
    # create problem
    -p1 <- problem(salt_pu, salt_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.17) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver()
    -
    -# print problem
    -print(p1)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (19794 units)
    -##   cost:           min: 0.02552, max: 10000
    -##   features:       salt_features.1, salt_features.2, salt_features.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (1)]
    -

    Note that the %>% notation is used to attach the objectives, targets, and decisions to the problem. Since binary-type decisions are the default decision-type, we don’t have to explicitly specify the decision-type, but we specify it here for clarity.

    -
    -
    -

    Solving the problem

    -

    The prioritizr R package supports three different integer linear programming solvers: gurobi (via the gurobi package), IBM CPLEX (via the cplexAPI package), SYMPHONY (via the Rsymphony and lpsymphony packages) and CBC (via the rcbc package). There are costs and benefits associated with each of these solvers, but the solver itself should have little impact on the actual solution returned (though certain solvers may take longer to return solutions than others).

    -

    First, remember that the solvers must be installed. You can check if these packages are installed by running the code below. Some of these solvers are a bit complicated to install. For instance, the gurobi package is distributed with the Gurobi commercial software suite and is not available on the Comprehensive R Archive Network (CRAN). For more information on installing the gurobi package, please refer to the Gurobi Installation Guide for more information on installing the gurobi R package. Additionally, the rcbc package is not available on CRAN, and must be installed from GitHub (please refer to its documentation for installation instructions. Furthermore, although the Rcplex package is available on CRAN, it cannot be successfully installed unless the IBM CPLEX software has already installed on your system.

    -
    print(require(gurobi))
    -print(require(cplexAPI))
    -print(require(Rsymphony))
    -print(require(lpsymphony))
    -print(require(rcbc))
    -

    Now we will try solving the problem using the different solvers (see ?solve for more information). We will also experiment with limiting the maximum amount of time that can be spent looking for each solution when solving the problem (using the time_limit parameter), and see how this alters the solutions.

    -
    titles <- c() # create vector to store plot titles
    -s1 <- stack()  # create empty stack to store solutions
    -
    -# create new problem object with added solver
    -if (require("Rsymphony")) {
    -  titles <- c(titles, "Rsymphony (5s)")
    -  p2 <- p1 %>% add_rsymphony_solver(time_limit = 5)
    -  s1 <- addLayer(s1, solve(p2))
    -}
    -
    ## Warning in res(x, ...): overwriting previously defined solver
    -
    if (require("Rsymphony")) {
    -  titles <- c(titles, "Rsymphony (10s)")
    -  p3 <- p1 %>% add_rsymphony_solver(time_limit = 10)
    -  s1 <- addLayer(s1, solve(p3))
    -}
    -
    ## Warning in res(x, ...): overwriting previously defined solver
    -
    if (require("gurobi")) {
    -  titles <- c(titles, "Gurobi (5s)")
    -  p4 <- p1 %>% add_gurobi_solver(time_limit = 5)
    -  s1 <- addLayer(s1, solve(p4))
    -}
    -
    ## Warning in res(x, ...): overwriting previously defined solver
    -
    if (require("cplexAPI")) {
    -  titles <- c(titles, "IBM CPLEX (5s)")
    -  p5 <- p1 %>% add_cplex_solver(time_limit = 5)
    -  s1 <- addLayer(s1, solve(p5))
    -}
    -
    ## Warning in res(x, ...): overwriting previously defined solver
    -
    if (require("lpsymphony")) {
    -  titles <- c(titles, "lpsymphony (10s)")
    -  p6 <- p1 %>% add_lpsymphony_solver(time_limit = 10)
    -  s1 <- addLayer(s1, solve(p6))
    -}
    -
    ## Warning in res(x, ...): overwriting previously defined solver
    -
    if (require("rcbc")) {
    -  titles <- c(titles, "CBC (10s)")
    -  p7 <- p1 %>% add_cbc_solver(time_limit = 10)
    -  s1 <- addLayer(s1, solve(p7))
    -}
    -
    ## Warning in res(x, ...): overwriting previously defined solver
    -

    Now let’s visualize the solutions.

    -
    plot(s1, main = titles, breaks = c(0, 0.5, 1),  col = c("grey70", "darkgreen"))
    -

    -

    We can see that all of the solutions are very similar. Therefore it would appear that for this particular problem, the solution is not highly sensitive to solver choice. For larger and more complex problems, however, we would expect the solution from Gurobi to be far superior when setting time limits. At a glance, it also appears that the time limit settings did not largely impact the solutions (5 seconds vs. 10 seconds), but a more rigorous analysis is needed to investigate this.

    -
    -
    -

    Adding connectivity

    -

    Isolated and fragmented populations are often more vulnerable to extinction. As a consequence, landscape connectivity is a key focus of many conservation planning exercises. There are a number of methods that can be used to increase connectivity in prioritizations. These methods typically involve adding constraints to a problem to ensure that solutions exhibit a specific property (e.g. selected planning units that form a contiguous reserve), or adding penalties to a problem to penalize solutions that exhibit specific properties (e.g. high levels of fragmentation). Here we will explore a couple different strategies for increasing connectivity in solutions. For brevity, we will use default solver which is automatically added to a problem if a solver is not manually specified.

    -
    # basic problem formulation
    -p6 <- problem(salt_pu, salt_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.17) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver()
    -
    -# print problem
    -print(p6)
    -
    ## Conservation Problem
    -##   planning units: RasterLayer (19794 units)
    -##   cost:           min: 0.02552, max: 10000
    -##   features:       salt_features.1, salt_features.2, salt_features.3, ... (5 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
    -##   decisions:      Binary decision 
    -##   constraints:    <none>
    -##   penalties:      <none>
    -##   portfolio:      default
    -##   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (1)]
    -
    titles2 <- c() # create vector to store plot titles
    -s2 <- stack()  # create empty stack to store solutions
    -
    -# no connectivity requirement
    -titles2 <- c(titles2, "No connectivity")
    -s2 <- addLayer(s2, solve(p6))
    -
    -# require at least two for each selected planning unit
    -titles2 <- c(titles2, "Neighbor constraints (two)")
    -p7 <- p6 %>% add_neighbor_constraints(2)
    -s2 <- addLayer(s2, solve(p7))
    -
    -# impose small penalty for fragmented solutions
    -titles2 <- c(titles2, "Boundary penalty (low)")
    -p8 <- p6 %>% add_boundary_penalties(0.0005, 0.5)
    -s2 <- addLayer(s2, solve(p8))
    -
    -# impose high penalty for fragmented solutions
    -titles2 <- c(titles2, "Boundary penalty (high)")
    -p9 <- p6 %>% add_boundary_penalties(0.05, 0.5)
    -s2 <- addLayer(s2, solve(p9))
    -
    # plot solutions
    -plot(s2, main = titles2, breaks = c(0, 0.5, 1),  col = c("grey70", "darkgreen"))
    -

    -

    Here we can see that adding the constraints and penalties to the problem has a small, but noticeable effect on the solutions. We would expect to see larger difference between the solutions for problems that contain more than five conservation features. You may also wish to explore the add_connectivity_penalties and add_feature_contiguity_constraints functions. These functions use additional data on landscape resistance to provide a more accurate parametrization of connectivity and, in turn, deliver more effective solutions.

    -
    -
    -

    References

    -
    -
    -BC Assessment. (2015). Property Information Services. URL https://info.bcassessment.ca/ [accessed 13 June 2016]. -
    -
    -Morrell, N., Schuster, R., Crombie, M. & Arcese, P. (2017). A Prioritization Tool for the Conservation of Coastal Douglas-fir Forest and Savannah Habitats of the Georgia Basin. The Nature Trust of British Colombia, Coastal Douglas Fir Conservation Partnership, and the Department of Forest and Conservation Sciences, University of British Colombia, URL https://peter-arcese-lab.sites.olt.ubc.ca/files/2016/09/CDFCP_tutorial_2017_05.pdf [accessed 9 October 2017]. -
    -
    -
    - - - - - - - - - - - diff --git a/inst/doc/solver_benchmark.Rmd b/inst/doc/solver_benchmarks.Rmd similarity index 77% rename from inst/doc/solver_benchmark.Rmd rename to inst/doc/solver_benchmarks.Rmd index a91dafadc..f0c6cd3e3 100644 --- a/inst/doc/solver_benchmark.Rmd +++ b/inst/doc/solver_benchmarks.Rmd @@ -1,5 +1,5 @@ --- -title: "Solver Benchmarks" +title: "Solver benchmarks" output: rmarkdown::html_vignette: toc: true @@ -10,7 +10,7 @@ documentclass: article bibliography: references.bib csl: reference-style.csl vignette: > - %\VignetteIndexEntry{Solver Benchmarks} + %\VignetteIndexEntry{Solver benchmarks} %\VignetteEngine{knitr::rmarkdown_notangle} --- @@ -35,17 +35,17 @@ boundary_penalty_values <- list( devtools::load_all() ``` -# Introduction +## Introduction -The _prioritizr R_ package supports a variety of optimization solvers for generating prioritizations. Specifically, the following functions can be used: [`add_gurobi_solver()`](https://prioritizr.net/reference/add_gurobi_solver.html) (interfaces with the [_Gurobi_ software](https://www.gurobi.com/)), [`add_cplex_solver()`](https://prioritizr.net/reference/add_cplex_solver.html) (interfaces with the [_IBM CPLEX_ software](https://www.ibm.com/analytics/cplex-optimizer)), [`add_cbc_solver()`](https://prioritizr.net/reference/add_cbc_solver.html) (interfaces with the [_CBC_ software](https://projects.coin-or.org/Cbc) using the [_rcbc R_ package](https://dirkschumacher.github.io/rcbc/)), [`add_rsymphony_solver()`](https://prioritizr.net/reference/add_rsymphony_solver.html) (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_Rsymphony R_ package](https://CRAN.R-project.org/package=Rsymphony)), and the [`add_lpsymphony_solver()`](https://prioritizr.net/reference/add_lsymphony_solver.html) function (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_lpsymphony R_ package](https://www.bioconductor.org/packages/release/bioc/html/lpsymphony.html)). Broadly speaking, _IBM CPLEX_ and _Gurobi_ tend to be the fastest among the supported solvers. Although they are both commercial software, special academic licenses are available at no cost. +The _prioritizr R_ package supports a variety of optimization solvers for generating prioritizations. Specifically, the following functions can be used: `add_gurobi_solver()` (interfaces with the [_Gurobi_ software](https://www.gurobi.com/)), `add_cplex_solver()` (interfaces with the [_IBM CPLEX_ software](https://www.ibm.com/analytics/cplex-optimizer)), `add_cbc_solver()` (interfaces with the [_CBC_ software](https://projects.coin-or.org/Cbc) using the [_rcbc R_ package](https://dirkschumacher.github.io/rcbc/)), `add_rsymphony_solver()` (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_Rsymphony R_ package](https://CRAN.R-project.org/package=Rsymphony)), and the `add_lpsymphony_solver()` function (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_lpsymphony R_ package](https://www.bioconductor.org/packages/release/bioc/html/lpsymphony.html)). Broadly speaking, _IBM CPLEX_ and _Gurobi_ tend to be the fastest among the supported solvers. Although they are both commercial software, special academic licenses are available at no cost. -In this vignette, we will explore the performance of different solvers. Using a benchmark analysis, we will see how well they can tackle problems of varying size (e.g. number of planning units) and complexity (e.g. adding boundary length penalties to reduce spatial fragmentation). Since users working in governmental and non-governmental organizations may need to purchase licenses for _IBM CPLEX_ or _Gurobi_ to use them, this vignette also aims to provide insight into whether the potential benefits of purchasing such licenses is worthwhile. Indeed -- depending on the size and complexity of a given conservation planning problem -- solvers based on open source software may only take slightly longer than commercial solvers. +In this vignette, we will explore the performance of different solvers. Using a benchmark analysis, we will see how well they can tackle problems of varying size (e.g., number of planning units) and complexity (e.g., adding boundary length penalties to reduce spatial fragmentation). Since users working in governmental and non-governmental organizations may need to purchase licenses for _IBM CPLEX_ or _Gurobi_ to use them, this vignette also aims to provide insight into whether the potential benefits of purchasing such licenses is worthwhile. Indeed -- depending on the size and complexity of a given conservation planning problem -- solvers based on open source software may only take slightly longer than commercial solvers. -# Methods +## Methods -This vignette will report the results of a benchmark analysis. To reduce computational burden, we previously completed the benchmark analysis and [uploaded the results to an online repository](https://github.com/prioritizr/benchmark/releases) ([code available online](https://github.com/prioritizr/benchmark)). This analysis involved generating prioritizations using different solvers and recording how long it took for the solvers to finish. To help understand the factors that influence how long it takes for solvers to generate a prioritization, we examined a suite of conservation planning problems with varying size (i.e. number of planning units), complexity (i.e. varying penalties to reduce spatial fragmentation), and with different objective functions (i.e. [metric used to evaluate competing solutions](https://prioritizr.net/reference/objectives.html)). In this section, we will download the results for the previously completed benchmark analysis and examine the parameters used to conduct it. +This vignette will report the results of a benchmark analysis. To reduce computational burden, we previously completed the benchmark analysis and [uploaded the results to an online repository](https://github.com/prioritizr/benchmark/releases) ([code available online](https://github.com/prioritizr/benchmark)). This analysis involved generating prioritizations using different solvers and recording how long it took for the solvers to finish. To help understand the factors that influence how long it takes for solvers to generate a prioritization, we examined a suite of conservation planning problems with varying size (i.e., number of planning units), complexity (i.e., varying penalties to reduce spatial fragmentation), and with different objective functions (i.e., [metric used to evaluate competing solutions](https://prioritizr.net/reference/objectives.html)). In this section, we will download the results for the previously completed benchmark analysis and examine the parameters used to conduct it. -## Set up +### Set up To start off, we will the load packages used in this vignette. @@ -58,7 +58,7 @@ library(units) library(dplyr) ``` -## Download benchmark results +### Download benchmark results Let's download the results of the benchmark analysis. This code will save the results to a temporary folder on your computer. Please note that downloading the results might take several minutes to complete depending on your Internet connection. If you are unable to download the results onto your computer, you can view the graphs shown in this vignette. You only need to run the code in this vignette if you wish to explore certain aspects of the results yourself. @@ -73,7 +73,7 @@ pb_download( load(file.path(tempdir(), "results.rda")) ``` -## Benchmark parameters +### Benchmark parameters After downloading the benchmark results, let's have a look at the parameters that were used to conduct it. Note that all benchmark scenarios have 72 features. @@ -82,11 +82,11 @@ After downloading the benchmark results, let's have a look at the parameters tha n_planning_units <- unique(benchmark_results$number_of_planning_units) print(n_planning_units) -# number of features (e.g. number of different species examined) +# number of features (e.g., number of different species examined) unique(benchmark_results$number_features) # representation targets, -# units are proportion of the total amount of each feature (e.g. 0.1 = 10%) +# units are proportion of the total amount of each feature (e.g., 0.1 = 10%) unique(benchmark_results$relative_target) # number of planning units @@ -107,7 +107,7 @@ boundary_penalty_values$add_min_set_objective ## boundary penalty values for min shortfall objective function boundary_penalty_values$add_min_shortfall_objective -# budgets examined for budget-limited objectives (e.g. 0.1 = 10% of total cost) +# budgets examined for budget-limited objectives (e.g., 0.1 = 10% of total cost) ## note that the min set objective function does not use a budget, ## and thus it has a NA value tibble(objective = unique(benchmark_results$objective), @@ -115,7 +115,7 @@ tibble(objective = unique(benchmark_results$objective), ``` -## Helper function +### Helper function Now we will define a helper function to quickly plot the results from the benchmark analysis. This will be helpful for interpreting the results of the benchmark analysis in the following section. @@ -208,9 +208,9 @@ plot_benchmark <- function( } ``` -# Results +## Results -We will now inspect the results of the benchmark analysis. The `benchmark_results` object is a table (i.e. `tibble()`) containing information for each benchmark run (e.g. run time), and the `solution_raster_data` object (i.e. `RasterStack`) contains the prioritizations generated for each benchmark run. +We will now inspect the results of the benchmark analysis. The `benchmark_results` object is a table (i.e., `tibble()`) containing information for each benchmark run (e.g., run time), and the `solution_raster_data` object (i.e., `RasterStack`) contains the prioritizations generated for each benchmark run. ```{r "preview_results"} # preview results @@ -233,7 +233,7 @@ theme(axis.text.x = element_text(size = 7)) + labs(x = "Solver", y = "Run time (hours)") ``` -## Minimum set results (no boundary penalty) +### Minimum set results (no boundary penalty) Now, let's investigate the solver behavior in more detail. Specifically, we will examine the benchmark results generated for the minimum set objective function. This is because the minimum set objective function is the most commonly used objective function in systematic conservation, due to the fact that it is used by the [_Marxan_ decision support software](https://marxansolutions.org/). To begin with, let's examine the results for the smallest and simplest conservation planning problems examined in the benchmark analysis. Here, all prioritizations were generated using problems that involved `r formatC(n_planning_units[1], big.mark = ",")` planning units and did not contain any boundary length penalties. Since all benchmark scenarios have 72 features -- as mentioned earlier -- all of these prioritizations were generated with 72 features. When looking at the results, we can see that all solvers solve the problem in a comparable amount of time across all targets investigated. @@ -244,7 +244,7 @@ plot_benchmark( boundary_penalty = 0) ``` -Next, let's look at the results for a more realistic problem involving `r formatC(n_planning_units[2], big.mark = ",")` planning units and see how the timing of the different solvers used compares. Note that all other factors (e.g. absence of boundary length penalties) are the same as for the previous graph. +Next, let's look at the results for a more realistic problem involving `r formatC(n_planning_units[2], big.mark = ",")` planning units and see how the timing of the different solvers used compares. Note that all other factors (e.g., absence of boundary length penalties) are the same as for the previous graph. ```{r "min_set_pu_n_2"} plot_benchmark( @@ -271,7 +271,7 @@ plot_benchmark( boundary_penalty = 0) ``` -To get a better sense of how the faster solvers (i.e. based on _CBC_, _IBM CPLEX_, _Gurobi_) compare for such a large problem, let's take a closer look at these three solvers. Interestingly, we can see that the solver based on the open source _CBC_ software is slightly faster -- by a few minutes -- than the other solvers. +To get a better sense of how the faster solvers (i.e., based on _CBC_, _IBM CPLEX_, _Gurobi_) compare for such a large problem, let's take a closer look at these three solvers. Interestingly, we can see that the solver based on the open source _CBC_ software is slightly faster -- by a few minutes -- than the other solvers. ```{r "min_set_pu_n_4_fast_only"} plot_benchmark( @@ -282,9 +282,9 @@ plot_benchmark( ``` -## Minimum set results with low boundary penalty +### Minimum set results with low boundary penalty -Now let's at the same problem types, but this time with a low boundary length penalty value added to the problem formulation. To start with, we will look at scenarios with a low `boundary_penalty` value (i.e. $`r boundary_penalty_values$add_min_set_objective[2]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. +Now let's at the same problem types, but this time with a low boundary length penalty value added to the problem formulation. To start with, we will look at scenarios with a low `boundary_penalty` value (i.e., $`r boundary_penalty_values$add_min_set_objective[2]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. ```{r "min_set_pu_n_1_with_low_boundary_penalty"} plot_benchmark( @@ -330,9 +330,9 @@ plot_benchmark( solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver")) ``` -## Minimum set results with high boundary penalty +### Minimum set results with high boundary penalty -Now let's look at the same problem types, but this time with a high boundary length penalty parameter added to the problem formulation (i.e. $`r boundary_penalty_values$add_min_set_objective[3]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. Although there are some differences between the solvers, they all have very similar run times (i.e. all less than one second). +Now let's look at the same problem types, but this time with a high boundary length penalty parameter added to the problem formulation (i.e., $`r boundary_penalty_values$add_min_set_objective[3]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. Although there are some differences between the solvers, they all have very similar run times (i.e., all less than one second). ```{r "min_set_pu_n_1_with_high_boundary_penalty"} plot_benchmark( @@ -378,7 +378,7 @@ plot_benchmark( solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver")) ``` -## Minimize shortfall results (no boundary penalty) +### Minimize shortfall results (no boundary penalty) Now, let's investigate the solver behavior for the minimum shortfall objective function. Let's start with the smallest problem size examined. All benchmark scenarios have 72 features. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. We can see that all solvers solve the problem in a comparable amount of time across all targets investigated. @@ -407,7 +407,7 @@ plot_benchmark( boundary_penalty = 0) ``` -Finally, let's look at timing comparisons for a large problem with `r formatC(n_planning_units[4], big.mark = ",")` planning units. We can see that all the open source solvers (i.e. _CBC_, _Rsymphony_ and _lpsymphony_) take a lot longer than the commercial solvers (i.e. _IBM CPLEX_ and _Guorbi_). +Finally, let's look at timing comparisons for a large problem with `r formatC(n_planning_units[4], big.mark = ",")` planning units. We can see that all the open source solvers (i.e., _CBC_, _Rsymphony_ and _lpsymphony_) take a lot longer than the commercial solvers (i.e., _IBM CPLEX_ and _Guorbi_). ```{r "min_short_pu_n_4"} plot_benchmark( @@ -416,9 +416,9 @@ plot_benchmark( boundary_penalty = 0) ``` -## Minimize shortfall results with low boundary penalty +### Minimize shortfall results with low boundary penalty -Now let's look at the same problem type, but this time with a low `boundary_penalty` parameter added to the problem formulation (i.e. $`r boundary_penalty_values$add_min_shortfall_objective[2]`$). Let's start again with the smallest problem size examined. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. +Now let's look at the same problem type, but this time with a low `boundary_penalty` parameter added to the problem formulation (i.e., $`r boundary_penalty_values$add_min_shortfall_objective[2]`$). Let's start again with the smallest problem size examined. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. ```{r "min_short_pu_n_1_with_low_boundary_penalty"} plot_benchmark( @@ -454,7 +454,7 @@ plot_benchmark( boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[2]) ``` -To get a better sense of how the faster solvers compare (i.e. _CBC_, _IBM CPLEX_, and _Gurobi_), let's take a closer look at these three solvers. We can see that the _CBC_ solver takes a lot longer to generate prioritizations than the _IBM CPLEX_ and _Gurobi_ solvers. This result suggests that _IBM CPLEX_ and _Gurobi_ could be really beneficial for large-scale conservation planning problems with boundary length penalties and the minimum shortfall objective function. +To get a better sense of how the faster solvers compare (i.e., _CBC_, _IBM CPLEX_, and _Gurobi_), let's take a closer look at these three solvers. We can see that the _CBC_ solver takes a lot longer to generate prioritizations than the _IBM CPLEX_ and _Gurobi_ solvers. This result suggests that _IBM CPLEX_ and _Gurobi_ could be really beneficial for large-scale conservation planning problems with boundary length penalties and the minimum shortfall objective function. ```{r "min_short_pu_n_4_and_boundary_penalty_and_fast_solvers"} plot_benchmark( @@ -464,7 +464,7 @@ plot_benchmark( solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver")) ``` -## Minimize shortfall results with high boundary penalty +### Minimize shortfall results with high boundary penalty Now let's look at the same problem types, but this time with a higher `boundary_penalty` parameter added to the problem formulation ($`r boundary_penalty_values$add_min_shortfall_objective[3]`$). Let's start again with the smallest problem size examined. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. @@ -475,7 +475,7 @@ plot_benchmark( boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[3]) ``` -Next, let's look at the results for a greater number of planning units (i.e. `r formatC(n_planning_units[2], big.mark = ",")` planning units) and see how the timings compare. All the solvers have a similar run time now. Interestingly, the _Gurobi_ solver is the slowest -- but only by a couple of minutes -- for these benchmark parameters. +Next, let's look at the results for a greater number of planning units (i.e., `r formatC(n_planning_units[2], big.mark = ",")` planning units) and see how the timings compare. All the solvers have a similar run time now. Interestingly, the _Gurobi_ solver is the slowest -- but only by a couple of minutes -- for these benchmark parameters. ```{r "min_short_pu_n_2 with boundary penalty"} plot_benchmark( @@ -502,6 +502,6 @@ plot_benchmark( boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[3]) ``` -# Conclusion +## Conclusion -The benchmark results demonstrate that the time required to solve a conservation planning problem can vary considerably depending on the size and complexity of the problem, and also the solver used to generate the prioritization. Indeed, some solvers (e.g. _Rsymphony_ solver) may require many hours to solve a problem that other solvers (e.g. _CBC_ or _Gurobi_ solvers) can solve within minutes. Broadly speaking, we recommend using the _Gurobi_ and _IBM CPLEX_ solvers where possible. This is because they often have the best performance. Although academics can obtain a special license to use these solvers at no cost, conservation planners working in governmental or non-governmental organizations may not have access to these solvers. In such cases, we recommend using the _CBC_ solver because it generally has better performance than the other open source solvers (i.e. the _Rsymphony_ and _lpsymphony_ solvers). Since the _CBC_ solver did not always have better performance than the other open source solvers, we recommend trying the _lpsymphony_ solver if the _CBC_ solver is taking a long time to solve a particular problem. +The benchmark results demonstrate that the time required to solve a conservation planning problem can vary considerably depending on the size and complexity of the problem, and also the solver used to generate the prioritization. Indeed, some solvers (e.g., _Rsymphony_ solver) may require many hours to solve a problem that other solvers (e.g., _CBC_ or _Gurobi_ solvers) can solve within minutes. Broadly speaking, we recommend using the _Gurobi_ and _IBM CPLEX_ solvers where possible. This is because they often have the best performance. Although academics can obtain a special license to use these solvers at no cost, conservation planners working in governmental or non-governmental organizations may not have access to these solvers. In such cases, we recommend using the _CBC_ solver because it generally has better performance than the other open source solvers (i.e., the _Rsymphony_ and _lpsymphony_ solvers). Since the _CBC_ solver did not always have better performance than the other open source solvers, we recommend trying the _lpsymphony_ solver if the _CBC_ solver is taking a long time to solve a particular problem. diff --git a/inst/doc/solver_benchmark.html b/inst/doc/solver_benchmarks.html similarity index 99% rename from inst/doc/solver_benchmark.html rename to inst/doc/solver_benchmarks.html index 4ee814d72..6f5f55d6c 100644 --- a/inst/doc/solver_benchmark.html +++ b/inst/doc/solver_benchmarks.html @@ -12,7 +12,7 @@ -Solver Benchmarks +Solver benchmarks @@ -137,7 +137,7 @@ -

    Solver Benchmarks

    +

    Solver benchmarks

    @@ -163,16 +163,16 @@

    Solver Benchmarks

    -
    -

    Introduction

    -

    The prioritizr R package supports a variety of optimization solvers for generating prioritizations. Specifically, the following functions can be used: add_gurobi_solver() (interfaces with the Gurobi software), add_cplex_solver() (interfaces with the IBM CPLEX software), add_cbc_solver() (interfaces with the CBC software using the rcbc R package), add_rsymphony_solver() (interfaces with the SYMPHONY software using the Rsymphony R package), and the add_lpsymphony_solver() function (interfaces with the SYMPHONY software using the lpsymphony R package). Broadly speaking, IBM CPLEX and Gurobi tend to be the fastest among the supported solvers. Although they are both commercial software, special academic licenses are available at no cost.

    -

    In this vignette, we will explore the performance of different solvers. Using a benchmark analysis, we will see how well they can tackle problems of varying size (e.g. number of planning units) and complexity (e.g. adding boundary length penalties to reduce spatial fragmentation). Since users working in governmental and non-governmental organizations may need to purchase licenses for IBM CPLEX or Gurobi to use them, this vignette also aims to provide insight into whether the potential benefits of purchasing such licenses is worthwhile. Indeed – depending on the size and complexity of a given conservation planning problem – solvers based on open source software may only take slightly longer than commercial solvers.

    +
    +

    Introduction

    +

    The prioritizr R package supports a variety of optimization solvers for generating prioritizations. Specifically, the following functions can be used: add_gurobi_solver() (interfaces with the Gurobi software), add_cplex_solver() (interfaces with the IBM CPLEX software), add_cbc_solver() (interfaces with the CBC software using the rcbc R package), add_rsymphony_solver() (interfaces with the SYMPHONY software using the Rsymphony R package), and the add_lpsymphony_solver() function (interfaces with the SYMPHONY software using the lpsymphony R package). Broadly speaking, IBM CPLEX and Gurobi tend to be the fastest among the supported solvers. Although they are both commercial software, special academic licenses are available at no cost.

    +

    In this vignette, we will explore the performance of different solvers. Using a benchmark analysis, we will see how well they can tackle problems of varying size (e.g., number of planning units) and complexity (e.g., adding boundary length penalties to reduce spatial fragmentation). Since users working in governmental and non-governmental organizations may need to purchase licenses for IBM CPLEX or Gurobi to use them, this vignette also aims to provide insight into whether the potential benefits of purchasing such licenses is worthwhile. Indeed – depending on the size and complexity of a given conservation planning problem – solvers based on open source software may only take slightly longer than commercial solvers.

    -
    -

    Methods

    -

    This vignette will report the results of a benchmark analysis. To reduce computational burden, we previously completed the benchmark analysis and uploaded the results to an online repository (code available online). This analysis involved generating prioritizations using different solvers and recording how long it took for the solvers to finish. To help understand the factors that influence how long it takes for solvers to generate a prioritization, we examined a suite of conservation planning problems with varying size (i.e. number of planning units), complexity (i.e. varying penalties to reduce spatial fragmentation), and with different objective functions (i.e. metric used to evaluate competing solutions). In this section, we will download the results for the previously completed benchmark analysis and examine the parameters used to conduct it.

    -
    -

    Set up

    +
    +

    Methods

    +

    This vignette will report the results of a benchmark analysis. To reduce computational burden, we previously completed the benchmark analysis and uploaded the results to an online repository (code available online). This analysis involved generating prioritizations using different solvers and recording how long it took for the solvers to finish. To help understand the factors that influence how long it takes for solvers to generate a prioritization, we examined a suite of conservation planning problems with varying size (i.e., number of planning units), complexity (i.e., varying penalties to reduce spatial fragmentation), and with different objective functions (i.e., metric used to evaluate competing solutions). In this section, we will download the results for the previously completed benchmark analysis and examine the parameters used to conduct it.

    +
    +

    Set up

    To start off, we will the load packages used in this vignette.

    # load packages
     library(prioritizr)
    @@ -181,8 +181,8 @@ 

    Set up

    library(units) library(dplyr)
    -
    -

    Download benchmark results

    +
    +

    Download benchmark results

    Let’s download the results of the benchmark analysis. This code will save the results to a temporary folder on your computer. Please note that downloading the results might take several minutes to complete depending on your Internet connection. If you are unable to download the results onto your computer, you can view the graphs shown in this vignette. You only need to run the code in this vignette if you wish to explore certain aspects of the results yourself.

    # download data to temporary folder
     pb_download(
    @@ -193,18 +193,18 @@ 

    Download benchmark results

    # load benchmark results load(file.path(tempdir(), "results.rda"))
    -
    -

    Benchmark parameters

    +
    +

    Benchmark parameters

    After downloading the benchmark results, let’s have a look at the parameters that were used to conduct it. Note that all benchmark scenarios have 72 features.

    # numbers of planning units examined the benchmark analysis
     n_planning_units <- unique(benchmark_results$number_of_planning_units)
     print(n_planning_units)
    ## [1]   1478  12902 102242 606180
    -
    # number of features (e.g. number of different species examined)
    +
    # number of features (e.g., number of different species examined)
     unique(benchmark_results$number_features)
    ## [1] 72
    # representation targets,
    -# units are proportion of the total amount of each feature (e.g. 0.1 = 10%)
    +# units are proportion of the total amount of each feature (e.g., 0.1 = 10%)
     unique(benchmark_results$relative_target)
    ## [1] 0.10 0.15 0.20 0.30
    # number of planning units
    @@ -225,7 +225,7 @@ 

    Benchmark parameters

    ## boundary penalty values for min shortfall objective function
     boundary_penalty_values$add_min_shortfall_objective
    ## [1] 0e+00 1e-14 1e-13
    -
    # budgets examined for budget-limited objectives (e.g. 0.1 = 10% of total cost)
    +
    # budgets examined for budget-limited objectives (e.g., 0.1 = 10% of total cost)
     ## note that the min set objective function does not use a budget,
     ## and thus it has a NA value
     tibble(objective = unique(benchmark_results$objective),
    @@ -236,8 +236,8 @@ 

    Benchmark parameters

    ## 1 add_min_set_objective NA ## 2 add_min_shortfall_objective 0.1
    -
    -

    Helper function

    +
    +

    Helper function

    Now we will define a helper function to quickly plot the results from the benchmark analysis. This will be helpful for interpreting the results of the benchmark analysis in the following section.

    # define helper function to create plots
     plot_benchmark <- function(
    @@ -327,9 +327,9 @@ 

    Helper function

    }
    -
    -

    Results

    -

    We will now inspect the results of the benchmark analysis. The benchmark_results object is a table (i.e. tibble()) containing information for each benchmark run (e.g. run time), and the solution_raster_data object (i.e. RasterStack) contains the prioritizations generated for each benchmark run.

    +
    +

    Results

    +

    We will now inspect the results of the benchmark analysis. The benchmark_results object is a table (i.e., tibble()) containing information for each benchmark run (e.g., run time), and the solution_raster_data object (i.e., RasterStack) contains the prioritizations generated for each benchmark run.

    # preview results
     print(benchmark_results)
    ## # A tibble: 480 × 21
    @@ -407,15 +407,15 @@ 

    Results

    theme(axis.text.x = element_text(size = 7)) + labs(x = "Solver", y = "Run time (hours)")

    -
    -

    Minimum set results (no boundary penalty)

    +
    +

    Minimum set results (no boundary penalty)

    Now, let’s investigate the solver behavior in more detail. Specifically, we will examine the benchmark results generated for the minimum set objective function. This is because the minimum set objective function is the most commonly used objective function in systematic conservation, due to the fact that it is used by the Marxan decision support software. To begin with, let’s examine the results for the smallest and simplest conservation planning problems examined in the benchmark analysis. Here, all prioritizations were generated using problems that involved 1,478 planning units and did not contain any boundary length penalties. Since all benchmark scenarios have 72 features – as mentioned earlier – all of these prioritizations were generated with 72 features. When looking at the results, we can see that all solvers solve the problem in a comparable amount of time across all targets investigated.

    plot_benchmark(
       objective = "add_min_set_objective",
       n_pu = n_planning_units[1],
       boundary_penalty = 0)

    -

    Next, let’s look at the results for a more realistic problem involving 12,902 planning units and see how the timing of the different solvers used compares. Note that all other factors (e.g. absence of boundary length penalties) are the same as for the previous graph.

    +

    Next, let’s look at the results for a more realistic problem involving 12,902 planning units and see how the timing of the different solvers used compares. Note that all other factors (e.g., absence of boundary length penalties) are the same as for the previous graph.

    plot_benchmark(
       objective = "add_min_set_objective",
       n_pu = n_planning_units[2],
    @@ -433,7 +433,7 @@ 

    Minimum set results (no boundary penalty)

    n_pu = n_planning_units[4], boundary_penalty = 0)

    -

    To get a better sense of how the faster solvers (i.e. based on CBC, IBM CPLEX, Gurobi) compare for such a large problem, let’s take a closer look at these three solvers. Interestingly, we can see that the solver based on the open source CBC software is slightly faster – by a few minutes – than the other solvers.

    +

    To get a better sense of how the faster solvers (i.e., based on CBC, IBM CPLEX, Gurobi) compare for such a large problem, let’s take a closer look at these three solvers. Interestingly, we can see that the solver based on the open source CBC software is slightly faster – by a few minutes – than the other solvers.

    plot_benchmark(
       objective = "add_min_set_objective",
       n_pu = n_planning_units[4],
    @@ -441,9 +441,9 @@ 

    Minimum set results (no boundary penalty)

    solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver"))

    -
    -

    Minimum set results with low boundary penalty

    -

    Now let’s at the same problem types, but this time with a low boundary length penalty value added to the problem formulation. To start with, we will look at scenarios with a low boundary_penalty value (i.e. \(10^{-5}\)). Let’s start again with the smallest problem size we’ve benchmarked. This problem has only 1,478 planning units.

    +
    +

    Minimum set results with low boundary penalty

    +

    Now let’s at the same problem types, but this time with a low boundary length penalty value added to the problem formulation. To start with, we will look at scenarios with a low boundary_penalty value (i.e., \(10^{-5}\)). Let’s start again with the smallest problem size we’ve benchmarked. This problem has only 1,478 planning units.

    plot_benchmark(
       objective = "add_min_set_objective",
       n_pu = n_planning_units[1],
    @@ -475,9 +475,9 @@ 

    Minimum set results with low boundary penalty

    solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver"))

    -
    -

    Minimum set results with high boundary penalty

    -

    Now let’s look at the same problem types, but this time with a high boundary length penalty parameter added to the problem formulation (i.e. \(0.001\)). Let’s start again with the smallest problem size we’ve benchmarked. This problem has only 1,478 planning units. Although there are some differences between the solvers, they all have very similar run times (i.e. all less than one second).

    +
    +

    Minimum set results with high boundary penalty

    +

    Now let’s look at the same problem types, but this time with a high boundary length penalty parameter added to the problem formulation (i.e., \(0.001\)). Let’s start again with the smallest problem size we’ve benchmarked. This problem has only 1,478 planning units. Although there are some differences between the solvers, they all have very similar run times (i.e., all less than one second).

    plot_benchmark(
       objective = "add_min_set_objective",
       n_pu = n_planning_units[1],
    @@ -509,8 +509,8 @@ 

    Minimum set results with high boundary penalty

    solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver"))

    -
    -

    Minimize shortfall results (no boundary penalty)

    +
    +

    Minimize shortfall results (no boundary penalty)

    Now, let’s investigate the solver behavior for the minimum shortfall objective function. Let’s start with the smallest problem size examined. All benchmark scenarios have 72 features. This problem has only 1,478 planning units. We can see that all solvers solve the problem in a comparable amount of time across all targets investigated.

    plot_benchmark(
       objective = "add_min_shortfall_objective",
    @@ -529,16 +529,16 @@ 

    Minimize shortfall results (no boundary penalty)

    n_pu = n_planning_units[3], boundary_penalty = 0)

    -

    Finally, let’s look at timing comparisons for a large problem with 606,180 planning units. We can see that all the open source solvers (i.e. CBC, Rsymphony and lpsymphony) take a lot longer than the commercial solvers (i.e. IBM CPLEX and Guorbi).

    +

    Finally, let’s look at timing comparisons for a large problem with 606,180 planning units. We can see that all the open source solvers (i.e., CBC, Rsymphony and lpsymphony) take a lot longer than the commercial solvers (i.e., IBM CPLEX and Guorbi).

    plot_benchmark(
       objective = "add_min_shortfall_objective",
       n_pu = n_planning_units[4],
       boundary_penalty = 0)

    -
    -

    Minimize shortfall results with low boundary penalty

    -

    Now let’s look at the same problem type, but this time with a low boundary_penalty parameter added to the problem formulation (i.e. \(10^{-14}\)). Let’s start again with the smallest problem size examined. This problem has only 1,478 planning units.

    +
    +

    Minimize shortfall results with low boundary penalty

    +

    Now let’s look at the same problem type, but this time with a low boundary_penalty parameter added to the problem formulation (i.e., \(10^{-14}\)). Let’s start again with the smallest problem size examined. This problem has only 1,478 planning units.

    plot_benchmark(
       objective = "add_min_shortfall_objective",
       n_pu = n_planning_units[1],
    @@ -562,7 +562,7 @@ 

    Minimize shortfall results with low boundary penalty

    n_pu = n_planning_units[4], boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[2])

    -

    To get a better sense of how the faster solvers compare (i.e. CBC, IBM CPLEX, and Gurobi), let’s take a closer look at these three solvers. We can see that the CBC solver takes a lot longer to generate prioritizations than the IBM CPLEX and Gurobi solvers. This result suggests that IBM CPLEX and Gurobi could be really beneficial for large-scale conservation planning problems with boundary length penalties and the minimum shortfall objective function.

    +

    To get a better sense of how the faster solvers compare (i.e., CBC, IBM CPLEX, and Gurobi), let’s take a closer look at these three solvers. We can see that the CBC solver takes a lot longer to generate prioritizations than the IBM CPLEX and Gurobi solvers. This result suggests that IBM CPLEX and Gurobi could be really beneficial for large-scale conservation planning problems with boundary length penalties and the minimum shortfall objective function.

    plot_benchmark(
       objective = "add_min_shortfall_objective",
       n_pu = n_planning_units[4],
    @@ -570,15 +570,15 @@ 

    Minimize shortfall results with low boundary penalty

    solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver"))

    -
    -

    Minimize shortfall results with high boundary penalty

    +
    +

    Minimize shortfall results with high boundary penalty

    Now let’s look at the same problem types, but this time with a higher boundary_penalty parameter added to the problem formulation (\(10^{-13}\)). Let’s start again with the smallest problem size examined. This problem has only 1,478 planning units.

    plot_benchmark(
       objective = "add_min_shortfall_objective",
       n_pu = n_planning_units[1],
       boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[3])

    -

    Next, let’s look at the results for a greater number of planning units (i.e. 12,902 planning units) and see how the timings compare. All the solvers have a similar run time now. Interestingly, the Gurobi solver is the slowest – but only by a couple of minutes – for these benchmark parameters.

    +

    Next, let’s look at the results for a greater number of planning units (i.e., 12,902 planning units) and see how the timings compare. All the solvers have a similar run time now. Interestingly, the Gurobi solver is the slowest – but only by a couple of minutes – for these benchmark parameters.

    plot_benchmark(
       objective = "add_min_shortfall_objective",
       n_pu = n_planning_units[2],
    @@ -598,9 +598,9 @@ 

    Minimize shortfall results with high boundary penalty

    -
    -

    Conclusion

    -

    The benchmark results demonstrate that the time required to solve a conservation planning problem can vary considerably depending on the size and complexity of the problem, and also the solver used to generate the prioritization. Indeed, some solvers (e.g. Rsymphony solver) may require many hours to solve a problem that other solvers (e.g. CBC or Gurobi solvers) can solve within minutes. Broadly speaking, we recommend using the Gurobi and IBM CPLEX solvers where possible. This is because they often have the best performance. Although academics can obtain a special license to use these solvers at no cost, conservation planners working in governmental or non-governmental organizations may not have access to these solvers. In such cases, we recommend using the CBC solver because it generally has better performance than the other open source solvers (i.e. the Rsymphony and lpsymphony solvers). Since the CBC solver did not always have better performance than the other open source solvers, we recommend trying the lpsymphony solver if the CBC solver is taking a long time to solve a particular problem.

    +
    +

    Conclusion

    +

    The benchmark results demonstrate that the time required to solve a conservation planning problem can vary considerably depending on the size and complexity of the problem, and also the solver used to generate the prioritization. Indeed, some solvers (e.g., Rsymphony solver) may require many hours to solve a problem that other solvers (e.g., CBC or Gurobi solvers) can solve within minutes. Broadly speaking, we recommend using the Gurobi and IBM CPLEX solvers where possible. This is because they often have the best performance. Although academics can obtain a special license to use these solvers at no cost, conservation planners working in governmental or non-governmental organizations may not have access to these solvers. In such cases, we recommend using the CBC solver because it generally has better performance than the other open source solvers (i.e., the Rsymphony and lpsymphony solvers). Since the CBC solver did not always have better performance than the other open source solvers, we recommend trying the lpsymphony solver if the CBC solver is taking a long time to solve a particular problem.

    diff --git a/inst/doc/tasmania.Rmd b/inst/doc/tasmania.Rmd deleted file mode 100644 index 62aaddab1..000000000 --- a/inst/doc/tasmania.Rmd +++ /dev/null @@ -1,225 +0,0 @@ ---- -title: "Tasmania Tutorial" -output: - rmarkdown::html_vignette: - toc: true - fig_caption: true - self_contained: yes -fontsize: 11pt -documentclass: article -bibliography: references.bib -csl: reference-style.csl -vignette: > - %\VignetteIndexEntry{Tasmania Tutorial} - %\VignetteEngine{knitr::rmarkdown_notangle} ---- - -```{r, include = FALSE} -h <- 3.5 -w <- 3.5 -is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", - "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) -knitr::opts_chunk$set(fig.align = "center", eval = !is_check) -``` - -## Introduction - -The aim of this tutorial is to provide a worked example of how vector-based data can be used to develop conservation prioritizations using the _prioritizr R_ package. It is also written for conservation planners that have used the _Marxan_ decision support tool [@r3] and are interested in applying the _prioritizr R_ package to their own work. The dataset used in this tutorial was obtained from the _Introduction to Marxan_ course. This data was originally a subset of a larger spatial prioritization project performed under contract to Australia's Department of Environment and Water Resources [@r30]. - -Please note that this tutorial uses data from the _prioritizrdata R_, so ensure that it is installed before trying out the code yourself. - -## Exploring the data - -This dataset contains two items. First, a spatial planning unit layer that has an attribute table which contains three columns: integer unique identifiers ("id"), unimproved land values ("cost"), and their existing level of protection ("status"). Units with 50 % or more of their area contained in protected areas are associated with a status of 2, otherwise they are associated with a value of 0. If you are familiar with the _Marxan_ decision support tool, then you will notice that these columns are formatted in a similar manner to the input data for _Marxan_. For _Marxan_ input data, planning units must be described in a table containing one row for each planning unit with a unique identifier and the corresponding cost. - -The second item in this dataset is the raster-based feature data. Specifically, the feature data is expressed as a stack of rasters (termed a `RasterStack` object). Here each layer in the stack represents the distribution of a different vegetation class in Tasmania, Australia. There are 62 vegetation classes in total. For a given layer, pixel values indicate the presence (value of 1) or absence (value of 0) of the vegetation class in an area. - -First, load the required packages and the data. - -```{r, message = FALSE} -# load packages -library(prioritizrdata) -library(prioritizr) - -# load planning unit data -data(tas_pu) - -# load conservation feature data -data(tas_features) -``` - -Now, let's have a look at the planning unit data. - -```{r, fig.width = w, fig.height = h} -# print planning unit data -print(tas_pu) - -# plot map of planning unit costs -plot(st_as_sf(tas_pu[, "cost"]), main = "Planning unit costs") -``` - -Next, let examine the feature data. Here we will only plot the first four features as an example. The pixel values denote the presence or absence of each feature within the extent of the study area. - -```{r, fig.width = 4.5, fig.height = 4.5} -# print planning unit data -print(tas_features) - -# plot map of the first four vegetation classes -plot(tas_features[[1:4]], main = paste("Feature", 1:4)) -``` - -The planning units in this example are a spatial polygon layer---not a raster---and so there can be considerable flexibility in the shape of the planning units. That said, there is a trade-off with the efficiency of data pre-processing in vector-based planning unit data compared to raster-based planning unit data. Vector-based planning unit data generally require more time to complete pre-processing computations (e.g. overlaying the planning unit data with the feature data, or generating boundary data). As a consequence, we generally recommend using raster-based planning unit data where possible to reduce processing time---but note that this is not possible when not all planning units are equal-sized squares. Another strategy is to complete the pre-processing in other software environments (e.g. _ArcGIS_) and use the pre-processed data directly with the _prioritizr_ package. - -## _Marxan_ problem formulation - -Here, we will provide an example of using the `marxan_problem` function to build and solve a typical _Marxan_ conservation planning problem. Then we will show how this same problem can be built and solved using the fully customizable `problem` function as a comparison. - -The dataset used in this example follows many of the conventions used by the _Marxan_ decision support tool. As a consequence, it is not too difficult to format the data for use with the `marxan_problem` function. The `marxan_problem` function is essentially a wrapper to the `problem` function. This means that when we solve problems created using the `marxan_problem` function, we will solve them using exact algorithms and not the simulated annealing algorithm used by _Marxan_. - -All problem objects formulated with `marxan_problem` use the minimum set objective. Targets can be either relative or absolute, and planning units can be specified for masking in or out using the `locked_in` and `locked_out` arguments. To favor clumped solutions, use the `penalty` argument to impose a penalty on solutions with high boundary lengths (equivalent to the Boundary Length Modifier (BLM) used in _Marxan_), and the `edge_factor` argument to scale the penalty for edges that do not have neighboring planning units, such as the coastline. For simplicity we set all of the targets at the same level, 17 %, to reflect the [Aichi](https://www.cbd.int/sp/targets/) biodiversity target to "safeguard" at least 17% of terrestrial ecosystems by 2020. For example, to prioritize planning units in Tasmania that meet the 17 % representation target at the least cost. - -First, we will convert the vector-based planning unit data and raster-based feature data into the tabular formats required by the `marxan_problem` function. These formats are very similar to the formats used by _Marxan_. - -```{r} -# create table with planning unit data -pu_data <- tas_pu@data - -# print first six rows -head(pu_data) - -# create table with the feature identifiers, names, and targets -spec_data <- data.frame(id = seq_len(nlayers(tas_features)), - name = paste0("veg", seq_len(nlayers(tas_features))), - prop = 0.17) - -# print first six rows -head(spec_data) - -# create table with the planning unit vs. feature data -puvspr_data <- rij_matrix(tas_pu, tas_features) -puvspr_data <- as(puvspr_data, "dgTMatrix") -puvspr_data <- data.frame(pu = puvspr_data@j + 1, species = puvspr_data@i + 1, - amount = puvspr_data@x) - -# print first six rows -head(puvspr_data) - -# create table with the boundary data -bound_data <- boundary_matrix(tas_pu) -bound_data <- as(bound_data, "dgTMatrix") -bound_data <- data.frame(id1 = bound_data@i + 1, id2 = bound_data@j + 1, - boundary = bound_data@x) -# print first six rows -head(bound_data) -``` - -Now that we have converted the data to tabular format, we can use the `marxan_problem` function to create a conservation planning problem. - -```{r} -# create problem -p1 <- marxan_problem(pu_data, spec_data, puvspr_data, bound_data, - blm = 0.0005) - -# print problem -print(p1) -``` - -Next, we can solve the problem (see [`?solve`](https://prioritizr.net/reference/solve.html) for more information). The _prioritizr_ R package supports three different exact algorithm software packages: _gurobi_, _Rsymphony_, and _lpsymphony_. There are costs and benefits associated with each of these different solvers, but each software should return similar results. Note that you will need at least one of these package installed on your system to solve problems. We recommend using the _gurobi_ solver if possible, and have used this solver when building this tutorial. After solving the problem, we will calculate some statistics to describe the solution. Note that the planning unit selections are stored in the "solution_1" column of the solution object. - -```{r, fig.width = w, fig.height = h} -# solve problem -s1 <- solve(p1) - -# print first six rows of solution object -head(s1) - -# count number of planning units in solution -sum(s1$solution_1) - -# proportion of planning units in solution -mean(s1$solution_1) - -# calculate feature representation -r1 <- eval_feature_representation_summary(p1, s1[, "solution_1", drop = FALSE]) -print(r1) - -# visualize the solution by converting the solution to a spatial object -s1 <- SpatialPolygonsDataFrame(as(tas_pu, "SpatialPolygons"), data = s1) -s1$solution_1 <- factor(s1$solution_1) -plot(st_as_sf(s1[, "solution_1"]), pal = c("grey90", "darkgreen"), - main = "marxan_problem solution") -``` - -## General problem formulation - -Now we will formulate the exact same problem using the `problem` function and the spatially referenced data. We recommend using this approach instead of the `marxan_problem` because it is more verbose and you can specify exactly how the conservation planning problem should be formulated. - -```{r, fig.width = w, fig.height = h} -# build problem -p2 <- problem(tas_pu, tas_features, cost_column = "cost") %>% - add_min_set_objective() %>% - add_relative_targets(0.17) %>% - add_locked_in_constraints("locked_in") %>% - add_binary_decisions() %>% - add_boundary_penalties(penalty = 0.0005, edge_factor = 1) - -# print the problem -print(p2) - -# solve problem -s2 <- solve(p2) - -# calculate feature representation -r2 <- eval_feature_representation_summary(p2, s2[, "solution_1"]) -print(r2) - -# visualize solution -s2$solution_1 <- factor(s2$solution_1) -plot(st_as_sf(s2[, "solution_1"]), pal = c("grey90", "darkgreen"), - main = "problem solution") -``` - -## Selection frequencies - -Similar to the _Marxan_ decision support tool, we can generate a portfolio of solutions and compute the planning unit selection frequencies to understand their relative importance. This can be useful when trying to understand which planning units in the solution are the most irreplaceable. To do this, we will create a portfolio containing 1000 solutions within 20% of optimality, and calculate the number of times that each planning unit was selected. Note that this requires the _Gurobi_ software to be installed. - -```{r} -# create new problem with a portfolio added to it -p3 <- p2 %>% - add_gap_portfolio(number_solutions = 1000, pool_gap = 0.2) - -# print problem -print(p3) -``` - -```{r, message = FALSE, results = "hide"} -# generate solutions -s3 <- solve(p3) - -# find column numbers with the solutions -solution_columns <- which(grepl("solution", names(s3))) - -# calculate selection frequencies -s3$selection_frequencies <- rowSums(as.matrix(s3@data[, solution_columns])) -``` - -```{r, fig.width = 4.5, fig.height = 4.5} -# plot first four solutions in the portfolio -s3$solution_1 <- factor(s3$solution_1) -s3$solution_2 <- factor(s3$solution_2) -s3$solution_3 <- factor(s3$solution_3) -s3$solution_4 <- factor(s3$solution_4) -plot(st_as_sf(s3[, c("solution_1", "solution_2", "solution_3", "solution_4")]), - pal = c("grey90", "darkgreen")) -``` - -```{r, fig.width = w, fig.height = h} -# plot histogram of selection frequencies -hist(s3$selection_frequencies, main = "Selection frequencies", - xlab = "Number of times units were selected") - -# plot spatial distribution of the selection frequencies -plot(st_as_sf(s3[, "selection_frequencies"]), main = "Selection frequencies") -``` - -## References diff --git a/inst/doc/tasmania.html b/inst/doc/tasmania.html deleted file mode 100644 index 3640584b2..000000000 --- a/inst/doc/tasmania.html +++ /dev/null @@ -1,537 +0,0 @@ - - - - - - - - - - - - - - -Tasmania Tutorial - - - - - - - - - - - - - - - - - - - - - - - - - - -

    Tasmania Tutorial

    - - - - -
    -

    Introduction

    -

    The aim of this tutorial is to provide a worked example of how vector-based data can be used to develop conservation prioritizations using the prioritizr R package. It is also written for conservation planners that have used the Marxan decision support tool (Ball et al. 2009) and are interested in applying the prioritizr R package to their own work. The dataset used in this tutorial was obtained from the Introduction to Marxan course. This data was originally a subset of a larger spatial prioritization project performed under contract to Australia’s Department of Environment and Water Resources (Klein et al. 2007).

    -

    Please note that this tutorial uses data from the prioritizrdata R, so ensure that it is installed before trying out the code yourself.

    -
    -
    -

    Exploring the data

    -

    This dataset contains two items. First, a spatial planning unit layer that has an attribute table which contains three columns: integer unique identifiers (“id”), unimproved land values (“cost”), and their existing level of protection (“status”). Units with 50 % or more of their area contained in protected areas are associated with a status of 2, otherwise they are associated with a value of 0. If you are familiar with the Marxan decision support tool, then you will notice that these columns are formatted in a similar manner to the input data for Marxan. For Marxan input data, planning units must be described in a table containing one row for each planning unit with a unique identifier and the corresponding cost.

    -

    The second item in this dataset is the raster-based feature data. Specifically, the feature data is expressed as a stack of rasters (termed a RasterStack object). Here each layer in the stack represents the distribution of a different vegetation class in Tasmania, Australia. There are 62 vegetation classes in total. For a given layer, pixel values indicate the presence (value of 1) or absence (value of 0) of the vegetation class in an area.

    -

    First, load the required packages and the data.

    -
    # load packages
    -library(prioritizrdata)
    -library(prioritizr)
    -
    -# load planning unit data
    -data(tas_pu)
    -
    -# load conservation feature data
    -data(tas_features)
    -

    Now, let’s have a look at the planning unit data.

    -
    # print planning unit data
    -print(tas_pu)
    -
    ## class       : SpatialPolygonsDataFrame 
    -## features    : 1130 
    -## extent      : 298809.6, 613818.8, 5167775, 5502544  (xmin, xmax, ymin, ymax)
    -## crs         : +proj=utm +zone=55 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    -## variables   : 5
    -## names       :   id,              cost, status, locked_in, locked_out 
    -## min values  :    1, 0.192488262910798,      0,         0,          0 
    -## max values  : 1130,  61.9272727272727,      2,         1,          0
    -
    # plot map of planning unit costs
    -plot(st_as_sf(tas_pu[, "cost"]), main = "Planning unit costs")
    -

    -

    Next, let examine the feature data. Here we will only plot the first four features as an example. The pixel values denote the presence or absence of each feature within the extent of the study area.

    -
    # print planning unit data
    -print(tas_features)
    -
    ## class      : RasterStack 
    -## dimensions : 398, 359, 142882, 62  (nrow, ncol, ncell, nlayers)
    -## resolution : 1000, 1000  (x, y)
    -## extent     : 288801.7, 647801.7, 5142976, 5540976  (xmin, xmax, ymin, ymax)
    -## crs        : +proj=utm +zone=55 +south +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
    -## names      : tas_features.1, tas_features.2, tas_features.3, tas_features.4, tas_features.5, tas_features.6, tas_features.7, tas_features.8, tas_features.9, tas_features.10, tas_features.11, tas_features.12, tas_features.13, tas_features.14, tas_features.15, ... 
    -## min values :              0,              0,              0,              0,              0,              0,              0,              0,              0,               0,               0,               0,               0,               0,               0, ... 
    -## max values :              1,              1,              1,              1,              1,              1,              1,              1,              1,               1,               1,               1,               1,               1,               1, ...
    -
    # plot map of the first four vegetation classes
    -plot(tas_features[[1:4]], main = paste("Feature", 1:4))
    -

    -

    The planning units in this example are a spatial polygon layer—not a raster—and so there can be considerable flexibility in the shape of the planning units. That said, there is a trade-off with the efficiency of data pre-processing in vector-based planning unit data compared to raster-based planning unit data. Vector-based planning unit data generally require more time to complete pre-processing computations (e.g. overlaying the planning unit data with the feature data, or generating boundary data). As a consequence, we generally recommend using raster-based planning unit data where possible to reduce processing time—but note that this is not possible when not all planning units are equal-sized squares. Another strategy is to complete the pre-processing in other software environments (e.g. ArcGIS) and use the pre-processed data directly with the prioritizr package.

    -
    -
    -

    Marxan problem formulation

    -

    Here, we will provide an example of using the marxan_problem function to build and solve a typical Marxan conservation planning problem. Then we will show how this same problem can be built and solved using the fully customizable problem function as a comparison.

    -

    The dataset used in this example follows many of the conventions used by the Marxan decision support tool. As a consequence, it is not too difficult to format the data for use with the marxan_problem function. The marxan_problem function is essentially a wrapper to the problem function. This means that when we solve problems created using the marxan_problem function, we will solve them using exact algorithms and not the simulated annealing algorithm used by Marxan.

    -

    All problem objects formulated with marxan_problem use the minimum set objective. Targets can be either relative or absolute, and planning units can be specified for masking in or out using the locked_in and locked_out arguments. To favor clumped solutions, use the penalty argument to impose a penalty on solutions with high boundary lengths (equivalent to the Boundary Length Modifier (BLM) used in Marxan), and the edge_factor argument to scale the penalty for edges that do not have neighboring planning units, such as the coastline. For simplicity we set all of the targets at the same level, 17 %, to reflect the Aichi biodiversity target to “safeguard” at least 17% of terrestrial ecosystems by 2020. For example, to prioritize planning units in Tasmania that meet the 17 % representation target at the least cost.

    -

    First, we will convert the vector-based planning unit data and raster-based feature data into the tabular formats required by the marxan_problem function. These formats are very similar to the formats used by Marxan.

    -
    # create table with planning unit data
    -pu_data <- tas_pu@data
    -
    -# print first six rows
    -head(pu_data)
    -
    ##   id     cost status locked_in locked_out
    -## 0  1 60.24638      0     FALSE      FALSE
    -## 1  2 19.86301      0     FALSE      FALSE
    -## 2  3 59.68051      0     FALSE      FALSE
    -## 3  4 32.41614      0     FALSE      FALSE
    -## 4  5 26.17706      0     FALSE      FALSE
    -## 5  6 51.26218      0     FALSE      FALSE
    -
    # create table with the feature identifiers, names, and targets
    -spec_data <- data.frame(id = seq_len(nlayers(tas_features)),
    -                        name = paste0("veg", seq_len(nlayers(tas_features))),
    -                        prop = 0.17)
    -
    -# print first six rows
    -head(spec_data)
    -
    ##   id name prop
    -## 1  1 veg1 0.17
    -## 2  2 veg2 0.17
    -## 3  3 veg3 0.17
    -## 4  4 veg4 0.17
    -## 5  5 veg5 0.17
    -## 6  6 veg6 0.17
    -
    # create table with the planning unit vs. feature data
    -puvspr_data <- rij_matrix(tas_pu, tas_features)
    -puvspr_data <- as(puvspr_data, "dgTMatrix")
    -puvspr_data <- data.frame(pu = puvspr_data@j + 1, species = puvspr_data@i + 1,
    -                          amount = puvspr_data@x)
    -
    -# print first six rows
    -head(puvspr_data)
    -
    ##   pu species       amount
    -## 1  3      54 1.000000e+00
    -## 2  3      59 1.165971e+01
    -## 3  5      30 1.000000e+00
    -## 4  6      27 1.000000e+00
    -## 5  6      35 1.000000e+00
    -## 6  6      59 3.635182e-05
    -
    # create table with the boundary data
    -bound_data <- boundary_matrix(tas_pu)
    -bound_data <- as(bound_data, "dgTMatrix")
    -bound_data <- data.frame(id1 = bound_data@i + 1, id2 = bound_data@j + 1,
    -                         boundary = bound_data@x)
    -# print first six rows
    -head(bound_data)
    -
    ##   id1 id2   boundary
    -## 1   1   1  6314.6621
    -## 2   3   1  3922.9264
    -## 3   6   1   150.3778
    -## 4   2   2 23958.6982
    -## 5   4   2  3233.3056
    -## 6   7   2  4639.9220
    -

    Now that we have converted the data to tabular format, we can use the marxan_problem function to create a conservation planning problem.

    -
    # create problem
    -p1 <- marxan_problem(pu_data, spec_data, puvspr_data, bound_data,
    -                     blm = 0.0005)
    -
    -# print problem
    -print(p1)
    -
    ## Conservation Problem
    -##   planning units: data.frame (1130 units)
    -##   cost:           min: 0.19249, max: 61.92727
    -##   features:       veg1, veg2, veg3, ... (62 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
    -##   decisions:      default
    -##   constraints:    <Locked in planning units [257 locked units]>
    -##   penalties:      <Boundary penalties [edge factor (min: 1, max: 1), penalty (5e-04), zones]>
    -##   portfolio:      default
    -##   solver:         default
    -

    Next, we can solve the problem (see ?solve for more information). The prioritizr R package supports three different exact algorithm software packages: gurobi, Rsymphony, and lpsymphony. There are costs and benefits associated with each of these different solvers, but each software should return similar results. Note that you will need at least one of these package installed on your system to solve problems. We recommend using the gurobi solver if possible, and have used this solver when building this tutorial. After solving the problem, we will calculate some statistics to describe the solution. Note that the planning unit selections are stored in the “solution_1” column of the solution object.

    -
    # solve problem
    -s1 <- solve(p1)
    -
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
    -## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    -## Optimize a model with 6358 rows, 4278 columns and 14496 nonzeros
    -## Model fingerprint: 0x96b70d15
    -## Variable types: 0 continuous, 4278 integer (4278 binary)
    -## Coefficient statistics:
    -##   Matrix range     [2e-06, 4e+01]
    -##   Objective range  [5e-02, 2e+02]
    -##   Bounds range     [1e+00, 1e+00]
    -##   RHS range        [1e-01, 1e+02]
    -## Found heuristic solution: objective 30263.129158
    -## Found heuristic solution: objective 12512.071934
    -## Presolve removed 3428 rows and 2241 columns
    -## Presolve time: 0.03s
    -## Presolved: 2930 rows, 2037 columns, 6882 nonzeros
    -## Found heuristic solution: objective 11751.436316
    -## Variable types: 0 continuous, 2037 integer (2037 binary)
    -## Found heuristic solution: objective 11425.978431
    -## Root relaxation presolved: 2930 rows, 2037 columns, 6882 nonzeros
    -## 
    -## 
    -## Root relaxation: objective 1.081359e+04, 715 iterations, 0.02 seconds (0.03 work units)
    -## 
    -##     Nodes    |    Current Node    |     Objective Bounds      |     Work
    -##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    -## 
    -##      0     0 10813.5861    0  135 11425.9784 10813.5861  5.36%     -    0s
    -## 
    -## Explored 1 nodes (715 simplex iterations) in 0.05 seconds (0.08 work units)
    -## Thread count was 1 (of 8 available processors)
    -## 
    -## Solution count 4: 11426 11751.4 12512.1 30263.1 
    -## 
    -## Optimal solution found (tolerance 1.00e-01)
    -## Best objective 1.142597843052e+04, best bound 1.081358611386e+04, gap 5.3596%
    -
    # print first six rows of solution object
    -head(s1)
    -
    ##   id     cost status locked_in locked_out solution_1
    -## 0  1 60.24638      0     FALSE      FALSE          0
    -## 1  2 19.86301      0     FALSE      FALSE          0
    -## 2  3 59.68051      0     FALSE      FALSE          0
    -## 3  4 32.41614      0     FALSE      FALSE          0
    -## 4  5 26.17706      0     FALSE      FALSE          0
    -## 5  6 51.26218      0     FALSE      FALSE          0
    -
    # count number of planning units in solution
    -sum(s1$solution_1)
    -
    ## [1] 315
    -
    # proportion of planning units in solution
    -mean(s1$solution_1)
    -
    ## [1] 0.2787611
    -
    # calculate feature representation
    -r1 <- eval_feature_representation_summary(p1, s1[, "solution_1", drop = FALSE])
    -print(r1)
    -
    ## # A tibble: 62 × 5
    -##    summary feature total_amount absolute_held relative_held
    -##    <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -##  1 overall veg1            33.9          5.97         0.176
    -##  2 overall veg2           170.          42.8          0.252
    -##  3 overall veg3            24.0          5.63         0.235
    -##  4 overall veg4            32.8          8.37         0.256
    -##  5 overall veg5            24.8          4.98         0.201
    -##  6 overall veg6            22.0          7.10         0.323
    -##  7 overall veg7            16.4          3.06         0.187
    -##  8 overall veg8            43.0          8.12         0.189
    -##  9 overall veg9           388.          78.9          0.203
    -## 10 overall veg10           14.5          4.02         0.277
    -## # … with 52 more rows
    -
    # visualize the solution by converting the solution to a spatial object
    -s1 <- SpatialPolygonsDataFrame(as(tas_pu, "SpatialPolygons"), data = s1)
    -s1$solution_1 <- factor(s1$solution_1)
    -plot(st_as_sf(s1[, "solution_1"]), pal = c("grey90", "darkgreen"),
    -    main = "marxan_problem solution")
    -

    -
    -
    -

    General problem formulation

    -

    Now we will formulate the exact same problem using the problem function and the spatially referenced data. We recommend using this approach instead of the marxan_problem because it is more verbose and you can specify exactly how the conservation planning problem should be formulated.

    -
    # build problem
    -p2 <- problem(tas_pu, tas_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.17) %>%
    -      add_locked_in_constraints("locked_in") %>%
    -      add_binary_decisions() %>%
    -      add_boundary_penalties(penalty = 0.0005, edge_factor = 1)
    -
    -# print the problem
    -print(p2)
    -
    ## Conservation Problem
    -##   planning units: SpatialPolygonsDataFrame (1130 units)
    -##   cost:           min: 0.19249, max: 61.92727
    -##   features:       tas_features.1, tas_features.2, tas_features.3, ... (62 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
    -##   decisions:      Binary decision 
    -##   constraints:    <Locked in planning units [257 locked units]>
    -##   penalties:      <Boundary penalties [edge factor (min: 1, max: 1), penalty (5e-04), zones]>
    -##   portfolio:      default
    -##   solver:         default
    -
    # solve problem
    -s2 <- solve(p2)
    -
    ## Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (linux64)
    -## Thread count: 4 physical cores, 8 logical processors, using up to 1 threads
    -## Optimize a model with 6358 rows, 4278 columns and 14496 nonzeros
    -## Model fingerprint: 0x96b70d15
    -## Variable types: 0 continuous, 4278 integer (4278 binary)
    -## Coefficient statistics:
    -##   Matrix range     [2e-06, 4e+01]
    -##   Objective range  [5e-02, 2e+02]
    -##   Bounds range     [1e+00, 1e+00]
    -##   RHS range        [1e-01, 1e+02]
    -## Found heuristic solution: objective 30263.129158
    -## Found heuristic solution: objective 12512.071934
    -## Presolve removed 3428 rows and 2241 columns
    -## Presolve time: 0.03s
    -## Presolved: 2930 rows, 2037 columns, 6882 nonzeros
    -## Found heuristic solution: objective 11751.436316
    -## Variable types: 0 continuous, 2037 integer (2037 binary)
    -## Found heuristic solution: objective 11425.978431
    -## Root relaxation presolved: 2930 rows, 2037 columns, 6882 nonzeros
    -## 
    -## 
    -## Root relaxation: objective 1.081359e+04, 715 iterations, 0.02 seconds (0.03 work units)
    -## 
    -##     Nodes    |    Current Node    |     Objective Bounds      |     Work
    -##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    -## 
    -##      0     0 10813.5861    0  135 11425.9784 10813.5861  5.36%     -    0s
    -## 
    -## Explored 1 nodes (715 simplex iterations) in 0.05 seconds (0.08 work units)
    -## Thread count was 1 (of 8 available processors)
    -## 
    -## Solution count 4: 11426 11751.4 12512.1 30263.1 
    -## 
    -## Optimal solution found (tolerance 1.00e-01)
    -## Best objective 1.142597843052e+04, best bound 1.081358611386e+04, gap 5.3596%
    -
    # calculate feature representation
    -r2 <- eval_feature_representation_summary(p2, s2[, "solution_1"])
    -print(r2)
    -
    ## # A tibble: 62 × 5
    -##    summary feature         total_amount absolute_held relative_held
    -##    <chr>   <chr>                  <dbl>         <dbl>         <dbl>
    -##  1 overall tas_features.1          33.9          5.97         0.176
    -##  2 overall tas_features.2         170.          42.8          0.252
    -##  3 overall tas_features.3          24.0          5.63         0.235
    -##  4 overall tas_features.4          32.8          8.37         0.256
    -##  5 overall tas_features.5          24.8          4.98         0.201
    -##  6 overall tas_features.6          22.0          7.10         0.323
    -##  7 overall tas_features.7          16.4          3.06         0.187
    -##  8 overall tas_features.8          43.0          8.12         0.189
    -##  9 overall tas_features.9         388.          78.9          0.203
    -## 10 overall tas_features.10         14.5          4.02         0.277
    -## # … with 52 more rows
    -
    # visualize solution
    -s2$solution_1 <- factor(s2$solution_1)
    -plot(st_as_sf(s2[, "solution_1"]), pal = c("grey90", "darkgreen"),
    -    main = "problem solution")
    -

    -
    -
    -

    Selection frequencies

    -

    Similar to the Marxan decision support tool, we can generate a portfolio of solutions and compute the planning unit selection frequencies to understand their relative importance. This can be useful when trying to understand which planning units in the solution are the most irreplaceable. To do this, we will create a portfolio containing 1000 solutions within 20% of optimality, and calculate the number of times that each planning unit was selected. Note that this requires the Gurobi software to be installed.

    -
    # create new problem with a portfolio added to it
    -p3 <- p2 %>%
    -      add_gap_portfolio(number_solutions = 1000, pool_gap = 0.2)
    -
    -# print problem
    -print(p3)
    -
    ## Conservation Problem
    -##   planning units: SpatialPolygonsDataFrame (1130 units)
    -##   cost:           min: 0.19249, max: 61.92727
    -##   features:       tas_features.1, tas_features.2, tas_features.3, ... (62 features)
    -##   objective:      Minimum set objective 
    -##   targets:        Relative targets [targets (min: 0.17, max: 0.17)]
    -##   decisions:      Binary decision 
    -##   constraints:    <Locked in planning units [257 locked units]>
    -##   penalties:      <Boundary penalties [edge factor (min: 1, max: 1), penalty (5e-04), zones]>
    -##   portfolio:      Gap portfolio [number_solutions (1000), pool_gap (0.2)]
    -##   solver:         default
    -
    # generate solutions
    -s3 <- solve(p3)
    -
    -# find column numbers with the solutions
    -solution_columns <- which(grepl("solution", names(s3)))
    -
    -# calculate selection frequencies
    -s3$selection_frequencies <- rowSums(as.matrix(s3@data[, solution_columns]))
    -
    # plot first four solutions in the portfolio
    -s3$solution_1 <- factor(s3$solution_1)
    -s3$solution_2 <- factor(s3$solution_2)
    -s3$solution_3 <- factor(s3$solution_3)
    -s3$solution_4 <- factor(s3$solution_4)
    -plot(st_as_sf(s3[, c("solution_1", "solution_2", "solution_3", "solution_4")]),
    -  pal = c("grey90", "darkgreen"))
    -

    -
    # plot histogram of selection frequencies
    -hist(s3$selection_frequencies, main = "Selection frequencies",
    -     xlab = "Number of times units were selected")
    -

    -
    # plot spatial distribution of the selection frequencies
    -plot(st_as_sf(s3[, "selection_frequencies"]), main = "Selection frequencies")
    -

    -
    -
    -

    References

    -
    -
    -Ball, I.R., Possingham, H. & Watts, M.E. (2009). Marxan and relatives: Software for spatial conservation prioritisation. Spatial Conservation Prioritisation: Quantitative Methods & Computational Tools (eds A. Moilanen, K.A. Wilson & H. Possingham), pp. 185–189. Oxford University Press, Oxford, UK. -
    -
    -Klein, C., Carwardine, J., Wilson, K., Watts, M. & Possingham, H. (2007). Spatial Prioritization Approaches for the Conservation of Biodiversity in Australia: Considering Conservation Costs, Ecological & Evolutionary Processes, and Large-Intact Areas. Report to the Department of Environment; Water Resources. -
    -
    -
    - - - - - - - - - - - diff --git a/man/ConservationProblem-class.Rd b/man/ConservationProblem-class.Rd index 5afa4fd2c..5605d256b 100644 --- a/man/ConservationProblem-class.Rd +++ b/man/ConservationProblem-class.Rd @@ -7,13 +7,13 @@ \description{ This class is used to represent conservation planning problems. A conservation planning problem has spatially explicit planning units. -A prioritization involves making a decision on each planning unit (e.g. is +A prioritization involves making a decision on each planning unit (e.g., is the planning unit going to be turned into a protected area?). Each planning unit is associated with a cost that represents the cost incurred by applying the decision to the planning unit. The problem also has a set of representation targets for each feature. Further, it also has constraints used to ensure that the solution meets additional -objectives (e.g. certain areas are locked into the solution). Finally, +objectives (e.g., certain areas are locked into the solution). Finally, a conservation planning problem---unlike an optimization problem---also requires a method to solve the problem. \strong{This class represents a planning problem, to actually build and then solve a planning problem, diff --git a/man/Parameter-class.Rd b/man/Parameter-class.Rd index 8dda4d116..aa80afd78 100644 --- a/man/Parameter-class.Rd +++ b/man/Parameter-class.Rd @@ -22,7 +22,7 @@ should interact directly with this class.} \item{$default}{\code{numeric} vector of default values.} \item{$class}{\code{character} name of the class that the values inherit -from (e.g. \code{"integer"}.} +from (e.g., \code{"integer"}.} \item{$lower_limit}{\code{numeric} vector specifying the minimum permitted value for each element in \verb{$value}.} diff --git a/man/ScalarParameter-class.Rd b/man/ScalarParameter-class.Rd index ae53d73bf..f5ef8e7f8 100644 --- a/man/ScalarParameter-class.Rd +++ b/man/ScalarParameter-class.Rd @@ -21,7 +21,7 @@ This prototype is used to represent a parameter has a single value. \item{$default}{\code{numeric} scalar default value.} \item{$class}{\code{character} name of the class that \verb{$value} should -inherit from (e.g. \code{integer}).} +inherit from (e.g., \code{integer}).} \item{$lower_limit}{\code{numeric} scalar value that is the minimum value that \verb{$value} is permitted to be.} diff --git a/man/add_absolute_targets.Rd b/man/add_absolute_targets.Rd index cf24c57a6..d66423387 100644 --- a/man/add_absolute_targets.Rd +++ b/man/add_absolute_targets.Rd @@ -17,13 +17,13 @@ add_absolute_targets(x, targets) \S4method{add_absolute_targets}{ConservationProblem,character}(x, targets) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{targets}{Object that specifies the targets for each feature. See the Targets format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the targets added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the targets added to it. } \description{ diff --git a/man/add_binary_decisions.Rd b/man/add_binary_decisions.Rd index 3bb2daaff..4a643a91b 100644 --- a/man/add_binary_decisions.Rd +++ b/man/add_binary_decisions.Rd @@ -7,10 +7,10 @@ add_binary_decisions(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the decisions added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the decisions added to it. } \description{ @@ -22,7 +22,7 @@ added to a problem then this decision class will be used by default. } \details{ Conservation planning problems involve making decisions on planning -units. These decisions are then associated with actions (e.g. turning a +units. These decisions are then associated with actions (e.g., turning a planning unit into a protected area). Only a single decision should be added to a \code{ConservationProblem} object. Note that if multiple decisions are added to a problem object, then the diff --git a/man/add_boundary_penalties.Rd b/man/add_boundary_penalties.Rd index b4631aca5..cdac0205b 100644 --- a/man/add_boundary_penalties.Rd +++ b/man/add_boundary_penalties.Rd @@ -13,11 +13,11 @@ add_boundary_penalties( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{penalty}{\code{numeric} penalty that is used to scale the importance of selecting planning units that are spatially clumped together compared -to the main problem objective (e.g. solution cost when the argument to +to the main problem objective (e.g., solution cost when the argument to \code{x} has a minimum set objective per \code{\link[=add_min_set_objective]{add_min_set_objective()}}). Higher \code{penalty} values prefer solutions with a higher degree of spatial clumping, and smaller \code{penalty} values prefer solutions with a smaller @@ -42,23 +42,23 @@ represent the relative importance of clumping planning units that are allocated to the same zone. Cell values must range between 1 and -1, where negative values favor solutions that spread out planning units. The default argument to \code{zones} is an identity -matrix (i.e. a matrix with ones along the matrix diagonal and zeros +matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that penalties are incurred when neighboring planning units are not assigned to the same zone. If the cells along the matrix diagonal contain markedly smaller values than those found elsewhere in the matrix, then solutions are preferred that surround planning units with those allocated to different zones -(i.e. greater spatial fragmentation).} +(i.e., greater spatial fragmentation).} \item{data}{\code{NULL}, \code{data.frame}, \code{matrix}, or \code{Matrix} object containing the boundary data. These data describe the total amount of boundary (perimeter) length for each planning unit, and the amount of boundary (perimeter) length shared between different -planning units (i.e. planning units that are adjacent to each other). +planning units (i.e., planning units that are adjacent to each other). See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the penalties +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the penalties added to it. } \description{ @@ -91,14 +91,14 @@ This argument is the default. Note that the boundary data must be supplied using one of the other formats below if the planning unit data in the argument to \code{x} do not explicitly contain spatial information -(e.g. planning unit data are a \code{data.frame} or \code{numeric} class).} +(e.g., planning unit data are a \code{data.frame} or \code{numeric} class).} \item{\code{data} as a \code{matrix}/\code{Matrix} object}{where rows and columns represent different planning units and the value of each cell represents the amount of shared boundary length between two different planning units. Cells that occur along the matrix diagonal represent the amount of exposed boundary associated with each planning unit that has -no neighbor (e.g. these value might pertain to boundaries along a +no neighbor (e.g., these value might pertain to boundaries along a coastline).} \item{\code{data} as a \code{data.frame} object}{with the columns \code{"id1"}, @@ -107,7 +107,7 @@ identifiers (indices) for a pair of planning units, and the \code{"boundary"} column contains the amount of shared boundary length between these two planning units. This format follows the the standard \emph{Marxan} format for boundary -data (i.e. per the "bound.dat" file).} +data (i.e., per the "bound.dat" file).} } } @@ -119,11 +119,11 @@ The boundary penalties are implemented using the following equations. Let (indexed by \eqn{i} or \eqn{j}), \eqn{Z} represent the set of management zones (indexed by \eqn{z} or \eqn{y}), and \eqn{X_{iz}}{Xiz} represent the decision -variable for planning unit \eqn{i} for in zone \eqn{z} (e.g. with binary +variable for planning unit \eqn{i} for in zone \eqn{z} (e.g., with binary values one indicating if planning unit is allocated or not). Also, let \eqn{p} represent the argument to \code{penalty}, \eqn{E_z}{Ez} represent the argument to \code{edge_factor}, \eqn{B_{ij}}{Bij} represent the matrix argument -to \code{data} (e.g. generated using \code{\link[=boundary_matrix]{boundary_matrix()}}), and +to \code{data} (e.g., generated using \code{\link[=boundary_matrix]{boundary_matrix()}}), and \eqn{W_{zz}}{Wzz} represent the matrix argument to \code{zones}. \deqn{ @@ -215,7 +215,7 @@ p9 <- p5 \%>\% add_boundary_penalties(500, zone = zm9) # create zone matrix which favors clumping planning units in zones 1 and 2 # together, and favors planning units in zone 3 being spread out -# (i.e. negative clumping) +# (i.e., negative clumping) zm10 <- diag(3) zm10[3, 3] <- -1 print(zm10) diff --git a/man/add_cbc_solver.Rd b/man/add_cbc_solver.Rd index 811a9c528..cd7c60c43 100644 --- a/man/add_cbc_solver.Rd +++ b/man/add_cbc_solver.Rd @@ -16,7 +16,7 @@ add_cbc_solver( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{gap}{\code{numeric} gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. @@ -24,12 +24,12 @@ For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1\% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10\% from optimality).} +The default value is 0.1 (i.e., 10\% from optimality).} \item{time_limit}{\code{numeric} time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. \code{.Machine$integer.max}), effectively meaning that solver +(i.e., \code{.Machine$integer.max}), effectively meaning that solver will keep running until a solution within the optimality gap is found.} \item{presolve}{\code{logical} attempt to simplify the @@ -49,7 +49,7 @@ Defaults to \code{FALSE}.} \item{start_solution}{\code{NULL} or object containing the starting solution for the solver. Defaults to \code{NULL} such that no starting solution is used. To specify a starting solution, the argument to \code{start_solution} should -be in the same format as the planning units (i.e. a \code{NULL}, \code{numeric}, +be in the same format as the planning units (i.e., a \code{NULL}, \code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, or \code{\link[sf:sf]{sf::sf()}} object). See the Start solution format section for more information.} @@ -58,7 +58,7 @@ See the Start solution format section for more information.} optimization problems? Defaults to \code{TRUE}.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the solver +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the solver added to it. } \description{ @@ -78,7 +78,7 @@ Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks examining the performance of this solver for conservation planning problems have yet to be completed, preliminary analyses suggest that it performs much faster than the other open-source -solvers (i.e. \code{\link[=add_rsymphony_solver]{add_rsymphony_solver()}}, \code{\link[=add_rsymphony_solver]{add_rsymphony_solver()}}), and +solvers (i.e., \code{\link[=add_rsymphony_solver]{add_rsymphony_solver()}}, \code{\link[=add_rsymphony_solver]{add_rsymphony_solver()}}), and so we recommend using this solver if the \emph{Gurobi} and \emph{IBM CPLEX} solvers are unavailable. } diff --git a/man/add_connectivity_penalties.Rd b/man/add_connectivity_penalties.Rd index e3ca33d9e..0c2e7880c 100644 --- a/man/add_connectivity_penalties.Rd +++ b/man/add_connectivity_penalties.Rd @@ -20,11 +20,11 @@ \S4method{add_connectivity_penalties}{ConservationProblem,ANY,ANY,array}(x, penalty, zones, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{penalty}{\code{numeric} penalty that is used to scale the importance of selecting planning units with strong connectivity between them compared -to the main problem objective (e.g. solution cost when the argument to +to the main problem objective (e.g., solution cost when the argument to \code{x} has a minimum set objective set using \code{\link[=add_min_set_objective]{add_min_set_objective()}}). Higher \code{penalty} values can be used to obtain solutions with a high degree of connectivity, @@ -40,7 +40,7 @@ of zones. Cell values along the diagonal of the matrix represent the level of connectivity between planning units allocated to the same zone. Cell values must lay between 1 and -1, where negative values favor solutions with weak connectivity. The default argument to -\code{zones} is an identity matrix (i.e. a matrix with ones along the +\code{zones} is an identity matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered to be connected when they are allocated to the same zone. This argument is required when the argument to \code{data} is a @@ -56,7 +56,7 @@ that are associated with higher values are more favorable in the solution. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the penalties +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the penalties added to it. } \description{ @@ -95,7 +95,7 @@ denotes the connectivity between two planning units following the \emph{Marxan} format. The data can be used to denote symmetric or asymmetric relationships between planning units. By default, input data is assumed to be symmetric unless asymmetric data is -also included (e.g. if data is present for planning units 2 and 3, then +also included (e.g., if data is present for planning units 2 and 3, then the same amount of connectivity is expected for planning units 3 and 2, unless connectivity data is also provided for planning units 3 and 2). If the argument to \code{x} contains multiple zones, then the columns @@ -109,7 +109,7 @@ allocated to specific zones. If the columns \code{"zone1"} and containing four-dimensions where cell values indicate the strength of connectivity between planning units when they are assigned to specific management zones. The first two -dimensions (i.e. rows and columns) indicate the strength of +dimensions (i.e., rows and columns) indicate the strength of connectivity between different planning units and the second two dimensions indicate the different management zones. Thus the \code{data[1, 2, 3, 4]} indicates the strength of @@ -126,7 +126,7 @@ Let \eqn{I} represent the set of planning units (indexed by \eqn{i} or \eqn{j}), \eqn{Z} represent the set of management zones (indexed by \eqn{z} or \eqn{y}), and \eqn{X_{iz}}{Xiz} represent the decision variable for planning unit \eqn{i} for in zone -\eqn{z} (e.g. with binary +\eqn{z} (e.g., with binary values one indicating if planning unit is allocated or not). Also, let \eqn{p} represent the argument to \code{penalty}, \eqn{D} represent the argument to \code{data}, and \eqn{W} represent the argument @@ -191,7 +191,7 @@ image(b_matrix) } # create a symmetric connectivity matrix where the connectivity between # two planning units corresponds to their spatial proximity -# i.e. planning units that are further apart share less connectivity +# i.e., planning units that are further apart share less connectivity centroids <- rgeos::gCentroid(sim_pu_polygons, byid = TRUE) d_matrix <- (1 / (as(dist(centroids@coords), "Matrix") + 1)) @@ -234,7 +234,7 @@ for (i in seq_len(length(sim_pu_polygons))) { # find if planning units are adjacent if (adjacent_units[i, j]) { # find if planning units lay north and south of each other - # i.e. they have the same x-coordinate + # i.e., they have the same x-coordinate if (centroids@coords[i, 1] == centroids@coords[j, 1]) { if (centroids@coords[i, 2] > centroids@coords[j, 2]) { # if i is north of j add 10 units of connectivity diff --git a/man/add_contiguity_constraints.Rd b/man/add_contiguity_constraints.Rd index 7dda85d75..5f29e02f6 100644 --- a/man/add_contiguity_constraints.Rd +++ b/man/add_contiguity_constraints.Rd @@ -14,12 +14,12 @@ \S4method{add_contiguity_constraints}{ConservationProblem,ANY,matrix}(x, zones, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{zones}{\code{matrix} or \code{Matrix} object describing the connection scheme for different zones. Each row and column corresponds to a different zone in the argument to \code{x}, and cell values must -contain binary \code{numeric} values (i.e. one or zero) that indicate +contain binary \code{numeric} values (i.e., one or zero) that indicate if connected planning units (as specified in the argument to \code{data}) should be still considered connected if they are allocated to different zones. The cell values along the diagonal @@ -28,7 +28,7 @@ contiguity constraints when they are allocated to a given zone. Note arguments to \code{zones} must be symmetric, and that a row or column has a value of one then the diagonal element for that row or column must also have a value of one. The default argument to \code{zones} is an identity -matrix (i.e. a matrix with ones along the matrix diagonal and zeros +matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered connected if they are both allocated to the same zone.} @@ -40,7 +40,7 @@ connection data is calculated automatically using the See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -65,13 +65,13 @@ calculated automatically using the \code{\link[=adjacency_matrix]{adjacency_matr This is the default argument. Note that the connection data must be manually defined using one of the other formats below when the planning unit data -in the argument to \code{x} is not spatially referenced (e.g. +in the argument to \code{x} is not spatially referenced (e.g., in \code{data.frame} or \code{numeric} format).} \item{\code{data} as a \code{matrix}/\code{Matrix} object}{where rows and columns represent different planning units and the value of each cell indicates if the two planning units are connected or not. Cell values should be binary -\code{numeric} values (i.e. one or zero). Cells that occur along the +\code{numeric} values (i.e., one or zero). Cells that occur along the matrix diagonal have no effect on the solution at all because each planning unit cannot be a connected with itself.} @@ -84,7 +84,7 @@ specified in the fields \code{"id1"} and \code{"id2"} are connected or not. This data can be used to describe symmetric or asymmetric relationships between planning units. By default, input data is assumed to be symmetric unless asymmetric data is -also included (e.g. if data is present for planning units 2 and 3, then +also included (e.g., if data is present for planning units 2 and 3, then the same amount of connectivity is expected for planning units 3 and 2, unless connectivity data is also provided for planning units 3 and 2).} diff --git a/man/add_cplex_solver.Rd b/man/add_cplex_solver.Rd index fd8ef5c2b..e8f04eeaa 100644 --- a/man/add_cplex_solver.Rd +++ b/man/add_cplex_solver.Rd @@ -14,7 +14,7 @@ add_cplex_solver( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{gap}{\code{numeric} gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. @@ -22,12 +22,12 @@ For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1\% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10\% from optimality).} +The default value is 0.1 (i.e., 10\% from optimality).} \item{time_limit}{\code{numeric} time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. \code{.Machine$integer.max}), effectively meaning that solver +(i.e., \code{.Machine$integer.max}), effectively meaning that solver will keep running until a solution within the optimality gap is found.} \item{presolve}{\code{logical} attempt to simplify the @@ -40,7 +40,7 @@ optimization algorithm. The default value is 1.} optimization problems? Defaults to \code{TRUE}.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the solver +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the solver added to it. } \description{ @@ -54,12 +54,12 @@ It requires the \pkg{cplexAPI} package to be installed \details{ \href{https://www.ibm.com/analytics/cplex-optimizer}{\emph{IBM CPLEX}} is a commercial optimization software. It is faster than -the available open source solvers (e.g. \code{\link[=add_lpsymphony_solver]{add_lpsymphony_solver()}} and +the available open source solvers (e.g., \code{\link[=add_lpsymphony_solver]{add_lpsymphony_solver()}} and \code{\link[=add_rsymphony_solver]{add_rsymphony_solver()}}. Although formal benchmarks examining the performance of this solver for conservation planning problems have yet to be completed, preliminary analyses suggest that it performs slightly slower than the \emph{Gurobi} -solver (i.e. \code{\link[=add_gurobi_solver]{add_gurobi_solver()}}). +solver (i.e., \code{\link[=add_gurobi_solver]{add_gurobi_solver()}}). We recommend using this solver if the \emph{Gurobi} solver is not available. Licenses are available for the \emph{IBM CPLEX} software to academics at no cost (see \url{https://www.ibm.com/products/ilog-cplex-optimization-studio}). @@ -75,7 +75,7 @@ this variable can be specified by adding the following text to the } Note that you may need to change the version -number in the file path (i.e. \code{"CPLEX_Studio128"}). For more information +number in the file path (i.e., \code{"CPLEX_Studio128"}). For more information on installing the pkg{cplexAPI} package, please see the \href{https://github.com/cran/cplexAPI/blob/master/inst/INSTALL}{official installation instructions for the package}. } diff --git a/man/add_cuts_portfolio.Rd b/man/add_cuts_portfolio.Rd index 30d286082..0c22832bc 100644 --- a/man/add_cuts_portfolio.Rd +++ b/man/add_cuts_portfolio.Rd @@ -7,13 +7,13 @@ add_cuts_portfolio(x, number_solutions = 10L) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{number_solutions}{\code{integer} number of attempts to generate different solutions. Defaults to 10.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the portfolio +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the portfolio added to it. } \description{ @@ -33,7 +33,7 @@ having multiple threads allocated for solving an individual problem. \section{Notes}{ In early versions (< 4.0.1), this function was only compatible with -\emph{Gurobi} (i.e. \code{\link[=add_gurobi_solver]{add_gurobi_solver()}}). To provide functionality with +\emph{Gurobi} (i.e., \code{\link[=add_gurobi_solver]{add_gurobi_solver()}}). To provide functionality with exact algorithm solvers, this function now adds constraints to the problem formulation to generate multiple solutions. } diff --git a/man/add_default_decisions.Rd b/man/add_default_decisions.Rd index 9a2b71201..21d009d67 100644 --- a/man/add_default_decisions.Rd +++ b/man/add_default_decisions.Rd @@ -7,10 +7,10 @@ add_default_decisions(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the decisions added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the decisions added to it. } \description{ diff --git a/man/add_default_solver.Rd b/man/add_default_solver.Rd index 275b561cc..c542952d9 100644 --- a/man/add_default_solver.Rd +++ b/man/add_default_solver.Rd @@ -7,12 +7,12 @@ add_default_solver(x, ...) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{...}{arguments passed to the solver.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the solver +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the solver added to it. } \description{ diff --git a/man/add_extra_portfolio.Rd b/man/add_extra_portfolio.Rd index e2af932ea..8dcde81da 100644 --- a/man/add_extra_portfolio.Rd +++ b/man/add_extra_portfolio.Rd @@ -7,10 +7,10 @@ add_extra_portfolio(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the portfolio +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the portfolio added to it. } \description{ @@ -23,7 +23,7 @@ the quality of solutions. } \details{ This strategy for generating a portfolio requires problems to -be solved using the \emph{Gurobi} software suite (i.e. using +be solved using the \emph{Gurobi} software suite (i.e., using \code{\link[=add_gurobi_solver]{add_gurobi_solver()}}. Specifically, version 8.0.0 (or greater) of the \pkg{gurobi} package must be installed. } diff --git a/man/add_feature_contiguity_constraints.Rd b/man/add_feature_contiguity_constraints.Rd index c976c2c39..d6f1a446d 100644 --- a/man/add_feature_contiguity_constraints.Rd +++ b/man/add_feature_contiguity_constraints.Rd @@ -17,13 +17,13 @@ \S4method{add_feature_contiguity_constraints}{ConservationProblem,ANY,ANY}(x, zones, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{zones}{\code{matrix}, \code{Matrix} or \code{list} object describing the connection scheme for different zones. For \code{matrix} or and \code{Matrix} arguments, each row and column corresponds to a different zone in the argument to \code{x}, and cell values must -contain binary \code{numeric} values (i.e. one or zero) that indicate +contain binary \code{numeric} values (i.e., one or zero) that indicate if connected planning units (as specified in the argument to \code{data}) should be still considered connected if they are allocated to different zones. The cell values along the diagonal @@ -36,7 +36,7 @@ should differ among the features, then the argument to \code{zones} should be a \code{list} of \code{matrix} or \code{Matrix} objects that shows the specific scheme for each feature using the conventions described above. The default argument to \code{zones} is an identity -matrix (i.e. a matrix with ones along the matrix diagonal and zeros +matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered connected if they are both allocated to the same zone.} @@ -54,7 +54,7 @@ all adjacent planning units are treated as being connected for all features. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -94,13 +94,13 @@ argument and means that all adjacent planning units are treated as potentially dispersible for all features. Note that the connection data must be manually defined using one of the other formats below when the planning unit data -in the argument to \code{x} is not spatially referenced (e.g. +in the argument to \code{x} is not spatially referenced (e.g., in \code{data.frame} or \code{numeric} format).} \item{\code{data} as a\code{matrix}/\code{Matrix} object}{where rows and columns represent different planning units and the value of each cell indicates if the two planning units are connected or not. Cell values should be binary -\code{numeric} values (i.e. one or zero). Cells that occur along the +\code{numeric} values (i.e., one or zero). Cells that occur along the matrix diagonal have no effect on the solution at all because each planning unit cannot be a connected with itself. Note that pairs of connected planning units are treated as being potentially dispersible @@ -115,7 +115,7 @@ specified in the fields \code{"id1"} and \code{"id2"} are connected or not. This data can be used to describe symmetric or asymmetric relationships between planning units. By default, input data is assumed to be symmetric unless asymmetric data is -also included (e.g. if data is present for planning units 2 and 3, then +also included (e.g., if data is present for planning units 2 and 3, then the same amount of connectivity is expected for planning units 3 and 2, unless connectivity data is also provided for planning units 3 and 2). Note that pairs of connected planning units are treated as being diff --git a/man/add_feature_weights.Rd b/man/add_feature_weights.Rd index fbf2b1c0b..5ade29f2d 100644 --- a/man/add_feature_weights.Rd +++ b/man/add_feature_weights.Rd @@ -11,13 +11,13 @@ \S4method{add_feature_weights}{ConservationProblem,matrix}(x, weights) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{weights}{\code{numeric} or \code{matrix} of weights. See the Weights format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the weights +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the weights added to it. } \description{ @@ -32,7 +32,7 @@ about how the budget should be allocated. } \details{ Weights can only be applied to problems that have an objective -that is budget limited (e.g. \code{\link[=add_max_cover_objective]{add_max_cover_objective()}}). +that is budget limited (e.g., \code{\link[=add_max_cover_objective]{add_max_cover_objective()}}). They can be applied to problems that aim to maximize phylogenetic representation (\code{\link[=add_max_phylo_div_objective]{add_max_phylo_div_objective()}}) to favor the representation of specific features over the representation of @@ -95,7 +95,7 @@ p2 <- p1 \%>\% add_feature_weights(w2) # create manually specified weights that assign higher importance to # certain features. These weights could be based on a pre-calculated index -# (e.g. an index measuring extinction risk where higher values +# (e.g., an index measuring extinction risk where higher values # denote higher extinction risk) w3 <- c(0, 0, 0, 100, 200) p3 <- p1 \%>\% add_feature_weights(w3) @@ -137,18 +137,18 @@ plot(sim_phylogeny, main = "represented features", tip.color = replace(rep("black", nlayers(sim_features)), which(r4$met), "red")) } -# we can see here that the third feature ("layer.3", i.e. +# we can see here that the third feature ("layer.3", i.e., # sim_features[[3]]) is not represented in the solution. Let us pretend # that it is absolutely critical this feature is adequately conserved # in the solution. For example, this feature could represent a species # that plays important role in the ecosystem, or a species that is -# important commercial activities (e.g. eco-tourism). So, to generate +# important commercial activities (e.g., eco-tourism). So, to generate # a solution that conserves the third feature whilst also aiming to # maximize phylogenetic diversity, we will create a set of weights that # assign a particularly high weighting to the third feature w5 <- c(0, 0, 1000, 0, 0) -# we can see that this weighting (i.e. w5[3]) has a much higher value than +# we can see that this weighting (i.e., w5[3]) has a much higher value than # the branch lengths in the phylogeny so solutions that represent this # feature be much closer to optimality print(sim_phylogeny$edge.length) diff --git a/man/add_gap_portfolio.Rd b/man/add_gap_portfolio.Rd index 2388d8403..14222b022 100644 --- a/man/add_gap_portfolio.Rd +++ b/man/add_gap_portfolio.Rd @@ -7,7 +7,7 @@ add_gap_portfolio(x, number_solutions, pool_gap = 0.1) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{number_solutions}{\code{integer} number of solutions required.} @@ -15,12 +15,12 @@ add_gap_portfolio(x, number_solutions, pool_gap = 0.1) This relative gap specifies a threshold worst-case performance for solutions in the portfolio. For example, value of 0.1 will result in the portfolio returning solutions that are within 10\% of an optimal solution. -Note that the gap specified in the solver (i.e. +Note that the gap specified in the solver (i.e., \code{\link[=add_gurobi_solver]{add_gurobi_solver()}} must be less than or equal to the gap specified to generate the portfolio. Defaults to 0.1.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the portfolio +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the portfolio added to it. } \description{ @@ -33,14 +33,15 @@ frequencies for moderate and large-sized problems (similar to } \details{ This strategy for generating a portfolio requires problems to -be solved using the \emph{Gurobi} software suite (i.e. using +be solved using the \emph{Gurobi} software suite (i.e., using \code{\link[=add_gurobi_solver]{add_gurobi_solver()}}. Specifically, version 9.0.0 (or greater) of the \pkg{gurobi} package must be installed. Note that the number of solutions returned may be less than the argument to \code{number_solutions}, if the total number of solutions that meet the optimality gap is less than the number of solutions requested. Also, note that this portfolio function only works with problems -that have binary decisions (i.e. specified using \code{\link[=add_binary_decisions]{add_binary_decisions()}}). +that have binary decisions (i.e., specified using +\code{\link[=add_binary_decisions]{add_binary_decisions()}}). } \examples{ \dontrun{ diff --git a/man/add_gurobi_solver.Rd b/man/add_gurobi_solver.Rd index b6380ecf6..6a9b63f92 100644 --- a/man/add_gurobi_solver.Rd +++ b/man/add_gurobi_solver.Rd @@ -18,7 +18,7 @@ add_gurobi_solver( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{gap}{\code{numeric} gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. @@ -26,12 +26,12 @@ For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1\% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10\% from optimality).} +The default value is 0.1 (i.e., 10\% from optimality).} \item{time_limit}{\code{numeric} time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. \code{.Machine$integer.max}), effectively meaning that solver +(i.e., \code{.Machine$integer.max}), effectively meaning that solver will keep running until a solution within the optimality gap is found.} \item{presolve}{\code{integer} number indicating how intensively the @@ -65,7 +65,7 @@ the optimization problem exceeds this parameter value, the solver will begin storing this information on disk (using the *Gurobi( \code{NodeFileStart} parameter). This functionality is useful if the system has insufficient memory to -solve a given problem (e.g. solving the problem with default settings +solve a given problem (e.g., solving the problem with default settings yields the \verb{OUT OF MEMORY} error message) and a system with more memory is not readily available. For example, a value of 4 indicates that the solver will start using @@ -77,7 +77,7 @@ to store information on disk when solving a given problem.} \item{start_solution}{\code{NULL} or object containing the starting solution for the solver. Defaults to \code{NULL} such that no starting solution is used. To specify a starting solution, the argument to \code{start_solution} should -be in the same format as the planning units (i.e. a \code{NULL}, \code{numeric}, +be in the same format as the planning units (i.e., a \code{NULL}, \code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, or \code{\link[sf:sf]{sf::sf()}} object). See the Start solution format section for more information.} @@ -86,7 +86,7 @@ See the Start solution format section for more information.} optimization problems? Defaults to \code{TRUE}.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the solver +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the solver added to it. } \description{ diff --git a/man/add_linear_constraints.Rd b/man/add_linear_constraints.Rd index 5879154b9..09eabb799 100644 --- a/man/add_linear_constraints.Rd +++ b/man/add_linear_constraints.Rd @@ -23,7 +23,7 @@ \S4method{add_linear_constraints}{ConservationProblem,ANY,ANY,dgCMatrix}(x, threshold, sense, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{threshold}{\code{numeric} value. This threshold value is also known as a "right-hand-side" value @@ -40,7 +40,7 @@ per integer programming terminology. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -53,12 +53,12 @@ ensure that solutions meet certain criteria (see Examples section below for details). For example, these constraints can be used to add multiple budgets. They can also be used to ensure that the total number of planning units -allocated to a certain administrative area (e.g. country) does not exceed -a certain threshold (e.g. 30\% of its total area). Furthermore, +allocated to a certain administrative area (e.g., country) does not exceed +a certain threshold (e.g., 30\% of its total area). Furthermore, they can also be used to ensure that features have a minimal level -of representation (e.g. 30\%) when using an objective +of representation (e.g., 30\%) when using an objective function that aims to enhance feature representation given a budget -(e.g. \code{\link[=add_min_shortfall_objective]{add_min_shortfall_objective()}}). +(e.g., \code{\link[=add_min_shortfall_objective]{add_min_shortfall_objective()}}). } \section{Mathematical formulation}{ @@ -67,13 +67,13 @@ equation. Let \eqn{I} denote the set of planning units (indexed by \eqn{i}), \eqn{Z} the set of management zones (indexed by \eqn{z}), and \eqn{X_{iz}}{Xiz} the decision variable for allocating -planning unit \eqn{i} to zone \eqn{z} (e.g. with binary +planning unit \eqn{i} to zone \eqn{z} (e.g., with binary values indicating if each planning unit is allocated or not). Also, let \eqn{D_{iz}}{Diz} denote the constraint data associated with planning units \eqn{i \in I}{i in I} for zones \eqn{z \in Z}{z in Z} (argument to \code{data}, if supplied as a \code{matrix} object), \eqn{\theta} denote the constraint sense -(argument to \code{sense}, e.g. \eqn{<=}), and \eqn{t} denote the constraint +(argument to \code{sense}, e.g., \eqn{<=}), and \eqn{t} denote the constraint threshold (argument to \code{threshold}). \deqn{ @@ -216,7 +216,7 @@ plot(stack(s0, s2), main = c("s1", "s2"), axes = FALSE, box = FALSE) # third, let's create a modified version of p0 that contains # additional constraints to ensure that the solution equitably # distributes conservation effort across different administrative areas -# (e.g. countries) within the study region +# (e.g., countries) within the study region # to begin with, we will simulate a dataset describing the spatial extent of # four different administrative areas across the study region diff --git a/man/add_linear_penalties.Rd b/man/add_linear_penalties.Rd index 064e49d21..82c3815cf 100644 --- a/man/add_linear_penalties.Rd +++ b/man/add_linear_penalties.Rd @@ -23,7 +23,7 @@ \S4method{add_linear_penalties}{ConservationProblem,ANY,dgCMatrix}(x, penalty, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{penalty}{\code{numeric} penalty value that is used to scale the importance not selecting planning units with high \code{data} values. @@ -44,15 +44,15 @@ associated with higher data values are penalized more strongly in the solution. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the penalties +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the penalties added to it. } \description{ Add penalties to a conservation planning \code{\link[=problem]{problem()}} to penalize solutions that select planning units with higher values from a specific -data source (e.g. anthropogenic impact). These penalties assume +data source (e.g., anthropogenic impact). These penalties assume a linear trade-off between the penalty values and the primary -objective of the conservation planning \code{\link[=problem]{problem()}} (e.g. +objective of the conservation planning \code{\link[=problem]{problem()}} (e.g., solution cost for minimum set problems; \code{\link[=add_min_set_objective]{add_min_set_objective()}}. } \details{ @@ -116,7 +116,7 @@ equations. Let \eqn{I} denote the set of planning units (indexed by \eqn{i}), \eqn{Z} the set of management zones (indexed by \eqn{z}), and \eqn{X_{iz}}{Xiz} the decision variable for allocating -planning unit \eqn{i} to zone \eqn{z} (e.g. with binary +planning unit \eqn{i} to zone \eqn{z} (e.g., with binary values indicating if each planning unit is allocated or not). Also, let \eqn{P_z} represent the penalty scaling value for zones \eqn{z \in Z}{z in Z} (argument to \code{penalty}), and @@ -143,7 +143,7 @@ set.seed(600) data(sim_pu_polygons, sim_pu_zones_stack, sim_features, sim_features_zones) # add a column to contain the penalty data for each planning unit -# e.g. these values could indicate the level of habitat +# e.g., these values could indicate the level of habitat sim_pu_polygons$penalty_data <- runif(nrow(sim_pu_polygons)) # plot the penalty data to visualise its spatial distribution @@ -174,7 +174,7 @@ s1 <- solve(p1) s2 <- solve(p2) # plot the solutions and compare them, -# since we supplied a very high penalty value (i.e. 100), relative +# since we supplied a very high penalty value (i.e., 100), relative # to the range of values in the penalty data and the objective function, # the solution in s2 is very sensitive to values in the penalty data spplot(s1, zcol = "solution_1", main = "solution without penalties", @@ -183,7 +183,7 @@ spplot(s2, zcol = "solution_1", main = "solution with penalties", axes = FALSE, box = FALSE) # for real conservation planning exercises, -# it would be worth exploring a range of penalty values (e.g. ranging +# it would be worth exploring a range of penalty values (e.g., ranging # from 1 to 100 increments of 5) to explore the trade-offs } diff --git a/man/add_locked_in_constraints.Rd b/man/add_locked_in_constraints.Rd index e07a82c48..b994cab4e 100644 --- a/man/add_locked_in_constraints.Rd +++ b/man/add_locked_in_constraints.Rd @@ -28,13 +28,13 @@ add_locked_in_constraints(x, locked_in) \S4method{add_locked_in_constraints}{ConservationProblem,Raster}(x, locked_in) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{locked_in}{Object that determines which planning units that should be locked in. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -45,7 +45,7 @@ lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network. If specific planning units should be locked out of a solution, use \code{\link[=add_locked_out_constraints]{add_locked_out_constraints()}}. For problems with non-binary -planning unit allocations (e.g. proportions), the +planning unit allocations (e.g., proportions), the \code{\link[=add_manual_locked_constraints]{add_manual_locked_constraints()}} function can be used to lock planning unit allocations to a specific value. } @@ -87,7 +87,7 @@ This format is only compatible if the planning units in the argument to \code{x} are a \code{\linkS4class{Spatial}}, \code{\link[sf:sf]{sf::sf()}}, or \code{data.frame} object. The fields -(columns) must have \code{logical} (i.e. \code{TRUE} or \code{FALSE}) +(columns) must have \code{logical} (i.e., \code{TRUE} or \code{FALSE}) values indicating if the planning unit is to be locked for the solution. For problems that contain a single zone, the argument to \code{data} must contain a single field name. Otherwise, for problems that diff --git a/man/add_locked_out_constraints.Rd b/man/add_locked_out_constraints.Rd index fb703f7bd..d8429ddba 100644 --- a/man/add_locked_out_constraints.Rd +++ b/man/add_locked_out_constraints.Rd @@ -28,13 +28,13 @@ add_locked_out_constraints(x, locked_out) \S4method{add_locked_out_constraints}{ConservationProblem,Raster}(x, locked_out) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{locked_out}{Object that determines which planning units that should be locked out. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -44,7 +44,7 @@ that specific planning units are not selected useful to lock out planning units that have been degraded and are not suitable for conserving species. If specific planning units should be locked in to the solution, use \code{\link[=add_locked_out_constraints]{add_locked_out_constraints()}}. For -problems with non-binary planning unit allocations (e.g. proportions), the +problems with non-binary planning unit allocations (e.g., proportions), the \code{\link[=add_manual_locked_constraints]{add_manual_locked_constraints()}} function can be used to lock planning unit allocations to a specific value. } @@ -86,7 +86,7 @@ This format is only compatible if the planning units in the argument to \code{x} are a \code{\linkS4class{Spatial}}, \code{\link[sf:sf]{sf::sf()}}, or \code{data.frame} object. The fields -(columns) must have \code{logical} (i.e. \code{TRUE} or \code{FALSE}) +(columns) must have \code{logical} (i.e., \code{TRUE} or \code{FALSE}) values indicating if the planning unit is to be locked for the solution. For problems that contain a single zone, the argument to \code{data} must contain a single field name. Otherwise, for problems that diff --git a/man/add_loglinear_targets.Rd b/man/add_loglinear_targets.Rd index b8fc3279c..d1fdbd6da 100644 --- a/man/add_loglinear_targets.Rd +++ b/man/add_loglinear_targets.Rd @@ -17,7 +17,7 @@ add_loglinear_targets( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{lower_bound_amount}{\code{numeric} threshold.} @@ -43,7 +43,7 @@ use when calculating the targets. Defaults to the feature abundances in the study area (calculated using the \code{\link[=feature_abundances]{feature_abundances()}} function.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the targets added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the targets added to it. } \description{ @@ -76,7 +76,7 @@ that should be used when calculating the targets. The target calculations do not account for the size of each planning unit. Therefore, the feature data should account for -the size of each planning unit if this is important (e.g. pixel values in +the size of each planning unit if this is important (e.g., pixel values in the argument to \code{features} in the function \code{\link[=problem]{problem()}} could correspond to amount of land occupied by the feature in \eqn{km^2} units). Additionally, the function can only be applied to diff --git a/man/add_lsymphony_solver.Rd b/man/add_lsymphony_solver.Rd index 84c00253b..332721a20 100644 --- a/man/add_lsymphony_solver.Rd +++ b/man/add_lsymphony_solver.Rd @@ -14,7 +14,7 @@ add_lpsymphony_solver( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{gap}{\code{numeric} gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. @@ -22,12 +22,12 @@ For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1\% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10\% from optimality).} +The default value is 0.1 (i.e., 10\% from optimality).} \item{time_limit}{\code{numeric} time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. \code{.Machine$integer.max}), effectively meaning that solver +(i.e., \code{.Machine$integer.max}), effectively meaning that solver will keep running until a solution within the optimality gap is found.} \item{first_feasible}{\code{logical} should the first feasible solution be @@ -42,7 +42,7 @@ Defaults to \code{FALSE}.} optimization problems? Defaults to \code{TRUE}.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the solver +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the solver added to it. } \description{ diff --git a/man/add_mandatory_allocation_constraints.Rd b/man/add_mandatory_allocation_constraints.Rd index f9bba43b1..3086865f1 100644 --- a/man/add_mandatory_allocation_constraints.Rd +++ b/man/add_mandatory_allocation_constraints.Rd @@ -8,10 +8,10 @@ \S4method{add_mandatory_allocation_constraints}{ConservationProblem}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -27,11 +27,11 @@ developing land-use plans, some decision makers may require that each and every single parcel of land has been allocated a specific land-use type. In other words are no "left over" areas. Although it might seem tempting to simply solve the problem and manually assign "left over" planning units -to a default zone afterwards (e.g. an "other", "urban", or "grazing" +to a default zone afterwards (e.g., an "other", "urban", or "grazing" land-use), this could result in highly sub-optimal solutions if there penalties for siting the default land-use adjacent to other zones. Instead, this function can be used to specify that all planning units in a -problem with multiple zones must be allocated to a management zone (i.e. +problem with multiple zones must be allocated to a management zone (i.e., zone allocation is mandatory). } \examples{ diff --git a/man/add_manual_bounded_constraints.Rd b/man/add_manual_bounded_constraints.Rd index f86be9bf1..3a89d9504 100644 --- a/man/add_manual_bounded_constraints.Rd +++ b/man/add_manual_bounded_constraints.Rd @@ -13,18 +13,18 @@ add_manual_bounded_constraints(x, data) \S4method{add_manual_bounded_constraints}{ConservationProblem,tbl_df}(x, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{data}{\code{data.frame} or \code{\link[tibble:tibble]{tibble::tibble()}} object. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ Add constraints to a conservation planning \code{\link[=problem]{problem()}} to ensure -that the planning unit values (e.g. proportion, binary) in a solution +that the planning unit values (e.g., proportion, binary) in a solution range between specific lower and upper bounds. This function offers more fine-grained control than the \code{\link[=add_manual_locked_constraints]{add_manual_locked_constraints()}} function and is is most useful for problems involving proportion-type diff --git a/man/add_manual_locked_constraints.Rd b/man/add_manual_locked_constraints.Rd index 1b6e19e1b..4716e9d3b 100644 --- a/man/add_manual_locked_constraints.Rd +++ b/man/add_manual_locked_constraints.Rd @@ -13,13 +13,13 @@ add_manual_locked_constraints(x, data) \S4method{add_manual_locked_constraints}{ConservationProblem,tbl_df}(x, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{data}{\code{data.frame} or \code{\link[tibble:tibble]{tibble::tibble()}} object. See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -44,10 +44,10 @@ zone.} \item{status}{\code{numeric} values indicating how much of each planning unit should be allocated to each zone in the solution. -For example, the \code{numeric} values could be binary values (i.e. zero +For example, the \code{numeric} values could be binary values (i.e., zero or one) for problems containing binary-type decision variables (using the \code{\link[=add_binary_decisions]{add_binary_decisions()}} function). Alternatively, -the \code{numeric} values could be proportions (e.g. 0.5) for problems +the \code{numeric} values could be proportions (e.g., 0.5) for problems containing proportion-type decision variables (using the \code{\link[=add_proportion_decisions]{add_proportion_decisions()}}).} diff --git a/man/add_manual_targets.Rd b/man/add_manual_targets.Rd index 185bbcfdf..d2784e95e 100644 --- a/man/add_manual_targets.Rd +++ b/man/add_manual_targets.Rd @@ -15,13 +15,13 @@ add_manual_targets(x, targets) \S4method{add_manual_targets}{ConservationProblem,tbl_df}(x, targets) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{targets}{\code{data.frame} or \code{\link[tibble:tibble]{tibble::tibble()}} object. See the Target data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the targets added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the targets added to it. } \description{ @@ -33,7 +33,7 @@ most cases, targets can be specified using the functions. However, this function can be used to (i) mix absolute and relative targets for different features and zones, (ii) set targets that pertain to the allocations of planning units in multiple zones, and (iii) -set targets that require different senses (e.g. targets which specify the +set targets that require different senses (e.g., targets which specify the solution should not exceed a certain quantity using \code{"<="} values). } \details{ diff --git a/man/add_max_cover_objective.Rd b/man/add_max_cover_objective.Rd index 15b60a5b9..9103a477e 100644 --- a/man/add_max_cover_objective.Rd +++ b/man/add_max_cover_objective.Rd @@ -7,7 +7,7 @@ add_max_cover_objective(x, budget) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{budget}{\code{numeric} value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument @@ -16,7 +16,7 @@ for the entire solution or a \code{numeric} vector to specify a budget for each each management zone.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -31,10 +31,10 @@ The maximum coverage objective seeks to find the set of planning units that maximizes the number of represented features, while keeping cost within a fixed budget. Here, features are treated as being represented if the reserve system contains at least a single instance of a feature -(i.e. an amount greater than 1). This formulation has often been +(i.e., an amount greater than 1). This formulation has often been used in conservation planning problems dealing with binary biodiversity data that indicate the presence/absence of suitable habitat -(e.g. Church & Velle 1974). Additionally, weights can be used to favor the +(e.g., Church & Velle 1974). Additionally, weights can be used to favor the representation of certain features over other features (see \code{\link[=add_feature_weights]{add_feature_weights()}}). Check out the \code{\link[=add_max_features_objective]{add_max_features_objective()}} for a more @@ -58,7 +58,7 @@ Maximize sum_i^I (-s * ci * xi) + sum_j^J (yj * wj) subject to sum_i^I (xi * rij) >= (yj * 1) for all j in J & sum_i^I (xi * ci) <= B} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit \eqn{i}{i}, \eqn{y_j}{yj} indicates if the solution has meet diff --git a/man/add_max_features_objective.Rd b/man/add_max_features_objective.Rd index e8a95f3d8..9f9cbd607 100644 --- a/man/add_max_features_objective.Rd +++ b/man/add_max_features_objective.Rd @@ -7,7 +7,7 @@ add_max_features_objective(x, budget) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{budget}{\code{numeric} value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument @@ -16,7 +16,7 @@ for the entire solution or (ii) a \code{numeric} vector to specify a separate budget for each management zone.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -51,7 +51,7 @@ Maximize sum_i^I (-s * ci * xi) + sum_j^J (yj * wj) subject to sum_i^I (xi * rij) >= (yj tj) for all j in J & sum_i^I (xi * ci) <= B} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/man/add_max_phylo_div_objective.Rd b/man/add_max_phylo_div_objective.Rd index 7c21107cc..8403f5dd0 100644 --- a/man/add_max_phylo_div_objective.Rd +++ b/man/add_max_phylo_div_objective.Rd @@ -8,7 +8,7 @@ add_max_phylo_div_objective(x, budget, tree) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{budget}{\code{numeric} value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument @@ -20,7 +20,7 @@ a separate budget for each management zone.} for the conservation features.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -76,7 +76,7 @@ Maximize sum_i^I (-s * ci * xi) + sum_j^J (mb * lb) subject to sum_i^I (xi * rij) >= (yj * tj) for all j in J & mb <= yj for all j in T(b) & sum_i^I (xi * ci) <= B} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/man/add_max_phylo_end_objective.Rd b/man/add_max_phylo_end_objective.Rd index 607e87fb2..6d47978da 100644 --- a/man/add_max_phylo_end_objective.Rd +++ b/man/add_max_phylo_end_objective.Rd @@ -7,7 +7,7 @@ add_max_phylo_end_objective(x, budget, tree) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{budget}{\code{numeric} value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument @@ -19,7 +19,7 @@ a separate budget for each management zone.} for the conservation features.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -75,7 +75,7 @@ Maximize sum_i^I (-s * ci * xi) + sum_j^J (mb * lb * (1 / ab)) subject to sum_i^I (xi * rij) >= (yj * tj) for all j in J & mb <= yj for all j in T(b) & sum_i^I (xi * ci) <= B} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/man/add_max_utility_objective.Rd b/man/add_max_utility_objective.Rd index 24c2537bd..f662400d6 100644 --- a/man/add_max_utility_objective.Rd +++ b/man/add_max_utility_objective.Rd @@ -7,7 +7,7 @@ add_max_utility_objective(x, budget) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{budget}{\code{numeric} value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument @@ -16,7 +16,7 @@ for the entire solution or (ii) a \code{numeric} vector to specify a separate budget for each management zone.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -50,7 +50,7 @@ by \eqn{j}{j}) as: Maximize sum_i^I (-s * ci * xi) + sum_j^J (aj * wj) subject to aj = sum_i^I (xi * rij) for all j in J & sum_i^I (xi * ci) <= B} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit \eqn{i}{i}, \eqn{A_j}{Aj} is the amount of feature \eqn{j}{j} diff --git a/man/add_min_largest_shortfall_objective.Rd b/man/add_min_largest_shortfall_objective.Rd index 44da36f9c..9d07759a2 100644 --- a/man/add_min_largest_shortfall_objective.Rd +++ b/man/add_min_largest_shortfall_objective.Rd @@ -7,7 +7,7 @@ add_min_largest_shortfall_objective(x, budget) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{budget}{\code{numeric} value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument @@ -16,7 +16,7 @@ for the entire solution or (ii) a \code{numeric} vector to specify a separate budget for each management zone.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -24,7 +24,7 @@ Set the objective of a conservation planning \code{\link[=problem]{problem()}} t minimize the largest target shortfall while ensuring that the cost of the solution does not exceed a budget. Note that if the target shortfall for a single feature cannot be decreased beyond a certain -point (e.g. because all remaining planning units occupied by that feature +point (e.g., because all remaining planning units occupied by that feature are too costly or are locked out), then solutions may only use a small proportion of the specified budget. } @@ -57,7 +57,7 @@ sum_i^I (xi * rij) + yj >= tj for all j in J & l >= (yj / tj) for all j in J & sum_i^I (xi * ci) <= B} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit \eqn{i}{i}, and \eqn{t_j}{tj} is the representation target for feature diff --git a/man/add_min_set_objective.Rd b/man/add_min_set_objective.Rd index 832d400ad..878580294 100644 --- a/man/add_min_set_objective.Rd +++ b/man/add_min_set_objective.Rd @@ -7,10 +7,10 @@ add_min_set_objective(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -41,7 +41,7 @@ mathematically for a set of planning units (\eqn{I}{I} indexed by Minimize sum_i^I (xi * ci) subject to sum_i^I (xi * rij) >= Tj for all j in J} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{c_i}{ci} is the cost of planning unit \eqn{i}{i}, \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit diff --git a/man/add_min_shortfall_objective.Rd b/man/add_min_shortfall_objective.Rd index 67493dae9..1cf22bdc4 100644 --- a/man/add_min_shortfall_objective.Rd +++ b/man/add_min_shortfall_objective.Rd @@ -7,7 +7,7 @@ add_min_shortfall_objective(x, budget) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{budget}{\code{numeric} value specifying the maximum expenditure of the prioritization. For problems with multiple zones, the argument @@ -16,7 +16,7 @@ for the entire solution or (ii) a \code{numeric} vector to specify a separate budget for each management zone.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the objective +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the objective added to it. } \description{ @@ -49,7 +49,7 @@ Minimize sum_j^J wj * (yj / tj) subject to sum_i^I (xi * rij) + yj >= tj for all j in J & sum_i^I (xi * ci) <= B} -Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g. +Here, \eqn{x_i}{xi} is the \link{decisions} variable (e.g., specifying whether planning unit \eqn{i}{i} has been selected (1) or not (0)), \eqn{r_{ij}}{rij} is the amount of feature \eqn{j}{j} in planning unit \eqn{i}{i}, \eqn{t_j}{tj} is the representation target for feature diff --git a/man/add_neighbor_constraints.Rd b/man/add_neighbor_constraints.Rd index 36789a6e3..7ce7a5663 100644 --- a/man/add_neighbor_constraints.Rd +++ b/man/add_neighbor_constraints.Rd @@ -17,7 +17,7 @@ \S4method{add_neighbor_constraints}{ConservationProblem,ANY,ANY,array}(x, k, zones, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{k}{\code{integer} minimum number of neighbors for selected planning units in the solution. For problems with multiple zones, @@ -26,13 +26,13 @@ the argument to \code{k} must have an element for each zone.} \item{zones}{\code{matrix} or \code{Matrix} object describing the neighborhood scheme for different zones. Each row and column corresponds to a different zone in the argument to \code{x}, and cell values must -contain binary \code{numeric} values (i.e. one or zero) that indicate +contain binary \code{numeric} values (i.e., one or zero) that indicate if neighboring planning units (as specified in the argument to \code{data}) should be considered neighbors if they are allocated to different zones. The cell values along the diagonal of the matrix indicate if planning units that are allocated to the same zone should be considered neighbors or not. The default argument to -\code{zones} is an identity matrix (i.e. a matrix with ones along the +\code{zones} is an identity matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered neighbors if they are both allocated to the same zone.} @@ -44,7 +44,7 @@ neighborhood data is calculated automatically using the See the Data format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the constraints +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the constraints added to it. } \description{ @@ -69,13 +69,13 @@ automatically using the \code{\link[=adjacency_matrix]{adjacency_matrix()}} function. This is the default argument. Note that the neighborhood data must be manually defined using one of the other formats below when the planning unit data -in the argument to \code{x} is not spatially referenced (e.g. +in the argument to \code{x} is not spatially referenced (e.g., in \code{data.frame} or \code{numeric} format).} \item{\code{data} as a \code{matrix}/\code{Matrix} object}{where rows and columns represent different planning units and the value of each cell indicates if the two planning units are neighbors or not. Cell values should be binary -\code{numeric} values (i.e. one or zero). Cells that occur along the +\code{numeric} values (i.e., one or zero). Cells that occur along the matrix diagonal have no effect on the solution at all because each planning unit cannot be a neighbor with itself.} @@ -88,7 +88,7 @@ specified in the fields \code{"id1"} and \code{"id2"} are neighbors or not. This data can be used to describe symmetric or asymmetric relationships between planning units. By default, input data is assumed to be symmetric unless asymmetric data is -also included (e.g. if data is present for planning units 2 and 3, then +also included (e.g., if data is present for planning units 2 and 3, then the same amount of connectivity is expected for planning units 3 and 2, unless connectivity data is also provided for planning units 3 and 2). If the argument to \code{x} contains multiple zones, then the columns @@ -102,7 +102,7 @@ are present, then the argument to \code{zones} must be \code{NULL}.} \code{numeric} values indicate if planning unit should be treated as being neighbors with every other planning unit when they are allocated to every combination of management zone. The first two -dimensions (i.e. rows and columns) correspond to the planning units, +dimensions (i.e., rows and columns) correspond to the planning units, and second two dimensions correspond to the management zones. For example, if the argument to \code{data} had a value of 1 at the index \code{data[1, 2, 3, 4]} this would indicate that planning unit 1 and diff --git a/man/add_proportion_decisions.Rd b/man/add_proportion_decisions.Rd index 4d325879f..672662b3d 100644 --- a/man/add_proportion_decisions.Rd +++ b/man/add_proportion_decisions.Rd @@ -7,10 +7,10 @@ add_proportion_decisions(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the decisions added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the decisions added to it. } \description{ @@ -23,7 +23,7 @@ decisions } \details{ Conservation planning problems involve making decisions on planning -units. These decisions are then associated with actions (e.g. turning a +units. These decisions are then associated with actions (e.g., turning a planning unit into a protected area). Only a single decision should be added to a \code{ConservationProblem} object. Note that if multiple decisions are added to a problem object, then the diff --git a/man/add_relative_targets.Rd b/man/add_relative_targets.Rd index ff4e3f939..3b599b7ff 100644 --- a/man/add_relative_targets.Rd +++ b/man/add_relative_targets.Rd @@ -18,13 +18,13 @@ add_relative_targets(x, targets) \S4method{add_relative_targets}{ConservationProblem,character}(x, targets) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{targets}{Object that specifies the targets for each feature. See the Targets format section for more information.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the targets added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the targets added to it. } \description{ diff --git a/man/add_rsymphony_solver.Rd b/man/add_rsymphony_solver.Rd index 64119808b..d511ada10 100644 --- a/man/add_rsymphony_solver.Rd +++ b/man/add_rsymphony_solver.Rd @@ -13,7 +13,7 @@ add_rsymphony_solver( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{gap}{\code{numeric} gap to optimality. This gap is relative and expresses the acceptable deviance from the optimal objective. @@ -21,12 +21,12 @@ For example, a value of 0.01 will result in the solver stopping when it has found a solution within 1\% of optimality. Additionally, a value of 0 will result in the solver stopping when it has found an optimal solution. -The default value is 0.1 (i.e. 10\% from optimality).} +The default value is 0.1 (i.e., 10\% from optimality).} \item{time_limit}{\code{numeric} time limit (seconds) for generating solutions. The solver will return the current best solution when this time limit is exceeded. The default value is the largest integer value -(i.e. \code{.Machine$integer.max}), effectively meaning that solver +(i.e., \code{.Machine$integer.max}), effectively meaning that solver will keep running until a solution within the optimality gap is found.} \item{first_feasible}{\code{logical} should the first feasible solution be @@ -41,7 +41,7 @@ Defaults to \code{FALSE}.} optimization problems? Defaults to \code{TRUE}.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the solver +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the solver added to it. } \description{ diff --git a/man/add_semicontinuous_decisions.Rd b/man/add_semicontinuous_decisions.Rd index 4eab94a76..3017b3694 100644 --- a/man/add_semicontinuous_decisions.Rd +++ b/man/add_semicontinuous_decisions.Rd @@ -7,13 +7,13 @@ add_semicontinuous_decisions(x, upper_limit) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{upper_limit}{\code{numeric} value specifying the maximum proportion -of a planning unit that can be reserved (e.g. set to 0.8 for 80\%).} +of a planning unit that can be reserved (e.g., set to 0.8 for 80\%).} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the decisions added +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the decisions added to it. } \description{ @@ -25,13 +25,13 @@ This decision is similar to the \code{\link[=add_proportion_decisions]{add_propo function except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0\%) to all (100\%) of a planning unit. However, an upper bound can be specified to ensure that at -most only a fraction (e.g. 80\%) of a planning unit can be preserved. This +most only a fraction (e.g., 80\%) of a planning unit can be preserved. This type of decision may be useful when it is not practical to conserve entire planning units. } \details{ Conservation planning problems involve making decisions on planning -units. These decisions are then associated with actions (e.g. turning a +units. These decisions are then associated with actions (e.g., turning a planning unit into a protected area). Only a single decision should be added to a \code{ConservationProblem} object. Note that if multiple decisions are added to a problem object, then the diff --git a/man/add_shuffle_portfolio.Rd b/man/add_shuffle_portfolio.Rd index 548c6a34d..2cd7f8d2a 100644 --- a/man/add_shuffle_portfolio.Rd +++ b/man/add_shuffle_portfolio.Rd @@ -12,7 +12,7 @@ add_shuffle_portfolio( ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{number_solutions}{\code{integer} number of attempts to generate different solutions. Defaults to 10.} @@ -24,7 +24,7 @@ the solution portfolio. Defaults to 1.} be removed? Defaults to \code{TRUE}.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the portfolio +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the portfolio added to it. } \description{ diff --git a/man/add_top_portfolio.Rd b/man/add_top_portfolio.Rd index 83b6bed38..a5c91e3f1 100644 --- a/man/add_top_portfolio.Rd +++ b/man/add_top_portfolio.Rd @@ -7,12 +7,12 @@ add_top_portfolio(x, number_solutions) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{number_solutions}{\code{integer} number of solutions required.} } \value{ -Object (i.e. \code{\linkS4class{ConservationProblem}}) with the portfolio +Object (i.e., \code{\linkS4class{ConservationProblem}}) with the portfolio added to it. } \description{ @@ -22,7 +22,7 @@ are closest to optimality (i.e the top solutions). } \details{ This strategy for generating a portfolio requires problems to -be solved using the \emph{Gurobi} software suite (i.e. using +be solved using the \emph{Gurobi} software suite (i.e., using \code{\link[=add_gurobi_solver]{add_gurobi_solver()}}. Specifically, version 9.0.0 (or greater) of the \pkg{gurobi} package must be installed. Note that the number of solutions returned may be less than the argument to diff --git a/man/boundary_matrix.Rd b/man/boundary_matrix.Rd index 2d21c7ec1..64936b74c 100644 --- a/man/boundary_matrix.Rd +++ b/man/boundary_matrix.Rd @@ -38,7 +38,7 @@ to pre-process data? If \code{TRUE}, then the experimental will be used to pre-compute which planning units are adjacent to each other and potentially reduce the processing time required to generate the boundary matrices. This argument is only used when -the planning unit data are vector-based polygons (i.e. +the planning unit data are vector-based polygons (i.e., \code{\link[sp:SpatialPolygons]{sp::SpatialPolygonsDataFrame()}} objects). \strong{Note that using \code{TRUE} may crash Mac OSX systems.} The default argument is \code{FALSE}.} @@ -59,11 +59,11 @@ This function returns a \code{\linkS4class{dsCMatrix}} symmetric sparse matrix. Cells on the off-diagonal indicate the length of the shared boundary between two different planning units. Cells on the diagonal indicate length of a given planning unit's edges that have no -neighbors (e.g. for edges of planning units found along the +neighbors (e.g., for edges of planning units found along the coastline). \strong{This function assumes the data are in a coordinate system where Euclidean distances accurately describe the proximity between two points on the earth}. Thus spatial data in a longitude/latitude -coordinate system (i.e. +coordinate system (i.e., \href{https://spatialreference.org/ref/epsg/wgs-84/}{WGS84}) should be reprojected to another coordinate system before using this function. Note that for \code{\linkS4class{Raster}} objects diff --git a/man/compile.Rd b/man/compile.Rd index d5bd21c8f..bc31583eb 100644 --- a/man/compile.Rd +++ b/man/compile.Rd @@ -10,7 +10,7 @@ compile(x, ...) \method{compile}{ConservationProblem}(x, compressed_formulation = NA, ...) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{...}{not used.} diff --git a/man/connectivity_matrix.Rd b/man/connectivity_matrix.Rd index 7d156320f..dbf6de9e0 100644 --- a/man/connectivity_matrix.Rd +++ b/man/connectivity_matrix.Rd @@ -39,7 +39,7 @@ the conductance values. Note that argument to \code{y} can only be a Also, note that if the argument to \code{x} is a \code{\linkS4class{Raster}} object then argument to \code{y} must have the same spatial properties as it -(i.e. coordinate system, extent, resolution).} +(i.e., coordinate system, extent, resolution).} \item{...}{additional arguments passed to \code{\link[=fast_extract]{fast_extract()}} for extracting and calculating the conductance values for each planning unit. @@ -112,7 +112,7 @@ plot(clamp(raster(as.matrix(cm_ply)), lower = 1e-5, useValues = FALSE), # create connectivity matrix using habitat suitability data for each feature, # this could be useful if prioritisations should spatially clump # together adjacent planning units that have suitable habitat -# for the same species (e.g. to maintain functional connectivity) +# for the same species (e.g., to maintain functional connectivity) ## let's use the raster data for this example, and we can generate the ## connectivity matrix that we would use in the prioritization by @@ -131,7 +131,7 @@ plot(clamp(raster(as.matrix(cm_sum)), lower = 1e-5, useValues = FALSE), ## we could take this example one step further, and use weights to indicate ## relative importance of maintaining functional connectivity -## for each feature (i.e. use the weighted sum instead of the sum) +## for each feature (i.e., use the weighted sum instead of the sum) ## let's pretend that the first feature is 20 times more important ## than all the other species @@ -151,7 +151,7 @@ plot(clamp(raster(as.matrix(cm_wsum)), lower = 1e-5, useValues = FALSE), } ## since the statistical distribution of the connectivity values -## for each feature (e.g. the mean and standard deviation of the +## for each feature (e.g., the mean and standard deviation of the ## connectivity values) are different, it might make sense -- depending ## on the goal of the conservation planning exercise and the underlying ## data -- to first normalize the conductance values before applying the diff --git a/man/constraints.Rd b/man/constraints.Rd index 558328909..f7c659580 100644 --- a/man/constraints.Rd +++ b/man/constraints.Rd @@ -58,7 +58,7 @@ Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study -region (e.g. different countries).} +region (e.g., different countries).} \item{\code{\link[=add_mandatory_allocation_constraints]{add_mandatory_allocation_constraints()}}}{ Add constraints to ensure that every planning unit is allocated to a diff --git a/man/decisions.Rd b/man/decisions.Rd index a5bc4657f..739e71e58 100644 --- a/man/decisions.Rd +++ b/man/decisions.Rd @@ -41,7 +41,7 @@ similar to \code{add_proportion_decision} except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0\%) to all (100\%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction -(e.g. 80\%) of a planning unit can be preserved. This type of +(e.g., 80\%) of a planning unit can be preserved. This type of decision may be useful when it is not practical to conserve the entire area encompassed by any single planning unit.} diff --git a/man/eval_boundary_summary.Rd b/man/eval_boundary_summary.Rd index 0beccae98..de09d3126 100644 --- a/man/eval_boundary_summary.Rd +++ b/man/eval_boundary_summary.Rd @@ -20,7 +20,7 @@ eval_boundary_summary(x, ...) ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{...}{not used.} @@ -47,19 +47,19 @@ represent the relative importance of clumping planning units that are allocated to the same zone. Cell values must range between 1 and -1, where negative values favor solutions that spread out planning units. The default argument to \code{zones} is an identity -matrix (i.e. a matrix with ones along the matrix diagonal and zeros +matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that penalties are incurred when neighboring planning units are not assigned to the same zone. If the cells along the matrix diagonal contain markedly smaller values than those found elsewhere in the matrix, then solutions are preferred that surround planning units with those allocated to different zones -(i.e. greater spatial fragmentation).} +(i.e., greater spatial fragmentation).} \item{data}{\code{NULL}, \code{data.frame}, \code{matrix}, or \code{Matrix} object containing the boundary data. These data describe the total amount of boundary (perimeter) length for each planning unit, and the amount of boundary (perimeter) length shared between different -planning units (i.e. planning units that are adjacent to each other). +planning units (i.e., planning units that are adjacent to each other). See the Data format section for more information.} } \value{ @@ -96,7 +96,7 @@ This summary statistic is equivalent to the \code{Connectivity_Edge} metric reported by the \href{https://marxansolutions.org}{\emph{Marxan} software} (Ball \emph{et al.} 2009). It is calculated using the same equations used to penalize solutions -according to their total exposed boundary (i.e. \code{\link[=add_boundary_penalties]{add_boundary_penalties()}}). +according to their total exposed boundary (i.e., \code{\link[=add_boundary_penalties]{add_boundary_penalties()}}). See the Examples section for examples on how differences \code{zone} arguments can be used to calculate boundaries for different combinations of zones. } @@ -114,14 +114,14 @@ This argument is the default. Note that the boundary data must be supplied using one of the other formats below if the planning unit data in the argument to \code{x} do not explicitly contain spatial information -(e.g. planning unit data are a \code{data.frame} or \code{numeric} class).} +(e.g., planning unit data are a \code{data.frame} or \code{numeric} class).} \item{\code{data} as a \code{matrix}/\code{Matrix} object}{where rows and columns represent different planning units and the value of each cell represents the amount of shared boundary length between two different planning units. Cells that occur along the matrix diagonal represent the amount of exposed boundary associated with each planning unit that has -no neighbor (e.g. these value might pertain to boundaries along a +no neighbor (e.g., these value might pertain to boundaries along a coastline).} \item{\code{data} as a \code{data.frame} object}{with the columns \code{"id1"}, @@ -130,7 +130,7 @@ identifiers (indices) for a pair of planning units, and the \code{"boundary"} column contains the amount of shared boundary length between these two planning units. This format follows the the standard \emph{Marxan} format for boundary -data (i.e. per the "bound.dat" file).} +data (i.e., per the "bound.dat" file).} } } diff --git a/man/eval_connectivity_summary.Rd b/man/eval_connectivity_summary.Rd index 0a3b3294d..9955a1395 100644 --- a/man/eval_connectivity_summary.Rd +++ b/man/eval_connectivity_summary.Rd @@ -20,7 +20,7 @@ \S4method{eval_connectivity_summary}{ConservationProblem,ANY,ANY,array}(x, solution, zones, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, @@ -37,7 +37,7 @@ of zones. Cell values along the diagonal of the matrix represent the level of connectivity between planning units allocated to the same zone. Cell values must lay between 1 and -1, where negative values favor solutions with weak connectivity. The default argument to -\code{zones} is an identity matrix (i.e. a matrix with ones along the +\code{zones} is an identity matrix (i.e., a matrix with ones along the matrix diagonal and zeros elsewhere), so that planning units are only considered to be connected when they are allocated to the same zone. This argument is required when the argument to \code{data} is a @@ -86,7 +86,7 @@ This summary statistic is comparable to the \code{Connectivity_In} metric reported by the \href{https://marxansolutions.org}{\emph{Marxan} software} (Ball \emph{et al.} 2009). It is calculated using the same equations used to penalize solutions -with connectivity data (i.e. \code{\link[=add_connectivity_penalties]{add_connectivity_penalties()}}). +with connectivity data (i.e., \code{\link[=add_connectivity_penalties]{add_connectivity_penalties()}}). Specifically, it is calculated as the sum of the pair-wise connectivity values in the argument to \code{data}, weighted by the value of the planning units in the solution. @@ -193,7 +193,7 @@ denotes the connectivity between two planning units following the \emph{Marxan} format. The data can be used to denote symmetric or asymmetric relationships between planning units. By default, input data is assumed to be symmetric unless asymmetric data is -also included (e.g. if data is present for planning units 2 and 3, then +also included (e.g., if data is present for planning units 2 and 3, then the same amount of connectivity is expected for planning units 3 and 2, unless connectivity data is also provided for planning units 3 and 2). If the argument to \code{x} contains multiple zones, then the columns @@ -207,7 +207,7 @@ allocated to specific zones. If the columns \code{"zone1"} and containing four-dimensions where cell values indicate the strength of connectivity between planning units when they are assigned to specific management zones. The first two -dimensions (i.e. rows and columns) indicate the strength of +dimensions (i.e., rows and columns) indicate the strength of connectivity between different planning units and the second two dimensions indicate the different management zones. Thus the \code{data[1, 2, 3, 4]} indicates the strength of diff --git a/man/eval_cost_summary.Rd b/man/eval_cost_summary.Rd index 200a08d23..8d4f9b6e0 100644 --- a/man/eval_cost_summary.Rd +++ b/man/eval_cost_summary.Rd @@ -13,7 +13,7 @@ eval_cost_summary(x, solution) \method{eval_cost_summary}{ConservationProblem}(x, solution) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, @@ -58,7 +58,7 @@ This metric is equivalent to the \code{Cost} metric reported by the \href{https://marxansolutions.org}{\emph{Marxan} software} (Ball \emph{et al.} 2009). Specifically, the cost of a solution is defined as the sum of the cost values, supplied when creating a \code{\link[=problem]{problem()}} object -(e.g. using the \code{cost_column} argument), +(e.g., using the \code{cost_column} argument), weighted by the status of each planning unit in the solution. } \section{Solution format}{ diff --git a/man/eval_feature_representation_summary.Rd b/man/eval_feature_representation_summary.Rd index 39d6ac744..78233cc08 100644 --- a/man/eval_feature_representation_summary.Rd +++ b/man/eval_feature_representation_summary.Rd @@ -25,7 +25,7 @@ eval_feature_representation_summary(x, solution) \S4method{eval_feature_representation_summary}{ConservationProblem,Raster}(x, solution) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, @@ -37,7 +37,7 @@ See the Solution format section for more information.} \value{ \code{\link[tibble:tibble]{tibble::tibble()}} object describing feature representation. Here, each row describes a specific summary statistic -(e.g. different management zone) for a specific feature. +(e.g., different management zone) for a specific feature. It contains the following columns: \describe{ @@ -57,13 +57,14 @@ in the entire conservation planning problem (not just planning units selected within the solution). It is calculated as the sum of the feature data, supplied when creating a \code{\link[=problem]{problem()}} object -(e.g. presence/absence values).} +(e.g., presence/absence values).} \item{absolute_held}{\code{numeric} total amount of each feature secured within the solution. It is calculated as the sum of the feature data, supplied when creating a \code{\link[=problem]{problem()}} object -(e.g. presence/absence values), weighted by the status of each -planning unit in the solution (e.g. selected or not for prioritization).} +(e.g., presence/absence values), weighted by the status of each +planning unit in the solution (e.g., selected or not for +prioritization).} \item{relative_held}{\code{numeric} proportion of each feature secured within the solution. It is calculated diff --git a/man/eval_ferrier_importance.Rd b/man/eval_ferrier_importance.Rd index 82ba13172..684e40775 100644 --- a/man/eval_ferrier_importance.Rd +++ b/man/eval_ferrier_importance.Rd @@ -25,7 +25,7 @@ eval_ferrier_importance(x, solution) \S4method{eval_ferrier_importance}{ConservationProblem,Raster}(x, solution) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, diff --git a/man/eval_n_summary.Rd b/man/eval_n_summary.Rd index 0af0d9441..6a9add7cd 100644 --- a/man/eval_n_summary.Rd +++ b/man/eval_n_summary.Rd @@ -13,7 +13,7 @@ eval_n_summary(x, solution) \method{eval_n_summary}{ConservationProblem}(x, solution) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, @@ -48,8 +48,8 @@ to a conservation planning \code{\link[=problem]{problem()}}. \details{ This summary statistic is calculated as the sum of the values in the solution. As a consequence, this metric can produce a -non-integer value (e.g. 4.3) for solutions containing proportion values -(e.g. generated by solving a \code{\link[=problem]{problem()}} built using the +non-integer value (e.g., 4.3) for solutions containing proportion values +(e.g., generated by solving a \code{\link[=problem]{problem()}} built using the \code{\link[=add_proportion_decisions]{add_proportion_decisions()}} function). } \section{Solution format}{ diff --git a/man/eval_rare_richness_importance.Rd b/man/eval_rare_richness_importance.Rd index ccedc5f84..035b5d3fc 100644 --- a/man/eval_rare_richness_importance.Rd +++ b/man/eval_rare_richness_importance.Rd @@ -25,7 +25,7 @@ eval_rare_richness_importance(x, solution, ...) \S4method{eval_rare_richness_importance}{ConservationProblem,Raster}(x, solution, rescale, ...) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, @@ -51,7 +51,7 @@ same format as the planning unit data in the argument to \code{x}. Calculate importance scores for planning units selected in a solution using rarity weighted richness scores (based on Williams \emph{et al.} 1996). This method is only recommended for large-scaled conservation -planning exercises (i.e. more than 100,000 planning units) where +planning exercises (i.e., more than 100,000 planning units) where importance scores cannot be calculated using other methods in a feasible period of time. This is because rarity weighted richness scores cannot (i) account for the cost of different planning units, (ii) account for multiple diff --git a/man/eval_replacement_importance.Rd b/man/eval_replacement_importance.Rd index 593172fc5..313372e20 100644 --- a/man/eval_replacement_importance.Rd +++ b/man/eval_replacement_importance.Rd @@ -25,7 +25,7 @@ eval_replacement_importance(x, solution, ...) \S4method{eval_replacement_importance}{ConservationProblem,Raster}(x, solution, rescale, run_checks, force, threads, ...) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, @@ -84,19 +84,19 @@ objective function (\code{\link[=add_max_utility_objective]{add_max_utility_obje replacement cost scores correspond to the reduction in the utility when each planning unit is locked out. Infinite values mean that no feasible solution exists when planning units are locked out---they are -absolutely essential for obtaining a solution (e.g. they contain rare +absolutely essential for obtaining a solution (e.g., they contain rare species that are not found in any other planning units or were locked in). Zeros values mean that planning units can swapped with other planning units and this will have no effect on the performance of the solution at all -(e.g. because they were only selected due to spatial fragmentation +(e.g., because they were only selected due to spatial fragmentation penalties). These calculations can take a long time to complete for large or complex conservation planning problems. As such, we using this method for small or moderate-sized conservation planning problems -(e.g. < 30,000 planning units). To reduce run time, we -recommend calculating these scores without additional penalties (e.g. -\code{\link[=add_boundary_penalties]{add_boundary_penalties()}}) or spatial constraints (e.g. +(e.g., < 30,000 planning units). To reduce run time, we +recommend calculating these scores without additional penalties (e.g., +\code{\link[=add_boundary_penalties]{add_boundary_penalties()}}) or spatial constraints (e.g., \code{\link[=add_contiguity_constraints]{add_contiguity_constraints()}}). To further reduce run time, we recommend using proportion-type decisions when calculating the scores (see below for an example). diff --git a/man/eval_target_coverage_summary.Rd b/man/eval_target_coverage_summary.Rd index 00204ac0c..aae7e607d 100644 --- a/man/eval_target_coverage_summary.Rd +++ b/man/eval_target_coverage_summary.Rd @@ -24,7 +24,7 @@ eval_target_coverage_summary(x, solution, include_zone, include_sense) ) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{solution}{\code{numeric}, \code{matrix}, \code{data.frame}, \code{\linkS4class{Raster}}, \code{\linkS4class{Spatial}}, @@ -60,16 +60,16 @@ is \code{TRUE}.} \item{sense}{\code{character} sense associated with each target. Sense values specify the nature of the target. -Typically (e.g. when using the \code{\link[=add_absolute_targets]{add_absolute_targets()}} or +Typically (e.g., when using the \code{\link[=add_absolute_targets]{add_absolute_targets()}} or \code{\link[=add_relative_targets]{add_relative_targets()}} functions), targets are specified using sense values indicating that the total amount of a feature held within a solution (ideally) be greater than or equal to a threshold amount -(i.e. a sense value of \code{">="}). -Additionally, targets (i.e. using the \code{\link[=add_manual_targets]{add_manual_targets()}} function) +(i.e., a sense value of \code{">="}). +Additionally, targets (i.e., using the \code{\link[=add_manual_targets]{add_manual_targets()}} function) can also be specified using sense values indicating that the total amount of a feature held within a solution must be equal to a -threshold amount (i.e. a sense value of \code{"="}) or smaller than or equal -to a threshold amount (i.e. a sense value of \code{"<="}). +threshold amount (i.e., a sense value of \code{"="}) or smaller than or equal +to a threshold amount (i.e., a sense value of \code{"<="}). This column is only included if the argument to \code{include_sense} is \code{TRUE}.} @@ -93,44 +93,45 @@ the feature and (if relevant) zones associated with each target (per the \code{"feature"} and \code{"zone"} columns, respectively). This column is calculated as the sum of the feature data, supplied when creating a \code{\link[=problem]{problem()}} object -(e.g. presence/absence values), weighted by the status of each -planning unit in the solution (e.g. selected or not for prioritization).} +(e.g., presence/absence values), weighted by the status of each +planning unit in the solution (e.g., selected or not for +prioritization).} \item{absolute_shortfall}{ \code{numeric} total amount by which the solution fails to meet each target. This column is calculated as the difference between the total amount held within the solution for the feature and (if relevant) zones -associated with the target (i.e. \code{"absolute_held"} column) and the -target total threshold amount (i.e. \code{"absolute_target"} column), with +associated with the target (i.e., \code{"absolute_held"} column) and the +target total threshold amount (i.e., \code{"absolute_target"} column), with values set to zero depending on the sense specified for the target -(e.g. if the target sense is \code{>=} then the difference is +(e.g., if the target sense is \code{>=} then the difference is set to zero if the value in the \code{"absolute_held"} is smaller than that in the \code{"absolute_target"} column).} \item{relative_target}{\code{numeric} proportion threshold amount associated with each target. This column is calculated by dividing the total threshold amount -associated with each target (i.e. \code{"absolute_target"} column) by +associated with each target (i.e., \code{"absolute_target"} column) by the total amount associated with each target -(i.e. \code{"total_amount"} column).} +(i.e., \code{"total_amount"} column).} \item{relative_held}{\code{numeric} proportion held within the solution for the feature and (if relevant) zones associated with each target (per the \code{"feature"} and \code{"zone"} columns, respectively). This column is calculated by dividing the total amount held -for each target (i.e. \code{"absolute_held"} column) by the +for each target (i.e., \code{"absolute_held"} column) by the total amount for with each target -(i.e. \code{"total_amount"} column).} +(i.e., \code{"total_amount"} column).} \item{relative_shortfall}{\code{numeric} proportion by which the solution fails to meet each target. This column is calculated by dividing the total shortfall for -each target (i.e. \code{"absolute_shortfall"} column) by the -total amount for each target (i.e. \code{"total_amount"} column).} +each target (i.e., \code{"absolute_shortfall"} column) by the +total amount for each target (i.e., \code{"total_amount"} column).} \item{met}{\code{logical} indicating if each target is met by the solution. This column is calculated by checking if the total shortfall associated -with each target (i.e. \verb{"absolute_shortfall}" column) is equal to +with each target (i.e., \verb{"absolute_shortfall}" column) is equal to zero.} } diff --git a/man/feature_abundances.Rd b/man/feature_abundances.Rd index e8ac74cca..4c42ab573 100644 --- a/man/feature_abundances.Rd +++ b/man/feature_abundances.Rd @@ -10,7 +10,7 @@ feature_abundances(x, na.rm) \method{feature_abundances}{ConservationProblem}(x, na.rm = FALSE) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{na.rm}{\code{logical} should planning units with \code{NA} cost data be excluded from the abundance calculations? The default argument @@ -51,10 +51,10 @@ of a conservation planning problem. } \details{ Planning units can have cost data with finite values -(e.g. 0.1, 3, 100) and \code{NA} values. This functionality is provided so +(e.g., 0.1, 3, 100) and \code{NA} values. This functionality is provided so that locations which are not available for protected area acquisition can be included when calculating targets for conservation features -(e.g. when targets are specified using \code{\link[=add_relative_targets]{add_relative_targets()}}). +(e.g., when targets are specified using \code{\link[=add_relative_targets]{add_relative_targets()}}). If the total amount of each feature in all the planning units is required---including the planning units with \code{NA} cost data---then the the \code{na.rm} argument should be set to \code{FALSE}. However, if @@ -137,8 +137,8 @@ p5 <- p3 \%>\% s5 <- solve(p5) # plot the solution -# this solution contains all the planning units with finite cost data (i.e. -# cost data that do not have NA values) +# this solution contains all the planning units with finite cost data +# (i.e., cost data that do not have NA values) plot(s5) } } diff --git a/man/feature_names.Rd b/man/feature_names.Rd index a6fc7a346..930cba968 100644 --- a/man/feature_names.Rd +++ b/man/feature_names.Rd @@ -16,7 +16,7 @@ feature_names(x) \S4method{feature_names}{ZonesCharacter}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) or \code{\link[=Zones]{Zones()}} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) or \code{\link[=Zones]{Zones()}} object.} } \value{ diff --git a/man/importance.Rd b/man/importance.Rd index bd2a6d74d..2a0c2ad82 100644 --- a/man/importance.Rd +++ b/man/importance.Rd @@ -18,7 +18,7 @@ The following methods are available for calculating importance scores: Calculate importance scores using replacement costs (based on Cabeza and Moilanen 2006). These scores quantify the change in the objective -function (e.g. additional costs required to meet feature targets) of the +function (e.g., additional costs required to meet feature targets) of the optimal solution if a given planning unit in a solution cannot be acquired. They can (i) account for the cost of different planning units, (ii) account for multiple management zones, (iii) apply to any objective function, and @@ -29,7 +29,7 @@ values).} Calculate importance scores following Ferrier \emph{et al.} (2000). These scores measure importance based on how critical planning units are for meeting targets. They can only be applied to -conservation problems with a minimum set objective and a single zone (i.e. +conservation problems with a minimum set objective and a single zone (i.e., the classic \emph{Marxan}-type problem). Furthermore---unlike the replacement cost scores---these scores provide a score for each feature within each planning unit, providing insight into @@ -53,8 +53,8 @@ planning problem -- regardless of the objective function or number of zones considered in the problem -- and measure planning unit importance based on degradation of the prioritization. Although the replacement cost scores can be calculated for small and -moderate sized problems (e.g. less than 30,000 planning units), they may not -be feasible for particularly large problems (e.g. more than 100,000 planning +moderate sized problems (e.g., less than 30,000 planning units), they may not +be feasible for particularly large problems (e.g., more than 100,000 planning units). In such cases, we recommend calculating importance scores using the Ferrier method. This is because the Ferrier method can be calculated relatively quickly for large-sized problems and it diff --git a/man/marxan_boundary_data_to_matrix.Rd b/man/marxan_boundary_data_to_matrix.Rd index b1e4d4e45..15fff2d37 100644 --- a/man/marxan_boundary_data_to_matrix.Rd +++ b/man/marxan_boundary_data_to_matrix.Rd @@ -7,7 +7,7 @@ marxan_boundary_data_to_matrix(x, data) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object that +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object that contains planning unit and zone data to ensure that the argument to \code{data} is converted correctly. This argument can be set to \code{NULL} if checks are not required (not recommended).} diff --git a/man/marxan_problem.Rd b/man/marxan_problem.Rd index c05e1886e..bf14f2040 100644 --- a/man/marxan_problem.Rd +++ b/man/marxan_problem.Rd @@ -104,13 +104,13 @@ has an effect when argument to \code{x} is a \code{data.frame}. The default argument is zero.} } \value{ -\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object. +\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object. } \description{ Create a conservation planning \code{\link[=problem]{problem()}} following the mathematical formulations used in \emph{Marxan} (detailed in Beyer \emph{et al.} 2016). Note that these problems are solved using -exact algorithms and not simulated annealing (i.e. the \emph{Marxan} software). +exact algorithms and not simulated annealing (i.e., the \emph{Marxan} software). } \details{ This function is provided as a convenient wrapper for solving diff --git a/man/number_of_features.Rd b/man/number_of_features.Rd index d1c0b895e..63eb3c7a3 100644 --- a/man/number_of_features.Rd +++ b/man/number_of_features.Rd @@ -19,7 +19,7 @@ number_of_features(x) \S4method{number_of_features}{ZonesCharacter}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}), +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}), \code{\linkS4class{OptimizationProblem}}, or \code{\link[=Zones]{Zones()}} object.} } \value{ diff --git a/man/number_of_planning_units.Rd b/man/number_of_planning_units.Rd index 3260fdb77..df8ae2ca7 100644 --- a/man/number_of_planning_units.Rd +++ b/man/number_of_planning_units.Rd @@ -13,7 +13,7 @@ number_of_planning_units(x) \S4method{number_of_planning_units}{OptimizationProblem}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}), +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}), \code{\linkS4class{OptimizationProblem}}, or \code{\link[=Zones]{Zones()}} object.} } \value{ diff --git a/man/number_of_total_units.Rd b/man/number_of_total_units.Rd index 51f2fb80b..b593b28e7 100644 --- a/man/number_of_total_units.Rd +++ b/man/number_of_total_units.Rd @@ -10,7 +10,7 @@ number_of_total_units(x) \S4method{number_of_total_units}{ConservationProblem}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}), +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}), \code{\linkS4class{OptimizationProblem}}, or \code{\link[=Zones]{Zones()}} object.} } \value{ diff --git a/man/number_of_zones.Rd b/man/number_of_zones.Rd index b91669197..03a1d42d0 100644 --- a/man/number_of_zones.Rd +++ b/man/number_of_zones.Rd @@ -19,7 +19,7 @@ number_of_zones(x) \S4method{number_of_zones}{ZonesCharacter}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}), +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}), \code{\linkS4class{OptimizationProblem}}, or \code{\link[=Zones]{Zones()}} object.} } \value{ diff --git a/man/penalties.Rd b/man/penalties.Rd index 315e9307f..8d076abd8 100644 --- a/man/penalties.Rd +++ b/man/penalties.Rd @@ -7,7 +7,7 @@ A penalty can be applied to a conservation planning \code{\link[=problem]{problem()}} to penalize solutions according to a specific metric. Penalties---unlike \link{constraints}---act as an explicit trade-off with the objective -being minimized or maximized (e.g. solution cost when used with +being minimized or maximized (e.g., solution cost when used with \code{\link[=add_min_set_objective]{add_min_set_objective()}}). } \details{ @@ -33,7 +33,7 @@ planning units with high connectivity between them.} \item{\code{\link[=add_linear_penalties]{add_linear_penalties()}}}{Add penalties to a conservation problem to favor solutions that avoid selecting planning units based on a certain variable -(e.g. anthropogenic pressure).} +(e.g., anthropogenic pressure).} } } diff --git a/man/portfolios.Rd b/man/portfolios.Rd index 2f5730e54..286459059 100644 --- a/man/portfolios.Rd +++ b/man/portfolios.Rd @@ -7,10 +7,10 @@ Conservation planning exercises rarely have access to all the data needed to identify the \emph{truly} perfect solution. This is because available data may lack important details -(e.g. land acquisition costs may be unavailable), contain errors -(e.g. species presence/absence data may have false positives), +(e.g., land acquisition costs may be unavailable), contain errors +(e.g., species presence/absence data may have false positives), or key objectives may not be formally incorporated into the -prioritization process (e.g. future land use requirements). +prioritization process (e.g., future land use requirements). As such, conservation planners can help decision makers by providing them with a portfolio of solutions to inform their decision. } diff --git a/man/presolve_check.Rd b/man/presolve_check.Rd index 0a14ca550..91486f2cb 100644 --- a/man/presolve_check.Rd +++ b/man/presolve_check.Rd @@ -13,7 +13,7 @@ presolve_check(x) \method{presolve_check}{OptimizationProblem}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) or +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) or \code{\linkS4class{OptimizationProblem}} object.} } \value{ @@ -37,7 +37,7 @@ locked in, (ii) all planning units are locked out, and (iii) all planning units have negative cost values (after applying penalties if any were specified). Although such conservation planning problems are mathematically valid, they are generally the result of a coding mistake -when building the problem (e.g. using an absurdly high +when building the problem (e.g., using an absurdly high penalty value or using the wrong dataset to lock in planning units). Thus such issues, if they are indeed issues and not false positives, can be fixed by carefully checking the code, data, and parameters used to build @@ -46,15 +46,15 @@ the conservation planning problem. This function then checks for values that may lead to numerical instability issues when solving the problem. Specifically, it checks if the range of values in certain components of the optimization problem are over a -certain threshold (i.e. \eqn{1 \times 10 ^9}{1e+9}) or if the values +certain threshold (i.e., \eqn{1 \times 10 ^9}{1e+9}) or if the values themselves exceed a certain threshold -(i.e. \eqn{1 \times 10^{10}}{1e+10}). +(i.e., \eqn{1 \times 10^{10}}{1e+10}). In most cases, such issues will simply cause an exact algorithm solver to take a very long time to generate a solution. In rare cases, such issues can cause incorrect calculations which can lead to exact algorithm solvers returning infeasible solutions -(e.g. a solution to the minimum set problem where not all targets are met) -or solutions that exceed the specified optimality gap (e.g. a suboptimal +(e.g., a solution to the minimum set problem where not all targets are met) +or solutions that exceed the specified optimality gap (e.g., a suboptimal solution when a zero optimality gap is specified). What can you do if a conservation planning problem fails to pass these @@ -72,7 +72,7 @@ planning unit cost values are too large. For example, if you express the costs of the planning units in terms of USD then you might have some planning units that cost over one billion dollars in large-scale planning exercises. This can be fixed by rescaling the values so that they -are smaller (e.g. multiplying the values by a number smaller than one, or +are smaller (e.g., multiplying the values by a number smaller than one, or expressing them as a fraction of the maximum cost). Let's consider another common issue, let's pretend that you used habitat suitability models to predict the amount of suitable habitat @@ -99,7 +99,7 @@ unit(s) is causing this infeasibility and set its cost to zero. After solving the problem, you will need to manually recalculate the cost of the solutions but at least now you can be confident that you have the optimal solution. Now let's pretend that you are using the maximum features -objective (i.e. \code{\link[=add_max_features_objective]{add_max_features_objective()}}) and assigned some +objective (i.e., \code{\link[=add_max_features_objective]{add_max_features_objective()}}) and assigned some really high weights to the targets for some features to ensure that their targets were met in the optimal solution. If you set the weights for these features to one billion then you will probably run into numerical @@ -107,7 +107,7 @@ instability issues. Instead, you can calculate minimum weight needed to guarantee that these features will be represented in the optimal solution and use this value instead of one billion. This minimum weight value can be calculated as the sum of the weight values for the other features -and adding a small number to it (e.g. 1). Finally, if you're running out +and adding a small number to it (e.g., 1). Finally, if you're running out of ideas for addressing numerical stability issues you have one remaining option: you can use the \code{numeric_focus} argument in the \code{\link[=add_gurobi_solver]{add_gurobi_solver()}} function to tell the solver to pay extra diff --git a/man/prioritizr-deprecated.Rd b/man/prioritizr-deprecated.Rd index b1ecd4db1..29871e48f 100644 --- a/man/prioritizr-deprecated.Rd +++ b/man/prioritizr-deprecated.Rd @@ -51,7 +51,7 @@ package, we have listed alternatives for deprecated the functions (where applicable). If a function is described as being renamed, then this means that only the name of the function has changed -(i.e. the inputs, outputs, and underlying code remain the same). +(i.e., the inputs, outputs, and underlying code remain the same). } \details{ The following functions have been deprecated: diff --git a/man/prioritizr.Rd b/man/prioritizr.Rd index e567fbc50..fc624f28c 100644 --- a/man/prioritizr.Rd +++ b/man/prioritizr.Rd @@ -30,7 +30,7 @@ for more information. This package contains several vignettes that are designed to showcase its functionality. To view them, please use the code \code{vignette("name", package = "prioritizr")} where \code{"name"} is the -name of the desired vignette (e.g. \code{"gurobi_installation"}). +name of the desired vignette (e.g., \code{"gurobi_installation"}). \describe{ diff --git a/man/problem.Rd b/man/problem.Rd index c9ed379d6..c9b938542 100644 --- a/man/problem.Rd +++ b/man/problem.Rd @@ -66,7 +66,7 @@ the study area. To exclude planning units, set the cost for those raster cells to \code{NA}, or use the \code{add_locked_out_constraint} function.} \item{features}{The feature data can be specified in a variety of ways. -The specific formats that can be used depend on the cost data format (i.e. +The specific formats that can be used depend on the cost data format (i.e., argument to \code{x}) and whether the problem should have a single zone or multiple zones. If the problem should have a single zone, then the feature data can be specified following: @@ -76,7 +76,7 @@ data can be specified following: \code{\link[sf:sf]{x = sf::st_sf()}}: \code{\link[raster:Raster-classes]{y = Raster-class}} object showing the distribution of conservation features. Missing -values (i.e. \code{NA} values) can be used to indicate the absence of +values (i.e., \code{NA} values) can be used to indicate the absence of a feature in a particular cell instead of explicitly setting these cells to zero. Note that this argument type for \code{features} can only be used to specify data for problems involving a single zone. @@ -117,7 +117,7 @@ data can be specified following: \code{\link[sf:sf]{x = sf::st_sf()}}: \code{\link[=zones]{y = ZonesRaster}}: object showing the distribution of conservation features in multiple -zones. As above, missing values (i.e. \code{NA} values) can be used to +zones. As above, missing values (i.e., \code{NA} values) can be used to indicate the absence of a feature in a particular cell instead of explicitly setting these cells to zero. \item \code{\link[sp:Spatial-class]{x = Spatial-class}}, or @@ -191,7 +191,7 @@ data for a prioritization. Create a systematic conservation planning problem. This function is used to specify the basic data used in a spatial prioritization problem: the spatial distribution of the planning units and their costs, as well as -the features (e.g. species, ecosystems) that need to be conserved. After +the features (e.g., species, ecosystems) that need to be conserved. After constructing this \code{ConservationProblem-class} object, it can be customized to meet specific goals using \link{objectives}, \link{targets}, \link{constraints}, and @@ -216,34 +216,34 @@ be incorporated by using multiple zones on the management action(s), you can compile the following data. First, you will need to create a set of planning units -(i.e. discrete spatial areas) to inform decision making. +(i.e., discrete spatial areas) to inform decision making. Planning units are often created by subdividing a study region into a set square or hexagonal cells. They can also be created using -administrative boundaries (e.g. provinces), land management boundaries -(e.g. property boundaries derived from cadastral data), or -ecological boundaries (e.g. based on ecosystem classification data). -The size (i.e. spatial grain) of the planning units is often determined +administrative boundaries (e.g., provinces), land management boundaries +(e.g., property boundaries derived from cadastral data), or +ecological boundaries (e.g., based on ecosystem classification data). +The size (i.e., spatial grain) of the planning units is often determined based on a compromise between the scale needed to inform decision making, the spatial accuracy (resolution) of available datasets, and the computational resources available for generating prioritizations -(e.g. RAM and number of CPUs on your computer). +(e.g., RAM and number of CPUs on your computer). Second, you will need data to quantify the cost of implementing implementing each management action within each planning unit. Critically, the cost data should reflect the management action(s) considered in the exercise. For example, costs are often specified using data that reflect economic -expenditure (e.g. land acquisition cost), -socioeconomic conditions (e.g. human population density), +expenditure (e.g., land acquisition cost), +socioeconomic conditions (e.g., human population density), opportunity costs of foregone commercial activities -(e.g. logging or agriculture), or +(e.g., logging or agriculture), or opportunity costs of foregone recreational activities -(e.g. recreational fishing) activities, +(e.g., recreational fishing) activities, In some cases -- depending on the management action(s) considered -- it can make sense to use a constant cost value -(e.g. all planning units are assigned a cost value equal to one) +(e.g., all planning units are assigned a cost value equal to one) or use a cost value based on spatial extent -(e.g. each planning unit is assigned a cost value based on its total area). +(e.g., each planning unit is assigned a cost value based on its total area). Also, in most cases, you want to avoid negative cost values. This because a negative value means that a place is \emph{desirable} for implementing a management action, and such places will almost @@ -254,13 +254,13 @@ management actions within planning units. To achieve this, you will need to select a set of conservation features that relate to the over-arching goals of the exercise. For example, conservation features often include -species (e.g. Clouded Leopard), habitats (e.g. mangroves or +species (e.g., Clouded Leopard), habitats (e.g., mangroves or cloud forest), or ecosystems. The benefit that each feature derives from a planning unit -can take a variety of forms, but is typically occupancy (i.e. +can take a variety of forms, but is typically occupancy (i.e., presence or absence), area of occurrence within each planning unit -(e.g. based on species' geographic range data), or -a measure of habitat suitability (e.g. estimated using a statistical model). +(e.g., based on species' geographic range data), or +a measure of habitat suitability (e.g., estimated using a statistical model). After compiling these data, you have the minimal data need to generate a prioritization. @@ -314,7 +314,7 @@ If you wish to see exactly how a conservation planning problem is formulated as mixed integer linear programming problem, you can use the \code{\link[=write_problem]{write_problem()}} function to save the optimization problem to a plain-text file on your computer and then view it using a standard -text editor (e.g. Notepad). +text editor (e.g., Notepad). Please note that this function internally computes the amount of each feature in each planning unit when this data is not supplied (using the @@ -365,13 +365,13 @@ p5 <- problem(sim_pu_sf, sim_features, "cost") \%>\% add_default_solver(verbose = FALSE) # since geo-processing can be slow for large spatial vector datasets -# (e.g. polygons, lines, points), it can be worthwhile to pre-process the +# (e.g., polygons, lines, points), it can be worthwhile to pre-process the # planning unit data so that it contains columns indicating the amount of # each feature inside each planning unit -# (i.e. each column corresponds to a different feature) +# (i.e., each column corresponds to a different feature) # calculate the amount of each species within each planning unit -# (i.e. SpatialPolygonsDataFrame object) +# (i.e., SpatialPolygonsDataFrame object) pre_proc_data <- rij_matrix(sim_pu_polygons, sim_features) # add extra columns to the polygon (Spatial) planning unit data @@ -404,7 +404,7 @@ p7 <- problem(sim_pu_sf, features = names(pre_proc_data2), "cost") \%>\% # in addition to spatially explicit data, pre-processed aspatial data # can also be used to create a problem -# (e.g. data created using external spreadsheet software) +# (e.g., data created using external spreadsheet software) costs <- sim_pu_polygons$cost features <- data.frame(id = seq_len(nlayers(sim_features)), name = names(sim_features)) diff --git a/man/rij_matrix.Rd b/man/rij_matrix.Rd index f4983a96c..1c12de8f9 100644 --- a/man/rij_matrix.Rd +++ b/man/rij_matrix.Rd @@ -44,7 +44,7 @@ Generate a matrix showing the amount of each feature in each planning unit (also known as an \emph{rij} matrix). } \details{ -Generally, processing vector (i.e. \code{\linkS4class{Spatial}} or +Generally, processing vector (i.e., \code{\linkS4class{Spatial}} or \code{\link[sf:sf]{sf::sf()}}) data takes much longer to process then \code{\linkS4class{Raster}} data, so it is recommended to use \code{\linkS4class{Raster}} data diff --git a/man/run_calculations.Rd b/man/run_calculations.Rd index 9a7685af9..d3afaf9d8 100644 --- a/man/run_calculations.Rd +++ b/man/run_calculations.Rd @@ -7,7 +7,7 @@ run_calculations(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} } \value{ Invisible \code{TRUE} indicating success. @@ -16,7 +16,7 @@ Invisible \code{TRUE} indicating success. Execute preliminary calculations in a conservation problem and store the results for later use. This function is useful when creating slightly different versions of the same conservation planning problem that involve -the same pre-processing steps (e.g. calculating boundary data), because +the same pre-processing steps (e.g., calculating boundary data), because means that the same calculations will not be run multiple times. } \details{ diff --git a/man/solve.Rd b/man/solve.Rd index 93a0de3c1..0d36c471a 100644 --- a/man/solve.Rd +++ b/man/solve.Rd @@ -11,7 +11,7 @@ \S4method{solve}{ConservationProblem,missing}(a, b, ..., run_checks = TRUE, force = FALSE) } \arguments{ -\item{a}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) or +\item{a}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) or \code{\linkS4class{OptimizationProblem}} object.} \item{b}{\code{\linkS4class{Solver}} object. Not used if \code{a} is an @@ -36,8 +36,8 @@ Additionally, the returned object will have the following additional attributes: \code{"objective"} containing the solution's objective, \code{"runtime"} denoting the number of seconds that elapsed while solving the problem, and \code{"status"} describing the status of the solution -(e.g. \code{"OPTIMAL"} indicates that the optimal solution was found). -In most cases, the first solution (e.g. \code{"solution_1"}) +(e.g., \code{"OPTIMAL"} indicates that the optimal solution was found). +In most cases, the first solution (e.g., \code{"solution_1"}) will contain the best solution found by the solver (note that this may not be an optimal solution depending on the gap used to solve the problem and noting that the default gap is 0.1). @@ -52,15 +52,15 @@ for available solvers). If no solver has been explicitly specified, then the best available exact algorithm solver will be used by default (see \code{\link[=add_default_solver]{add_default_solver()}}. Although these exact algorithm solvers will often display a lot of information that isn't really that -helpful (e.g. nodes, cutting planes), they do display information -about the progress they are making on solving the problem (e.g. the +helpful (e.g., nodes, cutting planes), they do display information +about the progress they are making on solving the problem (e.g., the performance of the best solution found at a given point in time). If potential issues were detected during the presolve checks (see \code{\link[=presolve_check]{presolve_check()}}) -and the problem is being forcibly solved (i.e. with \code{force = TRUE}), +and the problem is being forcibly solved (i.e., with \code{force = TRUE}), then it is also worth checking for any warnings displayed by the solver to see if these potential issues are actually causing issues -(e.g. \emph{Gurobi} can display warnings that include +(e.g., \emph{Gurobi} can display warnings that include \code{"Warning: Model contains large matrix coefficient range"} and \code{"Warning: Model contains large rhs"}). @@ -108,11 +108,11 @@ If the argument to \code{a} contains a single zone, then the solution object will contain fields (columns) that solution the values. Specifically, the field name(s) containing the solution values be will named as \code{"solution_XXX"} where \code{"XXX"} corresponds to a solution -identifier (e.g. \code{"solution_1"}). +identifier (e.g., \code{"solution_1"}). If the argument to \code{a} contains multiple zones, then the fields containing solutions will be named as \code{"solution_XXX_YYY"} where \code{"XXX"} corresponds to the solution identifier and \code{"YYY"} is the name -of the management zone (e.g. \code{"solution_1_zone1"}).} +of the management zone (e.g., \code{"solution_1_zone1"}).} } } diff --git a/man/write_problem.Rd b/man/write_problem.Rd index a81af070f..b369386bc 100644 --- a/man/write_problem.Rd +++ b/man/write_problem.Rd @@ -7,7 +7,7 @@ write_problem(x, path) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) object.} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) object.} \item{path}{\code{character} file path to save the problem formulation. The argument should contain a \code{".lp"} or \verb{.mps"} file extension diff --git a/man/zone_names.Rd b/man/zone_names.Rd index 22c57bf03..f486945af 100644 --- a/man/zone_names.Rd +++ b/man/zone_names.Rd @@ -17,7 +17,7 @@ zone_names(x) \S4method{zone_names}{ZonesCharacter}(x) } \arguments{ -\item{x}{\code{\link[=problem]{problem()}} (i.e. \code{\linkS4class{ConservationProblem}}) or \code{\link[=Zones]{Zones()}}} +\item{x}{\code{\link[=problem]{problem()}} (i.e., \code{\linkS4class{ConservationProblem}}) or \code{\link[=Zones]{Zones()}}} } \value{ \code{character} zone names. diff --git a/man/zones.Rd b/man/zones.Rd index a60fee953..ca28f3800 100644 --- a/man/zones.Rd +++ b/man/zones.Rd @@ -28,7 +28,7 @@ Organize data for multiple features for multiple management zones. Specifically, the data should describe the expected amount of each feature within each planning unit given each management zone. For example, the data could describe the occupancy -(e.g. presence/absence), probability of occurrence, or +(e.g., presence/absence), probability of occurrence, or abundance expected for each feature when each planning unit is allocated to a different zone. } diff --git a/pkgdown/extra.css b/pkgdown/extra.css index 68e9213f2..d4c532b04 100644 --- a/pkgdown/extra.css +++ b/pkgdown/extra.css @@ -2,14 +2,20 @@ background: #FFFFFF; } -[id='prioritizr-'] .page-header { - margin-bottom: 0px; - padding-bottom: 0px; +.template-home .contents .section.level1 .page-header { + padding-top: 60px; + padding-bottom: 0px !important; + margin-bottom: 15px !important; } -[id='systematic-conservation-prioritization-in-r'] h1 { - margin-top: 0px; - padding-top: 0px; +[id='prioritizr-'] { + margin-bottom: 0px !important; + padding-bottom: 0px !important; +} + +[id='systematic-conservation-prioritization-in-r'] { + margin-top: 0px !important; + padding-top: 0px !important; font-size: 35px; padding-bottom: 10px; } diff --git a/vignettes/calibrating_trade-offs_tutorial.Rmd b/vignettes/calibrating_trade-offs_tutorial.Rmd new file mode 100644 index 000000000..22089a2c7 --- /dev/null +++ b/vignettes/calibrating_trade-offs_tutorial.Rmd @@ -0,0 +1,678 @@ +--- +title: "Calibrating trade-offs tutorial" +output: + rmarkdown::html_vignette: + toc: true + fig_caption: true + self_contained: yes +fontsize: 11pt +documentclass: article +bibliography: references.bib +csl: reference-style.csl +vignette: > + %\VignetteIndexEntry{Calibrating trade-offs tutorial} + %\VignetteEngine{knitr::rmarkdown_notangle} +--- + +```{r, include = FALSE} +# define dummy variables so that vignette passes package checks +prelim_penalty <- rep(NA_real_, 100) +threshold <- rep(NA_real_, 100) +topsis_results <- data.frame( + alt.row = seq_len(3), score = runif(3), rank = seq_len(3) +) +``` + +```{r, include = FALSE} +# define variables for vignette figures and code execution +h <- 3.5 +w <- 3.5 +is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", + "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) +knitr::opts_chunk$set(fig.align = "center", eval = !is_check) +``` + +## Introduction + +Systematic conservation planning requires making trade-offs [@r4; @r36]. Since different criteria may conflict with one another -- or not align perfectly -- prioritizations need to make trade-offs between different criteria [@r37]. Although some criteria can easily be accounted for by using locked constraints or representation targets [e.g., @r40; @r39], this is not always the case [e.g., @r38]. For example, prioritizations often need to balance overall cost with the overall level spatial fragmentation among reserves [@r41; @r42]. Additionally, prioritizations often need to balance the overall level of connectivity among reserves against other criteria [@r43]. Since the best trade-off depends on a range of factors -- such as available budgets, species' connectivity requirements, and management capacity -- finding the best balance can be challenging. + +The _prioritizr R_ package provides multi-objective optimization methods to help identify the best trade-offs between different criteria. To achieve this, a conservation planning problem can be formulated with a primary objective (e.g., `add_min_set_objective()`) and penalties (e.g., `add_boundary_penalties()`) that relate to such criteria. When building the problem, the nature of the trade-offs can be specified using certain parameters (e.g., the `penalty` parameter of the `add_boundary_penalties()` function). To identify a prioritization that finds the best balance between different criteria, the trade-off parameters can be tuned using a calibration analysis. These analyses -- in the context of systematic conservation planning -- typically involve generating a set of candidate prioritizations based on different parameters, measuring their performance according to each of the criteria, and then selecting a prioritization (or set of prioritizations) based on how well they achieve the criteria [@r41; @r42; @r43]. For example, the _Marxan_ decision support tool has a range of parameters (e.g., species penalty factors, boundary length modifier) that are calibrated to balance cost, species' representation, and spatial fragmentation [@r45]. + +The aim of this tutorial is to provide guidance on calibrating trade-offs when using the _prioritizr R_ package. Here we will explore a couple of different approaches for generating candidate prioritizations, and methods for finding the best balance between different criteria. Specifically, we will try to generate prioritizations that strike the best balance between total cost and spatial fragmentation (measured as total boundary length). As such, the code used in this vignette will be directly applicable when performing a boundary length calibration analysis. + +## Data + +Let's load the packages and dataset used in this tutorial. Since this tutorial uses the _prioritizrdata R_ package along with several other _R_ packages (see below), please ensure that they are all installed. This particular dataset comprises two object: `tas_pu` and `tas_features`. Although we will briefly discuss this dataset below, please refer to the _Tasmania Tutorial_ vignette for further details. + +```{r, message = FALSE} +# load packages +library(prioritizrdata) +library(prioritizr) +library(dplyr) +library(tibble) +library(scales) +library(ggplot2) +library(topsis) +library(withr) + +# load planning unit data +data(tas_pu) + +# convert planning units to sf format +tas_pu <- st_as_sf(tas_pu) + +# load feature data +data(tas_features) + +# print planning unit data +print(tas_pu) + +# print feature data +print(tas_features) +``` + +The `tas_pu` object contains planning units represented as spatial polygons (i.e., converted to a `sf::st_sf()` object). This object has three columns that denote the following information for each planning unit: a unique identifier (`id`), unimproved land value (`cost`), and current conservation status (`locked_in`). Specifically, the conservation status column indicates if at least half the area planning unit is covered by existing protected areas (denoted by a value of 1) or not (denoted by a value of zero). + +```{r, fig.width = w, fig.height = h} +# plot map of planning unit costs +plot(tas_pu[, "cost"], main = "Planning unit costs") + +# plot map of planning unit statuses +plot(tas_pu[, "locked_in"], main = "Planning unit status") +``` + +The `tas_features` object describes the spatial distribution of different vegetation communities (using presence/absence data). We will use the vegetation communities as the biodiversity features for the prioritization. + +```{r, fig.width = 4.5, fig.height = 4.5} +# plot map of the first four vegetation classes +plot(tas_features[[1:4]], main = paste("Feature", 1:4)) +``` + +We can use this dataset to generate a prioritization. Specifically, we will use the minimum set objective so that the optimization process minimizes total cost. We will add representation targets to ensure that prioritizations cover 17% of each vegetation community. Additionally, we will add constraints to ensure that planning units covered by existing protected areas are selected (i.e., locked in). Finally, we will specify that the conservation planning exercise involves binary decisions (i.e., selecting or not selecting planning units for protected area establishment). + +```{r, fig.width = w, fig.height = h, results = "hide"} +# define a problem +p0 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# print problem +print(p0) + +# solve problem +s0 <- solve(p0) + +# print result +print(s0) + +# create column for making a map of the prioritization +s0$map_1 <- case_when( + s0$locked_in > 0.5 ~ "locked in", + s0$solution_1 > 0.5 ~ "priority", + TRUE ~ "other" +) + +# plot map of prioritization +plot( + s0[, "map_1"], pal = c("purple", "grey90", "darkgreen"), + main = NULL, key.pos = 1 +) +``` + +We can see that the priority areas identified by the prioritization are scattered across the study area (shown in green). Indeed, none of the priority areas are connect to existing protected areas (shown in purple), and very of them are connect with other priority areas. As such, the prioritization has a high level of spatial fragmentation. If it is important avoid such levels of spatial fragmentation, then we will need to explicitly account spatial fragmentation in the optimization process. + +## Preliminary processing + +We need to conduct some preliminary processing procedures to prepare the data for subsequent analysis. This is important to help make it easier to find suitable trade-off parameters, and avoid numerical scaling issues that can result in overly long run times (see `presolve_check()` for further information). These processing steps are akin to data scaling (or normalization) procedures that are applied in statistical analysis to improve model convergence. + +The first processing procedure involves setting the cost values for all locked in planning units to zero. This is so that the total cost estimates of the prioritization reflects the total cost of establishing new protected areas -- not just total land value. In other words, we want the total cost estimate for a prioritization to reflect the cost of implementing conservation actions. **This procedure is especially important when using the hierarchical approach described below, so that cost thresholds are based on percentage increases in the cost of establishing new protected areas.** + +```{r, fig.width = w, fig.height = h} + +# set costs for planning units covered by existing protected areas to zero +tas_pu$cost[tas_pu$locked_in > 0.5] <- 0 + +# plot map of planning unit costs +plot(tas_pu[, "cost"], main = "Planning unit cost") +``` + +The second procedure involves pre-computing the boundary length data and manually re-scaling the boundary length values. This procedure is important because boundary length values are often very large that, in turn, can cause numerical issues that result in excessive run times (see `presolve_check()` for further details). + +```{r} +# generate boundary length data for the planning units +tas_bd <- boundary_matrix(tas_pu) + +# manually re-scale the boundary length values +tas_bd@x <- rescale(tas_bd@x, to = c(0.01, 100)) +``` + +After applying these procedures, our data is ready for subsequent analysis. + +## Generating candidate prioritizations + +Here we will start the calibration analysis by generating a set of candidate prioritizations. Specifically, these prioritizations will be generated using different parameters to specify different trade-offs between the different criteria. Since this tutorial involves navigating trade-offs between the overall cost of a prioritization and the level of spatial fragmentation associated with a prioritization (as measured by total boundary length), we will generate prioritizations using different parameters related to these criteria. We will examine two approaches for generating candidate prioritizations based on multi-objective optimization procedures. +**Although we'll be examining both approaches in this tutorial, you would normally only use one of these approaches when conducting your own analysis** + +### Blended approach + +The blended approach for multi-objective optimization involves combining separate criteria (e.g., total cost and total boundary length) into a single joint criterion. To achieve this, a trade-off (or scaling) parameter is used to specify the relative importance of each criterion. This approach is the default approach provided by the _prioritizr R_ package. Specifically, each of the functions for adding a penalty to a problem formulation (e.g., `add_boundary_penalties()`) contains a parameter to control the relative importance of the penalties (i.e., the `penalty` parameter). For example, when using the `add_boundary_penalties()` function, setting a high `penalty` value will indicate that it is important to reduce the overall exposed boundary (perimeter) of the prioritization. + +The main challenge with the blended approach is identifying a range of suitable `penalty` values to generate candidate prioritizations. If we set a `penalty` value that is too low, then the penalties will have no effect (e.g., boundary length penalties would have no effect on the prioritization). If we set a `penalty` value too high, then the prioritization will effectively ignore the primary objective. In such cases, the prioritization will be overly spatially clustered -- because the planning unit cost values have no effect --- and contain a single reserve. Thus we need to find a suitable range of `penalty` values before we can generate a set of candidate prioritizations. + +We can find a suitable range of `penalty` values by generating a set of preliminary prioritizations. These preliminary prioritizations will be based on different `penalty` values -- similar to the process for generating the candidate prioritizations -- but solved using customized settings that sacrifice optimality for fast run times (see below for details). This is especially important because specifying a `penalty` value that is too high will cause the optimization process to take a very long time to generate a solutions (due to the numerical scaling issues mentioned previously). To find a suitable range of `penalty` values, we need to identify an upper limit for the `penalty` value (i.e., the highest `penalty` value that result in a prioritization containing a single reserve). Let's create some preliminary `penalty` to identify this upper limit. **Please note that you might need to adjust the `prelim_upper` value to find the upper limit when analyzing different datasets.** + +```{r} +# define a range of different penalty values +## note that we use a power scale to avoid focusing on very high penalty values +prelim_lower <- -5 # change this for your own data +prelim_upper <- 2.8 # change this for your own data +prelim_penalty <- round(10^seq(prelim_lower, prelim_upper, length.out = 9), 5) + +# print penalty values +print(prelim_penalty) +``` + +Next, let's use the preliminary `penalty` values to generate preliminary prioritizations. As mentioned earlier, we will generate these preliminary prioritizations using customized settings to reduce runtime. Specifically, we will set a time limit of 10 minutes per run, and relax the optimality gap to 20%. Although we would not normally use such settings -- because the resulting prioritizations are not guaranteed to be near-optimal (the default gap is 10%) -- this is fine because our goal here is to tune the preliminary `penalty` values. Indeed, none of these preliminary prioritizations will be considered as candidate prioritizations. **Please note that you might need to set a higher time limit, or relax the optimality gap even further (e.g., 40%) when analyzing larger datasets.** + +```{r, results = "hide"} +# define a problem without boundary penalties +p0 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# generate preliminary prioritizations based on each penalty +## note that we specify a relaxed gap and time limit for the solver +prelim_blended_results <- lapply(prelim_penalty, function(x) { + s <- + p0 %>% + add_boundary_penalties(penalty = x, data = tas_bd) %>% + add_default_solver(gap = 0.2, time_limit = 10 * 60) %>% + solve() + s <- data.frame(s = s$solution_1) + names(s) <- with_options(list(scipen = 30), paste0("penalty_", x)) + s +}) + +# format results as a single spatial object +prelim_blended_results <- cbind( + tas_pu, do.call(bind_cols, prelim_blended_results) +) + +# preview results +print(prelim_blended_results) +``` + +After generating the preliminary prioritizations, let's create some maps to visualize them. In particular, we want to understand how different penalty values influence the spatial fragmentation of the prioritizations. + +```{r, fig.width = 7, fig.height = 5.0} +# plot maps of prioritizations +plot( + x = + prelim_blended_results %>% + dplyr::select(starts_with("penalty_")) %>% + mutate_if(is.numeric, function(x) { + case_when( + prelim_blended_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +We can see that as the `penalty` value used to generate the prioritizations increases, the spatial fragmentation of the prioritizations decreases. In particular, we can see that a `penalty` value of `r prelim_penalty[8]` results in a single reserve -- meaning this is our best guess of the upper limit. Using this `penalty` value as an upper limit, we will now generate a second series of prioritizations that will be the candidate prioritizations. Critically, these candidate prioritizations will not be generated using with time limit and be generated using a more suitable gap (i.e., default gap of 10%). + +```{r, fig.width = 7, fig.height = 5.0, results = "hide"} +# define a new set of penalty values +## note that we use a linear scale to explore both low and high penalty values +penalty <- round(seq(1e-5, prelim_penalty[8], length.out = 9), 5) + +# generate prioritizations based on each penalty +blended_results <- lapply(penalty, function(x) { + ## generate solution + s <- + p0 %>% + add_boundary_penalties(penalty = x, data = tas_bd) %>% + solve() + ## return data frame with solution + s <- data.frame(s = s$solution_1) + names(s) <- with_options(list(scipen = 30), paste0("penalty_", x)) + s +}) + +# format results as a single spatial object +blended_results <- cbind(tas_pu, do.call(bind_cols, blended_results)) + +# plot maps of prioritizations +plot( + x = + blended_results %>% + dplyr::select(starts_with("penalty_")) %>% + mutate_if(is.numeric, function(x) { + case_when( + blended_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +We now have a set of candidate prioritizations generated using the blended approach. The main advantages of this approach is that it is similar calibration analyses used by other decision support tools for conservation (i.e., _Marxan_) and it is relatively straightforward to implement. However, this approach also has a key disadvantage. Because the `penalty` parameter is a unitless trade-off parameter -- meaning that we can't leverage existing knowledge to specify a suitable range of `penalty` values -- we first have to conduct a preliminary analysis to identify an upper limit. Although finding an upper limit was fairly simple for the example datset, it can be difficult to find for more realistic data. In the next section, we will show how to generate a set of candidate prioritzations using the hierachical approach -- which does not have this disadvantage. + +### Hierarchical approach + +The hierarchical approach for multi-objective optimization involves generating a series of incremental prioritizations -- using a different objective at each increment to refine the previous solution -- until the final solution achieves all of the objectives. The advantage with this approach is that we can specify trade-off parameters for each objective based on a percentage from optimality. This means that we can leverage our own knowledge -- or that of decision maker -- when to generate a range of suitable trade-off parameters. As such, this approach does not require us to generate a series of preliminary prioritizations. + +This approach is slightly more complicated to implement within the _prioritizr R_ package then the blended approach. To start off, we generate an initial prioritization based on a problem formulation that does not consider any penalties. Critically, we will generate this prioritization by solving the problem to optimality (using the `gap` parameter of the `add_default_solver()` function). + +```{r, fig.width = w, fig.height = h, results = "hide"} +# define a problem without boundary penalties +p1 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() %>% + add_default_solver(gap = 0) + +# solve problem +s1 <- solve(p1) + +# add column for making a map of the prioritization +s1$map_1 <- case_when( + s1$locked_in > 0.5 ~ "locked in", + s1$solution_1 > 0.5 ~ "priority", + TRUE ~ "other" +) + +# plot map of prioritization +plot( + s0[, "map_1"], pal = c("purple", "grey90", "darkgreen"), + main = NULL, key.pos = 1 +) +``` + +Next, we will calculate the total cost of the initial prioritization. + +```{r, fig.width = w, fig.height = h} +# calculate cost +s1_cost <- eval_cost_summary(p1, s1[, "solution_1"])$cost + +# print cost +print(s1_cost) +``` + +Now we will calculate a series of cost thresholds. These cost thresholds will be calculated by inflating the cost of the initial prioritization by a range of percentage values. Since these values are percentages -- and not unitless values unlike those used in the blended approach -- we can use domain knowledge to specify a suitable range of cost thresholds. For this tutorial, let's assume that it would be impractical -- per our domain knowledge -- to expend more than four times the total cost of the initial prioritization to reduce spatial fragmentation. + +```{r} +# calculate cost threshold values +threshold <- s1_cost + (s1_cost * seq(1e-5, 4, length.out = 9)) +threshold <- ceiling(threshold) + +# print cost thresholds +print(threshold) +``` + +After generating the cost thresholds, we can use them to generate prioritizations. Specifically, we will generate prioritizations that aim to minimize total boundary length as much as possible -- ignoring the total cost of the prioritizations -- whilst ensuring that the total cost of the prioritization does not exceed a given cost threshold and the other considerations (e.g., locked in constraints). To achieve this, we create a new column in the `tas_pu` object that contains only zero values (called `zeros`) and use this new column to specify the cost data for the prioritizations. +**Although we normally recommend against cost data that contain zero values -- because planning units with zero costs are often selected in prioritizations even if they are not needed -- here we use zero cost values so that the prioritization will focus exclusively on spatial fragmentation.** Additionally, when it comes to generating the prioritization, we will add linear constraints to ensure that the total cost of the prioritization does not exceed a given cost threshold (using the `add_linear_constraints()` function). + +```{r, fig.width = 7, fig.height = 5.0, results = "hide"} +# add a column with zeros +tas_pu$zeros <- 0 + +# define a problem with zero cost values and boundary penalties +## note that because all the costs are all zero, it doesn't actually +## matter what penalty value is used (as long as the value is > 0) +## and so we just use a value of 1 +p2 <- problem(tas_pu, tas_features, cost_column = "zeros") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 1, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# generate prioritizations based on each cost threshold +## note that the prioritizations are solved to within 10% of optimality +## (the default gap) because the gap is not specified +hierarchical_results <- lapply(threshold, function(x) { + ## generate solution by adding a constraint based on the threshold and + ## using the "real" cost values (i.e., not zeros) + s <- + p2 %>% + add_linear_constraints(threshold = x, sense = "<=", data = "cost") %>% + solve() + ## return data frame with solution + s <- data.frame(s = s$solution_1) + names(s) <- paste0("threshold_", x) + s +}) + +# format results as a single spatial object +hierarchical_results <- cbind(tas_pu, do.call(bind_cols, hierarchical_results)) + +# plot maps of prioritizations +plot( + x = + hierarchical_results %>% + dplyr::select(starts_with("threshold_")) %>% + mutate_if(is.numeric, function(x) { + case_when( + hierarchical_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +We now have a set of candidate prioritizations generated using the hierarchical approach. This approach can be much faster than the blended approach because it does not require generating a set of prioritizations to identify an upper limit for the `penalty` trade-off parameter. After generating a set of candidate prioritizations, we can then calculate performance metrics to compare the prioritizations. + +## Calculating performance metrics + +Here we will calculate performance metrics to compare the prioritizations. Since we aim to navigate trade-offs between the total cost of a prioritization and the overall level of spatial fragmentation associated with a prioritization (as measured by total boundary length), we will calculate metrics to assess these criteria. Although we generated two sets of candidate prioritizations in the previous section; for brevity, here we will consider the candidate prioritizations generated using the hierarchical approach. **Please note that you could also apply the following procedures to candidate prioritizations generated using the blended approach.** + +```{r} +# calculate metrics for prioritizations +## note that we use p0 and not p1 so that cost calculations are based +## on the cost values and not zeros +hierarchical_metrics <- lapply( + grep("threshold_", names(hierarchical_results)), function(x) { + x <- hierarchical_results[, x] + data.frame( + total_cost = eval_cost_summary(p0, x)$cost, + total_boundary_length = eval_boundary_summary(p0, x)$boundary + ) + } +) +hierarchical_metrics <- do.call(bind_rows, hierarchical_metrics) +hierarchical_metrics$threshold <- threshold +hierarchical_metrics <- as_tibble(hierarchical_metrics) + +# preview metrics +print(hierarchical_metrics) +``` + +After calculating the metrics, let's we can use them to help select a prioritization. + +## Selecting a prioritization + +Now we need to decide on which candidate prioritization achieves the best trade-off. There are a range of qualitative and quantitative methods that are available to select a candidate prioritization [@r45]. Here we will consider three different methods. Since some of these methods a set of candidate prioritizations, we will use the candidate prioritizations using the hierarchical approach for these methods. To keep track of the prioritizations selected by different methods, let's create a `results_data` table. + +```{r} +# create data for plotting +result_data <- + hierarchical_metrics %>% + ## rename threshold column to value column + rename(value = "threshold") %>% + ## add column with column names that contain candidate prioritizations + mutate(name = grep( + "threshold_", names(hierarchical_results), value = TRUE, fixed = TRUE + )) %>% + ## add column with labels for plotting + mutate(label = paste("Threshold =", value)) %>% + ## add column to keep track prioritizations selected by different methods + mutate(method = "none") + +# print table +print(result_data) +``` + +Next, let's examine some different methods for selecting prioritizations. + +### Visual method + +One qualitative method involves plotting the relationship between the different criteria, and using the plot to visually select a candidate prioritization. This visual method is often used to help calibrate trade-offs among prioritizations generated using the _Marxan_ decision support tool [e.g., @r41; @r42]. So, let's create a plot to select a prioritization. + +```{r, fig.width = 7, fig.height = 5.0} +# create plot to visualize trade-offs and show selected candidate prioritization +result_plot <- + ggplot( + data = result_data, + aes(x = total_boundary_length, y = total_cost, label = label) + ) + + geom_line() + + geom_point(size = 3) + + geom_text(hjust = -0.15) + + scale_color_manual( + values = c("visual" = "blue", "not selected" ="black") + ) + + xlab("Total boundary length of prioritization") + + ylab("Total cost of prioritization") + + scale_x_continuous(expand = expansion(mult = c(0.05, 0.4))) + + theme(legend.title = element_blank()) + +# render plot +print(result_plot) +``` + +We can see that there is a clear relationship between total cost and total boundary length. It would seem that in order to achieve a lower total boundary length -- and thus lower spatial fragmentation -- the prioritization must have a greater cost. Although we might expect the results to show a smoother curve -- in other words, only Pareto dominant solutions -- this result is expected because we generated candidate prioritizations using the default optimality gap of 10%. Typically, the visual method involves selecting a prioritization near the elbow of the plot. So, let's select the prioritization generated using a `threshold` value of `r threshold[3]`. To keep of the prioritizations selected based on different methods, let's create a `method` column in the `result_data` table. + +```{r} +# specify prioritization selected by visual method +result_data$method[3] <- "visual" +``` + +Next, let's consider a quantitative approach. + +### TOPSIS method + +Multiple-criteria decision analysis is a disciple that uses analytical methods to evaluate trade-offs between multiple criteria [MCDA; reviewed in @r44]. Although this discipline contains many different methods, here we will use the the Technique for Order of Preference by Similarity to Ideal Solution (TOPSIS) method [@r46]. This method requires (i) data describing the performance of each prioritization according the different criteria, (ii) weights to encode the relative importance of each criteria, and (iii) details on whether each criteria should ideally be minimized or maximized. Let's run the analysis, assuming that we equal weighting for total cost and total boundary length. + +```{r} +# calculate TOPSIS scores +topsis_results <- topsis( + decision = + hierarchical_metrics %>% + dplyr::select(total_cost, total_boundary_length) %>% + as.matrix(), + weights = c(1, 1), + impacts = c("-", "-") +) + +# print results +print(topsis_results) +``` + +The candidate prioritization with the greatest TOPSIS score is considered to represent the best trade-off between total cost and total boundary length. So, based on this method, we would select the prioritization generated using a `threshold` value of `r threshold[which.max(topsis_results$score)]`. Let's update the `result_data` with this information. + +```{r} +# add column indicating prioritization selected by TOPSIS method +result_data$method[which.max(topsis_results$score)] <- "TOPSIS" +``` + +Next, let's consider another quantitative method. + +### Cohon *et al.* (1979) method + +This method is based on an algorithm that was originally developed by Cohon *et al.* [@r49], and was later adapted for use in systematic conservation planning [@r50]. Specifically, it involves generating two ideal prioritizations -- with each prioritization representing the ideal prioritization for each criteria -- and then using the performance metrics calculated for these prioritizations to automatically derive a trade-off `penalty` value [@r45; @r49]. Thus, unlike the two other methods, this method does not require a set of candidate prioritizations. **As such, this method can be used to find a prioritization that meets multiple criteria in a much shorter period of time than the other methods.** To implement this method, we first need to generate the ideal prioritizations (note that we specify an gap of 0% to ensure optimality). + +```{r, results = "hide"} +# generate ideal prioritization based on cost criteria +## note that this is simply the same as the s1 prioritization we generated +## for the hierarchical approach +p3 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() %>% + add_default_solver(gap = 0) + +# solve problem +s3 <- solve(p3) + +# generate ideal prioritization based on spatial fragmentation criteria +## note that any non-zero penalty value would here, +## so we just use a penalty of 1 +p4 <- problem(tas_pu, tas_features, cost_column = "zeros") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 1, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() %>% + add_default_solver(gap = 0) + +# solve problem +s4 <- solve(p4) +``` + +Next, let's calculate the performance metrics for these prioritizations. + +```{r} +# generate problem formulation with costs and boundary penalties for +# calculating performance metrics +p5 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 1, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# calculate performance metrics for ideal cost prioritization +s3_metrics <- tibble( + total_cost = eval_cost_summary(p5, s3[, "solution_1"])$cost, + total_boundary_length = + eval_boundary_summary(p5, s3[, "solution_1"])$boundary +) + +# calculate performance metrics for ideal boundary length prioritization +s4_metrics <- tibble( + total_cost = eval_cost_summary(p5, s4[, "solution_1"])$cost, + total_boundary_length = + eval_boundary_summary(p5, s4[, "solution_1"])$boundary +) +``` + +After calculating these performance metrics, we can use them to automatically calculate a `penalty` value. + +```{r} +# calculate penalty value based on Cohon et al. 1979 +cohon_penalty <- abs( + (s3_metrics$total_cost - s4_metrics$total_cost) / + (s3_metrics$total_boundary_length - s4_metrics$total_boundary_length) +) + +# round to 5 decimal places to avoid numerical issues during optimization +cohon_penalty <- round(cohon_penalty, 5) + +# print penalty value +print(cohon_penalty) +``` + +Now that we have calculated a `penalty` value using this method, we can use it to generate a prioritization. + +```{r, results = "hide"} +# generate prioritization using penalty value calculated using Cohon et al. 1979 +p6 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = cohon_penalty, data = tas_bd) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() + +# solve problem +s6 <- solve(p6) +``` + +Let's update the `results_data` table with results about the prioritization. + +```{r} +# add new row with data for prioritization generated following Cohon et al. 1979 +result_data <- bind_rows( + result_data, + tibble( + total_cost = eval_cost_summary(p6, s6[, "solution_1"])$cost, + total_boundary_length = + eval_boundary_summary(p6, s6[, "solution_1"])$boundary, + value = cohon_penalty, + name = paste0("penalty_", cohon_penalty), + label = paste0("Penalty = ", cohon_penalty), + method = "Cohon" + ) +) +``` + +Next, let's compare the prioritizations selected by different methods. + +### Method comparison + +Let's create a plot to visualize the results from the different methods. + +```{r, fig.width = 7, fig.height = 5.0} +# create plot to visualize trade-offs and show selected candidate prioritization +result_plot <- + ggplot( + data = + result_data %>% + mutate(vjust = if_else(method == "Cohon", -1, 0.5)), + aes(x = total_boundary_length, y = total_cost, label = label) + ) + + geom_line() + + geom_point(aes(color = method), size = 3) + + geom_text(aes(vjust = vjust, color = method), hjust = -0.1) + + scale_color_manual( + name = "Method", + values = c( + "visual" = "#984ea3", + "none" = "#000000", + "TOPSIS" = "#e41a1c", + "Cohon" = "#377eb8" + ) + ) + + xlab("Total boundary length of prioritization") + + ylab("Total cost of prioritization") + + scale_x_continuous(expand = expansion(mult = c(0.05, 0.4))) + +# render plot +print(result_plot) +``` + +We can see that the different method selected different prioritizations. To further compare the results from the different methods, let's create some maps showing the selected prioritizations. + +```{r, fig.width = 7.0, fig.height = h} +# extract column names for creating the prioritizations +visual_name <- result_data$name[[which(result_data$method == "visual")]] +topsis_name <- result_data$name[[which(result_data$method == "TOPSIS")]] + +# create object with selected prioritizations +solutions <- bind_cols( + tas_pu, + hierarchical_results %>% + st_drop_geometry() %>% + dplyr::select(all_of(c(visual_name, topsis_name))) %>% + setNames(c("Visual", "TOPSIS")), + s6 %>% + st_drop_geometry() %>% + dplyr::select(solution_1) %>% + rename(Cohon = "solution_1") +) + +# plot maps of selected prioritizations +plot( + x = + solutions %>% + dplyr::select(Visual, TOPSIS, Cohon) %>% + mutate_if(is.numeric, function(x) { + case_when( + hierarchical_results$locked_in > 0.5 ~ "locked in", + x > 0.5 ~ "priority", + TRUE ~ "other" + ) + }), + pal = c("purple", "grey90", "darkgreen") +) +``` + +How do we determine which one is best? This is difficult to say. Ideally, additional information could be used to help select a prioritization, such as knowledge on available resources, species' connectivity requirements, and impacts of neighboring land use. However, from a practical perspective, prioritizations generated for academic contexts might find the quantitative approaches more useful because they have greater transparency and reproducibility. Ultimately, all of these methods are designed to support decision making. This means that they are intended to assist the decision making process, not serve as a replacement. + +## Conclusion + +Hopefully, this vignette has provided a useful introduction for resolving trade-offs in prioritizations. Although we only explored trade-offs between total cost and spatial fragmentation in this tutorial, this analysis could be adapted to explore trade-offs between a wide range of different criteria. For instance, instead of considering total cost as the primary objective, future analyses could explore trade-offs with feature representation (using the `add_min_shortfall_objective()` function). Additionally, instead of spatial fragmentation, future analyses could explore trade-offs that directly relate to connectivity (using the `add_connectivity_penalties()` function) or specific variables of interest -- such as ecosystem intactness or inverse human footprint index [@r47; @r48] -- to inform decision making (using the `add_linear_penalties()` function). Furthermore, after identifying the best `penalty` or `threshold` values to strike a balance between multiple criteria, you could generate a portfolio of prioritizations (e.g., using the `add_gap_portfolio_function()`) to find multiple options for achieving a similar balance. This might be helpful when you need to generate a set of prioritizations that have comparable performance -- in terms of how well they achieve different criteria -- but select different planning units. + +## References diff --git a/vignettes/connectivity_tutorial.Rmd b/vignettes/connectivity_tutorial.Rmd new file mode 100644 index 000000000..4deb120fc --- /dev/null +++ b/vignettes/connectivity_tutorial.Rmd @@ -0,0 +1,487 @@ +--- +title: "Connectivity tutorial" +output: + rmarkdown::html_vignette: + toc: true + fig_caption: true + self_contained: yes +fontsize: 11pt +documentclass: article +bibliography: references.bib +csl: reference-style.csl +vignette: > + %\VignetteIndexEntry{Connectivity tutorial} + %\VignetteEngine{knitr::rmarkdown_notangle} +--- + +```{r, include = FALSE} +h <- 4.5 +w <- 4.5 +is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", + "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) +knitr::opts_knit$set(global.par = TRUE) +knitr::opts_chunk$set( + fig.align = "center", eval = !is_check, + fig.width = 4.0, fig.height = 3.5 +) +``` + +## Introduction + +Connectivity is a key consideration in systematic conservation planning [@r4; @r55]. This is because isolated and fragmented populations are often more vulnerable to extinction [@r60; @r61; @r62]. To promote connectivity in prioritizations, a range of different approaches are available [reviewed in @r56]. These approaches can solely on the spatial configuration of a prioritization to enhance structural connectivity [e.g, reducing the spatial fragmentation of a prioritization\; @r2]. They can also leverage data -- such as environmental, river flow, and telemetry data -- to generate prioritizations that promote functional connectivity [e.g., @r43; @r58; @r59]. + +The aim of this tutorial is to show how connectivity can be incorporated into prioritizations using the _prioritizr R_ package. Here we will explore various approaches for incorporating connectivity, and see how they alter the spatial configuration of prioritizations. As you will discover, many of these approaches involve setting threshold or penalty values to specify the relative importance of connectivity compared to other criteria (e.g., overall cost). For more information on calibrating these values, please see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html). + +## Data + +The dataset used in this tutorial was created for the Coastal Douglas-fir Conservation Partnership [CDFCP\; @r29]. Although the original dataset covers a much larger area; for brevity, here we focus only on Salt Spring Island, British Columbia. Briefly, Salt Spring Island supports a diverse and globally unique mix of dry forest and savanna habitats. Today, these habitats are critically threatened due to land conversion, invasive species, and altered disturbance regimes. For more information on the data, please refer to the [Marxan tool portal](https://arcese.forestry.ubc.ca/marxan-tool/) and the [tool tutorial](https://peter-arcese-lab.sites.olt.ubc.ca/files/2016/09/CDFCP_tutorial_2017_05.pdf). + +
    + +![Extent of Coastal Douglas-fir Conservation Partnership Tool area and location of Salt Spring Island](figures/map.jpg) + +
    + +Let's begin by loading the packages and data for this tutorial. Since this tutorial requires the _prioritizrdata R_ package, please ensure that it is installed. Specifically, two objects underpin the data for this tutorial. The `salt_pu` object specifies the planning unit data as a raster layer (i.e., `RasterLayer` object), and the `salt_features` object contains biodiversity data represented as a multi-band raster stack (i.e., a `RasterStack` object). + +```{r, message = FALSE} +# load packages +library(prioritizr) +library(prioritizrdata) +library(scales) + +# load planning unit data +data(salt_pu) + +# load ecological data +data(salt_features) +``` + +Now we will conduct some preliminary processing. Specifically, we will aggregate from the 100 m resolution to the 300 m resolution. This is to reduce the time needed to generate prioritizations in this tutorial. In practice, we generally recommend consider other criteria too -- such as the spatial scale that is relevant for decision making and the resolution of available datasets -- when deciding on an appropriate scale for planning units. + +```{r} +# aggregate data to coarser resolution +salt_pu <- aggregate(salt_pu, fact = 3) +salt_features <- aggregate(salt_features, fact = 3) +``` + +Next, let's have a look at the `salt_pu` object. Here each grid cell represents a planning unit, and the grid cell values denote acquisition costs [@r28]. To aid with visualization, we will log-transform the values when plotting them on a map. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# print planning unit data +print(salt_pu) + +# plot map showing the planning units costs on a log-scale +plot(log(salt_pu), main = "Planning unit costs (log)", axes = FALSE) +``` + +Let's also look at the `salt_features` object. This object is a stack of raster layers, with each layer corresponding to a different variable that describes a particular aspect of biodiversity. The first four layers correspond to different ecological communities (i.e., _Old Forest_, _Savannah_, _Wetland_, and _Shrub_ communities), and their cell values indicate the probability of encountering a bird species associated a given community. The fifth layer describes the inverse probability of occurrence of human commensal species. In this tutorial, we will use the first four layers as biodiversity features, and the fifth layer to help parameterize connectivity (wherein higher values denote greater connectivity). So, let's extract the data and visualize them. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# print original data +print(salt_features) + +# extract connectivity data +salt_con <- salt_features[[nlayers(salt_features)]] + +# print connectivity data +print(salt_con) + +# plot map showing the connectivity data +plot(salt_con, main = "Connectivity data", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1), oma = c(0, 0, 0, 0.5)) +``` + +```{r, fig.width = 7.0, fig.height = 7.0} +# extract ecological communities and use these as features +salt_features <- salt_features[[seq_len(4)]] + +# assign names to features +names(salt_features) <- c("Old_Forest", "Savannah", "Wetland", "Shrub") + +# print features +print(salt_features) + +# plot map showing the distribution of the features +plot(salt_features, main = names(salt_features), axes = FALSE) +``` + +## Baseline problem + +In this tutorial, we will explore a few different ways of incorporating connectivity into prioritizations. To enable comparisons among prioritizations based on different approaches, we will first create a baseline problem formulation that we will subsequently customize to incorporate connectivity. Specifically, we will formulate the baseline problem using the minimum set objective. We will use representation targets of 17% -- based on [Aichi Biodiversity Target 11](https://www.cbd.int/sp/targets/) -- to provide adequate coverage of each ecological community. Additionally, because land properties on Salt Spring Island can either be acquired in their entirety or not at all, we will use binary decision types. This means that planning units are either selected in the solution or not selected in the solution---planning units cannot be partially acquired. Given all these details, let's formulate the baseline problem. + +```{r} +# create problem +p0 <- problem(salt_pu, salt_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.17) %>% + add_binary_decisions() %>% + add_default_solver() + +# print problem +print(p0) +``` + +After formulating the baseline problem, we can solve it to generate a prioritization. + +```{r, results = "hide"} +# solve problem +s0 <- solve(p0) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# print solution +print(s0) + +# plot solution +plot( + s0, main = "Baseline prioritization", axes = FALSE, + breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Next, let's explore some options for incorporating connectivity. + +## Adding constraints + +Let's explore approaches for promoting connectivity in prioritizations by adding constraints to the baseline problem formulation. These approaches ensure that prioritizations exhibit certain characteristics [e.g., ensure prioritizations form a contiguous reserve\; @r57]. This means that, regardless of the optimality gap used to generate a prioritization, the prioritization will always exhibit these characteristics. + +### Neighbor constraints + +Neighbor constraints can be added to ensure that each selected planning unit has a certain number of neighbors surrounding it (using the `add_neighbor_constraints()` function) [based on @r16]. The `k` parameter can be used to specify the required number of neighbors for each selected planning unit. Let's generate a prioritization by specifying that each planning unit requires at least two neighbors. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added neighbor constraints and solve it +s1 <- p0 %>% + add_neighbor_constraints(k = 2) %>% + solve() + +# plot solutions +plot( + stack(s0, s1), main = c("baseline", "neighbors constraints"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +### Contiguity constraints + +Contiguity constraints can be added to ensure that all planning units form a single contiguous reserve (using the `add_contiguity_constraints()` function) [similar to @r57]. These constraints are extremely complex. As such, they can only be applied to small conservation planning problems and the _Gurobi_ solver is required to solve them in a feasible period of time. Since it would take a long time to generate a near-optimal prioritization for this dataset with contiguity constraints, we will also tell the solver to simply return the first solution that it finds which meets the representation targets and the contiguity constraints. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added contiguity constraints and solve it +s2 <- p0 %>% + add_contiguity_constraints() %>% + add_gurobi_solver(first_feasible = TRUE) %>% + solve() + +# plot solutions +plot( + stack(s0, s2), main = c("baseline", "contiguity constraints"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +There is also an even more complex version of the contiguity constraints that is available. These constraints -- termed feature contiguity constraints [similar to @r64] -- can be added to ensure that all of the selected planning units used to the reach representation targets within a prioritization form a contiguous network for each feature (using the `add_feature_contiguity_constraints()` function). In other words, they ensure that each feature can disperse through the prioritization to access a target threshold amount of habitat. However, these constraints are extraordinarily complex, only feasible for small problems, and require preprocessing routines to identify initial solutions. As such, we will not consider them in this tutorial. + +### Linear constraints + +Linear constraints can be used to specify that the prioritizations must meet an arbitrary set of criteria. As such, they can be used to ensure that prioritizations provide adequate coverage of planning units that have facilitate a high level of connectivity. Recall that the `salt_con` data are used to describe connectivity across the study area. Since higher values denote planning units with greater connectivity, we could use linear constraints to ensure that the total sum of connectivity values -- based on this dataset -- meets a particular threshold (e.g. cover at least 30% of the total amount). This would effectively be treating connectivity as an additional feature [similar to @r52]. + +```{r} +# compute threshold for constraints +## here we use a threshold of 30% of the total connectivity values +threshold <- cellStats(salt_con, "sum") * 0.3 + +# print threshold +print(threshold) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +s3 <- p0 %>% + add_linear_constraints( + data = salt_con, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s3), main = c("baseline", "linear constraints"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Although using continuous values has the advantage that the prioritization process can explicitly account for differences in the relative amount of connectivity facilitated by different planning units, the disadvantage is that the prioritization could potentially focus on selecting lots of planning units with low connectivity values. To avoid this result, one strategy is to convert the continuous values into binary values using a threshold limit [similar to @r53]. By applying such a threshold limit, linear constraints can then be used to ensure that the prioritization selects a minimum amount of planning units with high connectivity values (i.e., those with connectivity values that are equal to or greater than the threshold limit). + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r} +# calculate threshold limit +## here we set a threshold limit based on the median +threshold_limit <- quantile(salt_con, probs = 0.5) + +# convert continuous values to binary values +salt_con_binary <- round(salt_con <= threshold_limit) + +# plot binary values +plot(salt_con_binary, main = "salt_con_binary", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +## note that we use the original threshold computed before, +## to ensure the prioritization covers at least 30% of the total amount +## connectivity values +s4 <- p0 %>% + add_linear_constraints( + data = salt_con_binary, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s4), main = c("baseline", "linear constraints (binary)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Another strategy is to clamp the continuous values below a threshold limit are assigned a value of zero [similar to @r54]. This strategy has the advantage that (i) the prioritization won't focus on selecting lots of planning units with low connectivity values to meet the constraint threshold, and (ii) the optimization process can use semi-continuous values to distinguish between places that can facilitate a moderate amount and a high amount of connectivity. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, results = "hide"} +# clamp continuous values using the threshold limit we computed before +salt_con_clamp <- salt_con +salt_con_clamp[Which(salt_con <= threshold_limit)] <- 0 + +# plot clamped values +plot(salt_con_clamp, main = "salt_con_clamp", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +## note that we use the original threshold computed before, +## to ensure the prioritization covers at least 30% of the total amount +## connectivity values +s5 <- p0 %>% + add_linear_constraints( + data = salt_con_clamp, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s5), main = c("baseline", "linear constraints (clamped)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +If we were concerned that the prioritization did not facilitate a high enough level of connectivity, we could increase the `threshold` value or the `threshold_limit` value. For example, let's increase the `threshold_limit` value used to clamp the continuous connectivity values. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, results = "hide"} +# compute threshold limit +threshold_limit2 <- quantile(salt_con, probs = 0.7) + +# clamp continuous values using the threshold limit we computed before +salt_con_clamp2 <- salt_con +salt_con_clamp2[Which(salt_con <= threshold_limit2)] <- 0 + +# plot clamped values +plot(salt_con_clamp2, main = "salt_con_clamp", axes = FALSE) +``` + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added linear constraints and solve it +## note that we use the original threshold computed before, +## to ensure the prioritization covers at least 30% of the total amount +## connectivity values +s6 <- p0 %>% + add_linear_constraints( + data = salt_con_clamp2, threshold = threshold, sense = ">=" + ) %>% + solve() + +# plot solutions +plot( + stack(s0, s6), main = c("baseline", "linear constraints (clamped 2)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Despite the advantages of clamping the connectivity values, we can see that the prioritization has a relatively high level of spatial fragmentation. In fact, all prioritizations generated using the linear constraints can potentially have this issue. This is because linear constraints do not explicitly account for the spatial arrangement of the planning units. As such, we recommend combining the linear constraints approach with another approach [e.g., the boundary penalties approach discussed below; @r53]. + +## Adding penalties + +Now let's explore approaches for promoting connectivity in prioritizations by adding penalties to the baseline problem formulation. These approaches involve penalizing solutions according to exhibit certain [e.g., penalize spatial fragmentation of prioritizations\; @r2]. Unlike constraint-based methods for incorporating connectivity -- if the optimality gap used to generate a prioritization is too high -- they may not necessarily produce prioritizations that exhibit desirable characteristics. + +### Boundary penalties + +Boundary penalties can be added to used to reduce the spatial fragmentation of prioritizations (using the `add_boundary_penalties()` function). Specifically, these penalties update the problem formulation to penalize solutions that have a high total amount of exposed boundary length [@r3]. Since boundary data often have large values which can degrade solver performance and result in excessive run times (see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html) for details), we will first precompute rescale the boundry data. + +```{r} +# precompute the boundary data +salt_boundary_data <- boundary_matrix(salt_pu) + +# rescale boundary data +salt_boundary_data@x <- rescale(salt_boundary_data@x, to = c(0.01, 100)) +``` + +Next, let's generate a prioritization using boundary penalties. To specify the relative importance reducing spatial fragmentation -- compared with the primary objective of a problem (e.g. minimizing cost) -- we need to a value for the `penalty` parameter is used. Setting a higher value for `penalty` indicates that it is more important to avoid highly fragmented solutions. Let's generate a prioritization with a `penalty` value of 0.001. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added boundary penalties +s7 <- p0 %>% + add_boundary_penalties(penalty = 0.001, data = salt_boundary_data) %>% + solve() + +# plot solutions +plot( + stack(s0, s7), main = c("baseline", "boundary penalties (0.001)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +We can see that the resulting prioritization is still relatively fragmented, so let's try generating another prioritization with a higher `penalty` value. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with increased boundary penalties +s8 <- p0 %>% + add_boundary_penalties(penalty = 10, data = salt_boundary_data) %>% + solve() + +# plot solutions +plot( + stack(s0, s8), main = c("baseline", "boundary penalties (10)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Although the prioritization is now less fragmented, it has also selected a greater number of planning units. Let's calculate the cost of the prioritizations to see how they vary in overall cost. + +```{r} +# calculate cost of baseline prioritization +eval_cost_summary(p0, s0) + +# calculate cost of prioritization with low boundary penalties (i.e., 0.001) +eval_cost_summary(p0, s7) + +# calculate cost of prioritization high low boundary penalties (i.e., 0.1) +eval_cost_summary(p0, s8) +``` + +We can see that the cost of the prioritizations increase with when we use higher `penalty` values. This is because there is a trade-off between the cost of a prioritization and the level of spatial fragmentation. Although it can be challenging to find the best balance, there are qualitative and quantitative methods available to help navigate such trade-offs. Please see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html) for a details on these methods. + +### Connectivity penalties + +Connectivity penalties can be used to promote connectivity in prioritizations (using the `add_connectivity_penalties()` function). These penalties use connectivity scores to parametrize the strength of connectivity between pairs of planning units [@r38]. Thus higher scores denote a greater level of connectivity between different planning units. For example, previous studies have parametrized connectivity scores using habitat quality, environmental, and river flow data [e.g. @r59; @r63; @r43]. Although there are many approaches to calculate connectivity scores, one approach involves using conductance data -- data that describe how much each planning unit facilitates movement (opposite of landscape resistance data) -- and calculating scores for each pair of planning units by averaging their conductance values (implemented using the `connectivity_matrix()` function). + +Let's compute connectivity scores by treating the `salt_con` object as conductance data. This means that we assume that neighboring planning units with higher values in the `salt_con` object are capable of facilitating a greater amount of connectivity. **Note that the data used to compute connectivity scores must conform to the same spatial properties as the planning unit data (e.g., resolution, spatial extent, coordinate reference system).** Also, although we are using raster data here, these scores can also be computed for vector data too (e.g., `sf::st_sf()` objects). Similar to the boundary data, we will also rescale the connectivity scores to avoid numerical issues during optimization. + +```{r} +# compute connectivity scores +salt_con_scores <- connectivity_matrix(salt_pu, salt_con) + +# rescale scores +salt_con_scores@x <- rescale(salt_con_scores@x, to = c(0.01, 100)) +``` + +After computing the connectivity scores, we can use them to generate prioritizations using connectivity penalties. Similar to the boundary penalties, we use the `penalty` parameter to specify the relative importance of promoting connectivity relative to the primary objective of a problem (i.e., minimizing overall cost). Let's generate a prioritization with a `penalty` value of 0.001. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with added connectivity penalties +s9 <- p0 %>% + add_connectivity_penalties(penalty = 0.001, data = salt_con_scores) %>% + solve() + +# plot solutions +plot( + stack(s0, s9), main = c("baseline", "connectivity penalties (0.001)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +Now let's try generating another prioritization with a higher `penalty` value. + +```{r, include = FALSE} +par(mar = c(1.1, 4.1, 4.1, 2.1)) +``` + +```{r, fig.width = 7.0, results = "hide"} +# create problem with increased connectivity penalties +s10 <- p0 %>% + add_connectivity_penalties(penalty = 0.002, data = salt_con_scores) %>% + solve() + +# plot solutions +plot( + stack(s0, s10), main = c("baseline", "connectivity penalties (0.002)"), + axes = FALSE, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen") +) +``` + +We can see that increasing the `penalty` parameter causes the prioritizations to select planning units in regions with greater connectivity values (i.e., per the `salt_con` object). As discussed with the boundary penalties, increasing the `penalty` value tells the optimization process to focus more on promoting connectivity---meaning that it won't focus as much on the primary objective (i.e., because the primary objective is to minimize overall costs). For details on calibrating these trade-offs please see the [_Calibrating trade-offs tutorial_](calibrating_trade-offs-tutorial.html). **Note that you will need to the use `eval_connectivity_summary()` function -- instead of the `eval_boundary_summary()` function -- when adapting the tutorial code for connectivity penalties.** + +## Conclusion + +Hopefully, this tutorial has provided a helpful introduction for incorporating connectivity into prioritizations. Broadly speaking, we recommend using the boundary penalties or the connectivity penalties to ensure that prioritizations explicitly account for the spatial configuration of selected planning units. Additionally, though not fully explored here, the connectivity penalties are a very flexible approach for promoting connectivity. For instance, in addition to parameterizing pair-wise connectivity scores for neighboring planning units, they can also be used to parametrize pair-wise connectivity scores between more distant planning units. Thus connectivity penalties could be used to parametrize connectivity across both small scales and large spatial scales (e.g., using a scaling procedure wherein connectivity scores between pairs of planning units decline with the distance between them). + +## References diff --git a/inst/doc/gurobi_installation.Rmd b/vignettes/gurobi_installation_guide.Rmd similarity index 88% rename from inst/doc/gurobi_installation.Rmd rename to vignettes/gurobi_installation_guide.Rmd index 016dd7dfd..e36fe4832 100644 --- a/inst/doc/gurobi_installation.Rmd +++ b/vignettes/gurobi_installation_guide.Rmd @@ -1,5 +1,5 @@ --- -title: "Gurobi Installation Guide" +title: "Gurobi installation guide" author: "Richard Schuster" date: "`r Sys.Date()`" output: @@ -10,7 +10,7 @@ output: fontsize: 11pt documentclass: article vignette: > - %\VignetteIndexEntry{Gurobi Installation Guide} + %\VignetteIndexEntry{Gurobi installation guide} %\VignetteEngine{knitr::rmarkdown_notangle} --- @@ -28,7 +28,7 @@ devtools::load_all() ## Introduction -_Gurobi_ is the most powerful and fastest solver that the _prioritizr R_ package can use to solve conservation planning problems. This vignette will walk you through the process of setting up _Gurobi_ on your computer so that you can use it to solve conservation planning problems. If you encounter any problems while following the instructions below, check out the [official _Gurobi_ documentation](https://www.gurobi.com/documentation/). +_Gurobi_ is the most powerful and fastest solver that the _prioritizr R_ package can use to solve conservation planning problems (see the [_Solver benchmarks_](solver_benchmarks.html) vignette for further details). This guide will walk you through the process of setting up _Gurobi_ on your computer so that it can be used to solve conservation planning problems. If you encounter any problems while following the instructions below, please refer to the [official _Gurobi_ documentation](https://www.gurobi.com/documentation/). ## Obtaining a license @@ -48,7 +48,7 @@ After obtaining a license, you will need to download the _Gurobi_ installer to y ## Software installation -The process for installing the _Gurobi_ software depends on the operating system on your computer. Fortunately, _Gurobi_ provide platform-specific ["Quick Start Guides"](https://www.gurobi.com/documentation/) for [Windows](https://www.gurobi.com/documentation/9.0/quickstart_windows/software_installation_guid.html#section:Installation), [MacOS](https://www.gurobi.com/documentation/9.0/quickstart_mac/software_installation_guid.html), and [Linux](https://www.gurobi.com/documentation/9.0/quickstart_linux/software_installation_guid.html) systems that should help with this. Briefly, on Windows systems, you just need to double-click on the _Gurobi_ installer, follow the prompts, and the installer will automatically handle everything for you. On Linux and MacOS systems, you will need to manually extract the downloaded file's contents to a folder, move the extracted contents to a suitable location (typically _/opt/gurobi_), and update your system's variables so that it knows where to find _Gurobi_ (i.e. the `PATH` variable). +The process for installing the _Gurobi_ software depends on the operating system on your computer. Fortunately, _Gurobi_ provide platform-specific ["Quick Start Guides"](https://www.gurobi.com/documentation/) for [Windows](https://www.gurobi.com/documentation/9.0/quickstart_windows/software_installation_guid.html#section:Installation), [MacOS](https://www.gurobi.com/documentation/9.0/quickstart_mac/software_installation_guid.html), and [Linux](https://www.gurobi.com/documentation/9.0/quickstart_linux/software_installation_guid.html) systems that should help with this. Briefly, on Windows systems, you just need to double-click on the _Gurobi_ installer, follow the prompts, and the installer will automatically handle everything for you. On Linux and MacOS systems, you will need to manually extract the downloaded file's contents to a folder, move the extracted contents to a suitable location (typically _/opt/gurobi_), and update your system's variables so that it knows where to find _Gurobi_ (i.e., the `PATH` variable). Additionally, if you are using [_RStudio_](https://www.rstudio.com/products/rstudio/) on a Linux system, you might need to add the following text to a Rstudio configuration file (located at `/etc/rstudio/rserver.conf`). @@ -64,7 +64,7 @@ Now we will activate the _Gurobi_ software using the license you obtained earlie
    ![](figures/cmd-windows-success.png){ width=75% }

    -Next, we will now check that the license has been successfully activated. To achieve this, we will try running _Gurobi_ directly from the command line. Note that the following commands assume you are using version 8.0.0 of _Gurobi_, and so you will need to modify the command if you are using a more recent version (e.g. if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). +Next, we will now check that the license has been successfully activated. To achieve this, we will try running _Gurobi_ directly from the command line. Note that the following commands assume you are using version 8.0.0 of _Gurobi_, and so you will need to modify the command if you are using a more recent version (e.g., if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). On Windows systems, users can type in the following system command to check their license activation. @@ -86,7 +86,7 @@ After activating the license, you now need to install the _gurobi R_ package. Th ## _R_ package installation -Now we will install the _gurobi R_ package. This package is not available on the Comprehensive R Archive Network and is instead distributed with the _Gurobi_ software suite. Specifically, the _gurobi_ _R_ package should be located within the folder where you installed the _Gurobi_ software suite. We will install the _gurobi_ _R_ package by running the following _R_ code within your _R_ session. Note that the following code assumes that you are using version 8.0.0 of _Gurobi_, and so you will need to modify the code if you are using a more recent version (e.g. if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). +Now we will install the _gurobi R_ package. This package is not available on the Comprehensive R Archive Network and is instead distributed with the _Gurobi_ software suite. Specifically, the _gurobi_ _R_ package should be located within the folder where you installed the _Gurobi_ software suite. We will install the _gurobi_ _R_ package by running the following _R_ code within your _R_ session. Note that the following code assumes that you are using version 8.0.0 of _Gurobi_, and so you will need to modify the code if you are using a more recent version (e.g., if using version 9.1.2, then use `gurobi912` instead of `gurobi800` below). Assuming you installed _Gurobi_ in the default location, Windows users can install _gurobi_ _R_ package using the following code. @@ -102,7 +102,7 @@ install.packages(file.path(Sys.getenv("GUROBI_HOME"), repos = NULL) ``` -Next, you will need to install the _slam R_ package because the _gurobi_ _R_ package needs this package to work. Users of all platforms (i.e. Windows, Linux, and MacOS) can install the package using the following _R_ code. +Next, you will need to install the _slam R_ package because the _gurobi_ _R_ package needs this package to work. Users of all platforms (i.e., Windows, Linux, and MacOS) can install the package using the following _R_ code. ```{r, eval = FALSE} install.packages("slam", repos = "https://cloud.r-project.org") @@ -136,7 +136,7 @@ If you see the outputs for `result$objval` and `result$x` and you don't see any ## Solving a _prioritzr_ problem with _Gurobi_ -If you successfully installed the _Gurobi_ software suite and the _gurobi_ _R_ package, you can now try solving conservation planning problems using the _prioritzr_ _R_ package. Although the _prioritizr_ _R_ package should automatically detect that _Gurobi_ has been installed, you can use the function `add_gurobi_solver` to manually specify that _Gurobi_ should be used to solve problems. This function is also useful because you can use it to customize the optimization process (e.g. specify the desired optimality gap or set a limit on how much time should be spent searching for a solution). +If you successfully installed the _Gurobi_ software suite and the _gurobi_ _R_ package, you can now try solving conservation planning problems using the _prioritzr_ _R_ package. Although the _prioritizr_ _R_ package should automatically detect that _Gurobi_ has been installed, you can use the function `add_gurobi_solver()` to manually specify that _Gurobi_ should be used to solve problems. This function is also useful because you can use it to customize the optimization process (e.g., specify the desired optimality gap or set a limit on how much time should be spent searching for a solution). Finally, to check that everything has been installed correctly, we will use the _Gurobi_ software suite to solve a reserve selection problem created using the _prioritzr_ _R_ package. diff --git a/inst/doc/zones.Rmd b/vignettes/management_zones_tutorial.Rmd similarity index 70% rename from inst/doc/zones.Rmd rename to vignettes/management_zones_tutorial.Rmd index b28c8541d..48efd1835 100644 --- a/inst/doc/zones.Rmd +++ b/vignettes/management_zones_tutorial.Rmd @@ -1,5 +1,5 @@ --- -title: "Management Zones" +title: "Management zones tutorial" output: rmarkdown::html_vignette: toc: true @@ -10,7 +10,7 @@ documentclass: article bibliography: references.bib csl: reference-style.csl vignette: > - %\VignetteIndexEntry{Management Zones} + %\VignetteIndexEntry{Management zones tutorial} %\VignetteEngine{knitr::rmarkdown_notangle} --- @@ -23,21 +23,22 @@ knitr::opts_chunk$set(fig.align = "center", eval = !is_check, ## Introduction -One of the main aims in conservation planning is to identify the most cost-effective set of areas to manage biodiversity [@r4]. To achieve this, prioritizations are generally created to identify areas for expanding protected area systems. However, many real-world conservation problems do not simply involve deciding if an area should be protected or not [e.g. @r5; @r18]. Instead, many real-world problems involve a range of different management categories and the goal is to determine which areas should be allocated to which management category. For example, a manager might have a range of different methods (e.g. baiting or trapping at various intensities) for controlling invasive pests in a set of different areas [e.g. @r25]. They would need a prioritization that shows which control methods should be implemented in which areas. In this particular case, a binary prioritization showing which areas contain the most biodiversity is simply not helpful. Furthermore, many real-world problems require decisions that meet multiple, and sometimes conflicting, objectives from different stakeholders. For example, a manager might need to implement a set of no-take and partial-take areas to prevent overfishing, but also ensure that there still remain plenty of areas for fishing activities [e.g. @r26; @r27]. Popularized by _Marxan with Zones_ [@r2], this concept has become known as "zones" and is becoming increasingly important in conservation planning. +One of the main aims in conservation planning is to identify the most cost-effective set of areas to manage biodiversity [@r4]. To achieve this, prioritizations are generally created to identify areas for expanding protected area systems. However, many real-world conservation problems do not simply involve deciding if an area should be protected or not [e.g., @r5; @r18]. Instead, many problems involve a range of different management categories and the goal is to determine which areas should be allocated to which management category. For example, a manager might have a range of different methods (e.g., baiting or trapping at various intensities) for controlling invasive pests in a set of different areas [e.g., @r25]. They would need a prioritization that shows which control methods should be implemented in which areas. In this particular case, a binary prioritization showing which areas contain the most biodiversity is simply not helpful. Furthermore, many real-world problems require decisions that meet multiple, and sometimes conflicting, objectives from different stakeholders. For example, a manager might need to implement a set of no-take and partial-take areas to prevent overfishing, but also ensure that there still remain plenty of areas for fishing activities [e.g., @r26; @r27]. Popularized by the _Marxan with Zones_ decision support tool [@r2], this concept has become known as "zones" and is becoming increasingly important in conservation planning. -The aim of this vignette is to showcase the zones functionality of the _prioritizr R_ package. It will assume a certain level of familiarity with conservation planning terminology and the _prioritizr R_ package. We recommend reading the [_prioritizr_ vignette](prioritizr.html) first if you don't have much experience in either of these topics. +The aim of this tutorial is to showcase the zones functionality of the _prioritizr R_ package. It will assume a certain level of familiarity with conservation planning terminology and the package. If you don't have much experience in either of these topics, we recommend first reading the [_Package overview_](package_overview.html) vignette. ## Usage - ### Simple minimum set problem -In the _prioritizr R_ package, all conservation planning problems---including those which contain multiple management zones or actions---are initialized using the `problem` function. To refresh our memory on how we can construct problems using the _prioritizr R_ package, let us quickly construct a simple conservation planning problem. This problem will use the simulated built-in planning unit and feature data distributed with the package. It will have a minimum set objective, targets which require that solution secure to 10 % of the habitat in the study area for each feature, and binary decision variables indicating that planning units are selected or not selected for protection. +In the _prioritizr R_ package, all conservation planning problems -- including those which contain multiple management zones or actions -- are initialized using the `problem` function. To refresh our memory on how we can construct problems, let us quickly construct a simple conservation planning problem. This problem will use the simulated built-in planning unit and feature data distributed with the package. It will have a minimum set objective, targets which require that solution secure to 10 % of the habitat in the study area for each feature, and binary decision variables indicating that planning units are selected or not selected for protection. -```{r} +```{r, results = "hide"} # load prioritizr package library(prioritizr) +``` +```{r} # load data data(sim_pu_raster, sim_features) @@ -66,7 +67,7 @@ plot(s1, main = "solution", axes = FALSE, box = FALSE) ### Adding management zones -Now let us imagine that instead of having a single management zone (e.g. protected area), we have two management zones. Similar to the example above, we require a solution that secures 10 % of the habitat in the study area for each feature in the first management zone. But we also require a solution that secures 5 % of the habitat in the study area for each feature in the second management zone. Each planning unit must be allocated to either zone or not selected for management at all. In this example, each planning unit costs the same when it is allocated to either of the two zones. We can formulate and solve this problem using the following code. +Now let us imagine that instead of having a single management zone (e.g., protected area), we have two management zones. Similar to the example above, we require a solution that secures 10 % of the habitat in the study area for each feature in the first management zone. But we also require a solution that secures 5 % of the habitat in the study area for each feature in the second management zone. Each planning unit must be allocated to either zone or not selected for management at all. In this example, each planning unit costs the same when it is allocated to either of the two zones. We can formulate and solve this problem using the following code. ```{r} # create a matrix with the targets @@ -125,7 +126,7 @@ plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE) ### Multiple zones with varying costs -Real-world problems often have different costs for managing planning units under different zones. These problems also tend to have different expected amounts of each feature when planning units are managed differently. So let us consider a slightly more complex example. Similar to before we will have two management zones. But this time, the cost of managing each planning unit is different depending on which management zone it is assigned to in the solution. Furthermore, when we assign a planning unit to the second zone, we only expect to end up with half of the habitat we would get if we managed the unit in the first zone (e.g. because the second zone is a partial-take zone and the first zone is a no-take zone). We will use the same target data as in the previous example. +Real-world problems often have different costs for managing planning units under different zones. These problems also tend to have different expected amounts of each feature when planning units are managed differently. So let us consider a slightly more complex example. Similar to before we will have two management zones. But this time, the cost of managing each planning unit is different depending on which management zone it is assigned to in the solution. Furthermore, when we assign a planning unit to the second zone, we only expect to end up with half of the habitat we would get if we managed the unit in the first zone (e.g., because the second zone is a partial-take zone and the first zone is a no-take zone). We will use the same target data as in the previous example. ```{r, fig.width = 4.0} # create new planning unit and cost data @@ -159,7 +160,7 @@ plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE) ### Multiple zones with complex targets -So far, we have dealt with problems where each feature has a target that pertains to a single zone. But sometimes we have targets that pertain to multiple zones. For example, what if we were in charge of managing pest control in a set of areas and we had three different pest control methods we could implement in any given planning unit. We could (1) set up a few traps in a given planning unit and make 10 % of the habitat in the unit pest-free, (2) set up a lot of traps and make 50 % of the habitat in the unit pest-free, or (3) drop baits over a given planning unit and make 80 % of the planning unit pest-free. Each of these different actions has a different cost, with a few low intensity trapping costing $100 per planning unit, a high intensity trapping costing $300, and baiting costing $200 (please note these costs aren't meant to be realistic). After defining our management actions and costs, we require a solution that will yield 8 units of pest free habitat per feature. It's important to note that unlike the previous examples, here we don't have targets for each feature in each zone, but rather our targets are for each feature and across multiple zones. In other words, we don't really care which management actions we implement, we just want the set of actions that will meet our targets for minimum expenditure. We can formulate and solve this problem using the following code. +So far, we have dealt with problems where each feature has a target that pertains to a single zone. But sometimes we have targets that pertain to multiple zones. For example, what if we were in charge of managing pest control in a set of areas and we had three different pest control methods we could implement in any given planning unit. We could (i) set up a few traps in a given planning unit and make 10 % of the habitat in the unit pest-free, (ii) set up a lot of traps and make 50 % of the habitat in the unit pest-free, or (iii) drop baits over a given planning unit and make 80 % of the planning unit pest-free. Each of these different actions has a different cost, with a few low intensity trapping costing $100 per planning unit, a high intensity trapping costing $300, and baiting costing $200 (please note these costs aren't meant to be realistic). After defining our management actions and costs, we require a solution that will yield 8 units of pest free habitat per feature. It's important to note that unlike the previous examples, here we don't have targets for each feature in each zone, but rather our targets are for each feature and across multiple zones. In other words, we don't really care which management actions we implement, we just want the set of actions that will meet our targets for minimum expenditure. We can formulate and solve this problem using the following code. ```{r, fig.width = 4.5, fig.height = 3} # create planning unit data with costs @@ -318,6 +319,6 @@ plot(category_layer(s7), main = "solution", axes = FALSE, box = FALSE) ## Conclusion -Hopefully, this vignette has provided an informative introduction to building and solving problems with multiple zones using the _prioritizr R_ package. Although we have examined only a few different functions here, almost every single function for modifying conservation planning problems is compatible with problems that contain zones. It's worth noting that working with multiple zones is a lot trickier than working with a single zone, so we would recommend playing around with the code in the Examples sections of the help pages to get a feel for what different functions do when they are applied to problems with multiple zones. +Hopefully, this vignette has provided an informative introduction to building and solving problems with multiple zones. Although we have examined only a few different functions here, almost every single function for modifying conservation planning problems is compatible with problems that contain zones. It's worth noting that working with multiple zones is a lot trickier than working with a single zone, so we would recommend playing around with the code in the _Examples_ sections of the package documentation to help understand how functions work when applied to multiple zones. ## References diff --git a/vignettes/package_overview.Rmd b/vignettes/package_overview.Rmd new file mode 100644 index 000000000..eb1d5ddf3 --- /dev/null +++ b/vignettes/package_overview.Rmd @@ -0,0 +1,905 @@ +--- +title: "Package overview" +output: + rmarkdown::html_vignette: + toc: true + fig_caption: true + self_contained: yes +fontsize: 11pt +documentclass: article +bibliography: references.bib +csl: reference-style.csl +vignette: > + %\VignetteIndexEntry{Package overview} + %\VignetteEngine{knitr::rmarkdown_notangle} +--- + +```{r, include = FALSE} +h = 3.5 +w = 3.5 +is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", + "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) +knitr::opts_chunk$set(fig.align = "center", eval = !is_check, + root.dir = normalizePath("../..")) +``` + +## Summary + +The _prioritizr R_ package uses integer linear programming (ILP) techniques to provide a flexible interface for building and solving conservation planning problems [@r11; @r16]. It supports a broad range of objectives, constraints, and penalties that can be used to custom-tailor conservation planning problems to the specific needs of a conservation planning exercise. Once built, conservation planning problems can be solved using a variety of commercial and open-source exact algorithm solvers. In contrast to the algorithms conventionally used to solve conservation problems, such as heuristics or simulated annealing [@r3], the exact algorithms used here are guaranteed to find optimal solutions. Furthermore, conservation problems can be constructed to optimize the spatial allocation of different management zone (or actions), meaning that conservation practitioners can identify solutions that benefit multiple stakeholders. Finally, this package has the functionality to read input data formatted for the _Marxan_ conservation planning program [@r3], and find much cheaper solutions in a much shorter period of time than _Marxan_ [@r1]. + +## Introduction + +Systematic conservation planning is a rigorous, repeatable, and structured approach to designing new protected areas that efficiently meet conservation objectives [@r4]. Historically, conservation decision-making has often evaluated parcels opportunistically as they became available for purchase, donation, or under threat. Although purchasing such areas may improve the _status quo_, such decisions may not substantially enhance the long-term persistence of target species or communities. Faced with this realization, conservation planners began using decision support tools to help simulate alternative reserve designs over a range of different biodiversity and management goals and, ultimately, guide protected area acquisitions and management actions. Due to the systematic, evidence-based nature of these tools, conservation prioritization can help contribute to a transparent, inclusive, and more defensible decision making process. + +A conservation planning exercise typically starts by defining a study area. This study area should encompass all the areas relevant to the decision maker or the hypothesis being tested. The extent of a study area could encompass a few important localities [e.g., @r18], a single state [e.g., @r17], an entire country [@r19], or the entire planet [@r20]. Next, the study area is divided into a set of discrete areas termed _planning units_. Each planning unit represents a discrete locality in the study area that can be managed independently of other areas. The general idea is that some combination of the planning units can be selected for conservation actions (e.g., protected area establishment, habitat restoration). Planning units are often created as square or hexagon cells that are sized according to the scale of the conservation actions, and the resolution of the data that underpin the planning exercise [but see @r5]. + +Cost data (or a surrogate thereof) are needed to inform the prioritization process. Specifically, these cost data describe the relative expenditure associated with managing each planning unit for conservation. For example, if the goal of the conservation planning exercise is to identify priority areas for expanding a local protected area system, then the cost data could represent the physical cost of purchasing the land. Alternatively, if such data are not available, then surrogate data could are often used instead (e.g., human population density, opportunity cost of foregone commercial activities, or planning unit size). + +Conservation planning exercises also require data on the biodiversity elements that are of conservation interest (termed _conservation features_). These features could be species (e.g., _Neofelis nebulosa_, the Clouded Leopard), populations, or habitats (e.g., mangroves or cloud forest). After identifying the set of relevant conservation features for a conservation planning exercise, spatially explicit data need to be obtained for each and every feature to describe their spatial distribution (e.g., habitat suitability data, probability of occurrence data, presence/absence data). This is important to ensure that conservation features are adequately covered (represented) by prioritizations. After assembling all the data, the next step is to define the conservation planning problem. + +The _prioritizr R_ package is designed to help you build and solve conservation planning problems. Specifically, prioritizations are generated by using formulating a mathematical optimization problem and then solving it to generate a solution. These mathematical optimization problems are formulated using the planning unit data, cost data, and feature data, and with information related to the overarching aim of the prioritization process. In general, the goal of an optimization problem is to minimize (or maximize) an _objective function_ that is calculated using a set of _decision variables_, subject to a series of constraints to ensure that solution exhibits specific properties. The objective function describes the quantity which we are trying to minimize (e.g., cost of the solution) or maximize (e.g., number of features conserved). The decision variables describe the entities that we can control, and indicate which planning units are selected for conservation management and which of those are not. The constraints can be thought of as rules that the need decision variables need to follow. For example, a commonly used constraint is specifying that the solution must not exceed a certain budget. + +A wide variety of approaches have been developed for solving optimization problems. Reserve design problems are frequently solved using simulated annealing [@r6] or heuristics [@r8; @r7]. These methods are conceptually simple and can be applied to a wide variety of optimization problems. However, they do not scale well for large or complex problems [@r1]. Additionally, these methods cannot tell you how close any given solution is to the optimal solution. The package uses exact algorithms to efficiently solve conservation planning problems to within a pre-specified a optimality gap. In other words, you can specify that you need the optimal solution (i.e., a gap of 0%) and the algorithms will, given enough time, find a solution that meets this criteria. In the past, exact algorithms have been too slow for conservation planning exercises [@r9]. However, improvements over the last decade mean that they are now much faster [@r23; @r1]. + +In this package, optimization problems are expressed using _integer linear programming_ (ILP) so that they can be solved using (linear) exact algorithm solvers. The general form of an integer programming problem can be expressed in matrix notation using the following equation. + +$$\text{Minimize} \space \boldsymbol{c}^\text{T} \boldsymbol{x} \space +\space \text{subject to} \space A\boldsymbol{x} +\space \Box \space \boldsymbol{b}$$ + +Here, where $x$ is a vector of decision variables, $c$ and $b$ are vectors of known coefficients, and $A$ is the constraint matrix. The final term specifies a series of structural constants and the $\Box$ symbol is used to indicate that the relational operators for the constraints can be either $\geq$, $=$, or $\leq$. In the context of a conservation planning problem, $c$ could be used to represent the planning unit costs, $A$ could be used to store the data showing the presence / absence (or amount) of each feature in each planning unit, $b$ could be used to represent minimum amount of habitat required for each species in the solution, the $\Box$ could be set to $\geq$ symbols to indicate that the total amount of each feature in the solution must exceed the quantities in $b$. But there are many other ways of formulating the reserve selection problem [@r11]. + +## A grammar for conservation planning + +The _prioritizr R_ package uses a grammar to describe elements of conservation planning. This means that functions are organized into verbs that relate to specific concepts. For example, all of the functions used to specify the primary objective for optimization end with the `_objective` suffix (e.g., `add_min_set_objective()` and `add_min_shortfall_objective()`). By combining multiple functions together, they can be used to formulate a complete conservation planning problem. Specifically, the verbs for formulating problems are described below. + +* Create a new conservation planning [problem](https://prioritizr.net/reference/problem.html) by specifying the planning units, features, and management zones of conservation interest. +* Add a primary [objective](https://prioritizr.net/reference/objectives.html) to a conservation planning problem (e.g., minimize overall cost). +* Add [penalties](https://prioritizr.net/reference/penalties.html) to a conservation planning problem to penalize solutions according to specific metric (e.g., connectivity). +* Add [targets](https://prioritizr.net/reference/targets.html) to a conservation planning problem to specify how much of each feature should ideally be represented solutions. +* Add [constraints](https://prioritizr.net/reference/constraints.html) to a conservation planning problem to ensure that solutions exhibit specific properties (e.g., select specific planning units for protection). +* Add [decisions](https://prioritizr.net/reference/decisions.html) to a conservation planning problem to specify the nature of the decisions in the problem (e.g., binary decisions indicate the planning units should be selected or not selected for management). +* Add a [portfolio](https://prioritizr.net/reference/portfolios.html) to a conservation planning problem to specify a methodological approach for generating multiple solutions (e.g. generate multiple solutions by finding 100 solutions within 10% of optimality). +* Add a [solver](https://prioritizr.net/reference/solvers.html) to a conservation problem to specify the optimization software (e.g. Gurobi) for generating solutions and customize the optimization settings (e.g. generate an optimal solution). + +After building a conservation planning problem, it can be solved to generate a prioritization (using the `solve()` function). There are also verbs available to help evaluate and interpret solutions. These verbs are described below. + +* Evaluate performance by computing [summary statistics](https://prioritizr.net/reference/summaries.html) (e.g. overall cost, feature representation, or total boundary length). +* Evaluate the relative [importance](https://prioritizr.net/reference/importance.html) of planning units selected by a solution (e.g. based on irreplaceability). + +## Workflow + +The general workflow when using the _prioritizr R_ package starts with creating a new conservation planning `problem()` object using data. Specifically, the `problem()` object should be constructed using data that specify the planning units, biodiversity features, management zones (if applicable), and costs. After creating a new `problem()` object, it can be customized---by adding objectives, penalties, constraints, and other information---to build a precise representation of the conservation planning problem required, and then solved to obtain a solutions. + +All conservation planning problems require an objective. An objective specifies the property which is used to compare different feasible solutions. Simply put, the objective is the property of the solution which should be maximized or minimized during the optimization process. For instance, with the minimum set objective (specified using `add_min_set_objective()`), we are seeking to minimize the cost of the solution (similar to _Marxan_). On the other hand, with the minimum shortfall objective (specified using `add_min_shortfall_objective()`), we are seeking to minimize the average target shortfall for all features represented in the solution, subject to a budget. + +Many objectives require representation targets (e.g., the minimum set objective). These targets are a specialized set of constraints that relate to the total quantity of each feature secured in the solution (e.g., amount of suitable habitat or number of individuals). In the case of the minimum set objective ( `add_min_set_objective()`), they are used to ensure that solutions secure a sufficient quantity of each feature, and in other objectives, such as the maximum features objective ( `add_max_features_objective()`) they are used to assess whether a feature has been adequately conserved by a candidate solution. Targets can be expressed numerically as the total amount required for a given feature (using `add_absolute_targets()`), or as a proportion of the total amount found in the planning units (using `add_relative_targets()`). Note that not all objectives require targets, and a warning will be thrown if an attempt is made to add targets to a problem with an objective that does not use them. + +Constraints and penalties can be added to a conservation planning problem to ensure that solutions exhibit a specific property or penalize solutions which don't exhibit a specific property (respectively). The difference between constraints and penalties, strictly speaking, is constraints are used to rule out potential solutions that don't exhibit a specific property. For instance, constraints can be used to ensure that specific planning units are selected in the solution for prioritization (using `add_locked_in_constraints()`) or not selected in the solution for prioritization (using `add_locked_out_constraints()`). On the other hand, penalties are combined with the objective of a problem, with a penalty factor, and the overall objective of the problem then becomes to minimize (or maximize) the primary objective function and the penalty function. For example, penalties can be added to a problem to penalize solutions that are excessively fragmented (using `add_boundary_penalties()`). These penalties have a `penalty` argument that specifies the relative importance of having spatially clustered solutions. When the argument to `penalty` is high, then solutions which are less fragmented are valued more highly -- even if they cost more -- and when the argument to `penalty` is low, then the solutions which are more fragmented are valued less highly. + +After building a conservation problem, it can then be solved to obtain a solution (or portfolio of solutions if desired). The solution is returned in the same format as the planning unit data used to construct the problem. For instance, this means that if raster data was used to initialize the problem, then the solution will also be output in raster format. This can be very helpful when it comes to interpreting and visualizing solutions because it means that the solution data does not first have to be merged with spatial data before they can be plotted on a map. + +## Usage + +Here we will provide an introduction to using the _prioritizr R_ package to build and solve a conservation planning problem. Please note that we will not discuss conservation planning with multiple zones in this vignette, for more information on working with multiple management zones please see the [zones vignette](zones.html). + +First, we will load the _prioritizr_ package. + +```{r, results = "hide", message = FALSE} +# load package +library(prioritizr) + +# set default options for printing tabular data +options(tibble.width = Inf) +``` + +### Data + +Now we will load some built-in data sets that are distributed with the _prioritizr R_ package. This package contains several different planning unit data sets. To provide a comprehensive overview of the different ways that we can initialize a conservation planning problem, we will load each of them. + +First, we will load the raster planning unit data (`sim_pu_raster`). Here, the planning units are represented as a raster (i.e., a `RasterLayer` object) and each pixel corresponds to the spatial extent of each panning unit. The pixel values correspond to the acquisition costs of each planning unit. + +```{r} +# load raster planning unit data +data(sim_pu_raster) + +# print description of the data +print(sim_pu_raster) + +# plot the data +plot(sim_pu_raster) +``` + +Secondly, we will load one of the spatial vector planning unit data sets (`sim_pu_polygons`). Here, each polygon (i.e., feature using ArcGIS terminology) corresponds to a different planning unit. This data set has an attribute table that contains additional information about each polygon. Namely, the `cost` field (column) in the attribute table contains the acquisition cost for each planning unit. + +```{r} +# load polygon planning unit data +data(sim_pu_polygons) + +# print first six rows of attribute table +head(sim_pu_polygons@data) + +# plot the planning units +spplot(sim_pu_polygons, zcol = "cost") +``` + +Thirdly, we will load some planning unit data stored in tabular format (i.e., `data.frame` format). For those familiar with _Marxan_ or dealing with very large conservation planning problems (> 10 million planning units), it may be useful to work with data in this format because it does not contain any spatial information which will reduce computational burden. When using tabular data to initialize conservation planning problems, the data must follow the conventions used by _Marxan_. Specifically, each row in the planning unit table must correspond to a different planning unit. The table must also have an "id" column to provide a unique integer identifier for each planning unit, and it must also have a column that indicates the cost of each planning unit. For more information, please see the [official _Marxan_ documentation](https://marxansolutions.org/). + +```{r} +# specify file path for planning unit data +pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr") + +# load in the tabular planning unit data +# note that we use the data.table::fread function, as opposed to the read.csv +# function, because it is much faster +pu_dat <- data.table::fread(pu_path, data.table = FALSE) + +# preview first six rows of the tabular planning unit data +# note that it has some extra columns other than id and cost as per the +# Marxan format +head(pu_dat) +``` + +Finally, we will load data showing the spatial distribution of the conservation features. Our conservation features (`sim_features`) are represented as a stack of raster objects (i.e., a `RasterStack` object) where each layer corresponds to a different feature (e.g., a multi-band GeoTIFF where each band corresponds to a different feature). The pixel values in each layer correspond to the amount of suitable habitat available in a given planning unit. Note that our planning unit raster layer and our conservation feature stack have exactly the same spatial properties (i.e., resolution, extent, coordinate reference system) so their pixels line up perfectly. + +```{r, fig.width = 4, fig.height = 3} +# load feature data +data(sim_features) + +# plot the distribution of suitable habitat for each feature +plot(sim_features, main = paste("Feature", seq_len(nlayers(sim_features))), + nr = 2, box = FALSE, axes = FALSE) +``` + +### Initialize a problem + +After having loaded our planning unit and feature data, we will now try initializing the some conservation planning problems. There are a lot of different ways to initialize a conservation planning problem, so here we will just showcase a few of the more commonly used methods. For an exhaustive description of all the ways you can initialize a conservation problem, see the help file for the `problem()` function. First off, we will initialize a conservation planning problem using the raster data. + +```{r} +# create problem +p1 <- problem(sim_pu_raster, sim_features) + +# print problem +print(p1) + +# print number of planning units +number_of_planning_units(p1) + +# print number of features +number_of_features(p1) +``` + +Generally, we recommend initializing problems using raster data where possible. This is because the `problem()` function needs to calculate the amount of each feature in each planning unit, and by providing both the planning unit and feature data in raster format with the same spatial resolution, extents, and coordinate systems, this means that the `problem()` function does not need to do any geoprocessing behind the scenes. But sometimes we can't use raster planning unit data because our planning units aren't equal-sized grid cells. So, below is an example showing how we can initialize a conservation planning problem using planning units that are formatted as spatial vector data. Note that if we had precomputed the amount of each feature in each planning unit and stored the data in the attribute table, we could pass in the names of the columns as an argument to the `problem()` function. + +```{r} +# create problem with spatial vector data +# note that we have to specify which column in the attribute table contains +# the cost data +p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") + +# print problem +print(p2) +``` + +We can also initialize a conservation planning problem using tabular planning unit data (i.e., `data.frame` format). Since the tabular planning unit data does not contain any spatial information, we also have to provide the feature data in tabular format (i.e., `data.frame` format) and data showing the amount of each feature in each planning unit in tabular format (i.e., `data.frame` format). The feature data must have an "id" column containing a unique integer identifier for each feature, and the planning unit by feature data must contain the following three columns: `pu` corresponding to the planning unit identifiers, `species` corresponding to the feature identifiers, and `amount` showing the amount of a given feature in a given planning unit. + +```{r} +# set file path for feature data +spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr") + +# load in feature data +spec_dat <- data.table::fread(spec_path, data.table = FALSE) + +# print first six rows of the data +# note that it contains extra columns +head(spec_dat) + +# set file path for planning unit vs. feature data +puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr") + +# load in planning unit vs feature data +puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE) + +# print first six rows of the data +head(puvspr_dat) + +# create problem +p3 <- problem(pu_dat, spec_dat, cost_column = "cost", rij = puvspr_dat) + +# print problem +print(p3) +``` + +For more information on initializing problems, please see the help page for the `problem()` function (which you can open by entering the code: `?problem`). Now that we have initialized a conservation planning problem, we will show you how you can customize it to suit the exact needs of your conservation planning scenario. Although we initialized the conservation planning problems using several different methods, moving forward, we will only use raster-based planning unit data to keep things simple. + +### Add an objective + +The next step is to add a primary objective to the problem. A problem objective is used to specify the primary goal of the problem (i.e., the quantity that is to be maximized or minimized). All conservation planning problems involve minimizing or maximizing some kind of objective. For instance, we might require a solution that conserves enough habitat for each species while minimizing the overall cost of the reserve network. Alternatively, we might require a solution that maximizes the number of conserved species while ensuring that the cost of the reserve network does not exceed the budget. Please note that objectives are added in the same way regardless of the type of data used to initialize the problem. The following objectives are available. + +* __Minimum set objective__: Minimize the cost of the solution whilst ensuring that all targets are met [@r11]. This objective is similar to that used in _Marxan_ [@r3]. For example, we can add a minimum set objective to a problem using the following code. +```{r} +# create a new problem that has the minimum set objective +p3 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() + +# print the problem +print(p3) +``` +* __Maximum cover objective__: Represent at least one instance of as many features as possible within a given budget [@r12]. +```{r} +# create a new problem that has the maximum coverage objective and a budget +# of 5000 +p4 <- problem(sim_pu_raster, sim_features) %>% + add_max_cover_objective(5000) + +# print the problem +print(p4) +``` +* __Maximum features objective__: Fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget [inspired by @r10]. This object is similar to the maximum cover objective except that we have the option of later specifying targets for each feature. In practice, this objective is more useful than the maximum cover objective because features often require a certain amount of area for them to persist and simply capturing a single instance of habitat for each feature is generally unlikely to enhance their long-term persistence. +```{r} +# create a new problem that has the maximum features objective and a budget +# of 5000 +p5 <- problem(sim_pu_raster, sim_features) %>% + add_max_features_objective(budget = 5000) + +# print the problem +print(p5) +``` +* __Minimum shortfall objective__: Minimize the overall (weighted sum) shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when there is a large amount of left-over budget when using the maximum feature representation objective and the remaining funds need to be allocated to places that will enhance the representation of features with unmet targets. +```{r} +# create a new problem that has the minimum shortfall objective and a budget +# of 5000 +p6 <- problem(sim_pu_raster, sim_features) %>% + add_min_shortfall_objective(budget = 5000) + +# print the problem +print(p6) +``` +* __Minimum largest shortfall objective__: Minimize the largest (maximum) shortfall while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when the minimum shortfall objective returns solutions that focus too much on representing a small number of features (e.g., because they occur in much cheaper planning units), and solutions are needed to spread conservation effort out more evenly among all features---even if it means that all features will have (relatively) poor representation. +```{r} +# create a new problem that has the minimum largest shortfall objective and a +# budget of 5000 +p7 <- problem(sim_pu_raster, sim_features) %>% + add_min_largest_shortfall_objective(budget = 5000) + +# print the problem +print(p7) +``` +* __Maximum phylogenetic diversity objective__: Maximize the phylogenetic diversity of the features represented in the solution subject to a budget [inspired by @r13; @r14]. This objective is similar to the maximum features objective except that emphasis is placed on protecting features which are associated with a diverse range of evolutionary histories. The package contains a simulated phylogeny that can be used with the simulated feature data (`sim_phylogny`). +```{r} +# load simulated phylogeny data +data(sim_phylogeny) + +# create a new problem that has the maximum phylogenetic diversity +# objective and a budget of 5000 +p8 <- problem(sim_pu_raster, sim_features) %>% + add_max_phylo_div_objective(budget = 5000, tree = sim_phylogeny) + +# print the problem +print(p8) +``` +* __Maximum phylogenetic endemism objective__: Maximize the phylogenetic endemism of the features represented in the solution subject to a budget [inspired by @r13; @r14; @r32]. This objective is similar to the maximum phylogenetic diversity except that emphasis is placed conserving features that are associated with geographically restricted periods of evolutionary history rather than a diverse range of evolutionary histories. +```{r} +# load simulated phylogeny data +data(sim_phylogeny) + +# create a new problem that has the maximum phylogenetic diversity +# objective and a budget of 5000 +p9 <- problem(sim_pu_raster, sim_features) %>% + add_max_phylo_end_objective(budget = 5000, tree = sim_phylogeny) + +# print the problem +print(p9) +``` +* __Maximum utility objective__: Secure as much of the features as possible without exceeding a budget. This objective is functionally equivalent to selecting the planning units with the greatest amounts of each feature (e.g., species richness). Generally, we don't encourage the use of this objective because it will only rarely identify complementary solutions--solutions which adequately conserve a range of different features---except perhaps to explore trade-offs or provide a baseline solution with which to compare other solutions. +```{r} +# create a new problem that has the maximum utility objective and a budget +# of 5000 +p10 <- problem(sim_pu_raster, sim_features) %>% + add_max_utility_objective(budget = 5000) + +# print the problem +print(p10) +``` + +### Add targets + +Most conservation planning problems require targets. Targets are used to specify the minimum amount or proportion of a feature's distribution that needs to be protected in the solution. For example, we may want to develop a reserve network that will secure 20% of the distribution for each feature for minimal cost. The following methods are available for specifying targets. + +* __Absolute targets__: Targets are expressed as the total amount of each feature in the study area that need to be secured. For example, if we had binary feature data that showed the absence or presence of suitable habitat across the study area, we could set an absolute target as 5 to mean that we require 5 planning units with suitable habitat in the solution. +```{r} +# create a problem with targets which specify that the solution must conserve +# a need a sum total of 3 units of suitable habitat for each feature +p11 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_absolute_targets(3) + +# print problem +print(p11) +``` +* __Relative targets__: Targets are set as a proportion (between 0 and 1) of the total amount of each feature in the study area. For example, if we had binary feature data and the feature occupied a total of 20 planning units in the study area, we could set a relative target of 50 % to specify that the solution must secure 10 planning units for the feature. We could alternatively specify an absolute target of 10 to achieve the same result, but sometimes proportions are easier to work with. +```{r} +# create a problem with the minimum set objective and relative targets of 10 % +# for each feature +p12 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) + +# print problem +print(p12) + +# create a problem with targets which specify that we need 10 % of the habitat +# for the first feature, 15 % for the second feature, 20 % for the third feature +# 25 % for the fourth feature and 30 % of the habitat for the fifth feature +targets <- c(0.1, 0.15, 0.2, 0.25, 0.3) +p13 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(targets) + +# print problem +print(p13) +``` +* __Log-linear targets__: Targets are expressed using scaling factors and log-linear interpolation. This method for specifying targets is commonly used for global prioritization analyses [@r15]. +```{r} +# create problem with added log-linear targets +p14 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_loglinear_targets(10, 0.9, 100, 0.2) + +# print problem +print(p14) +``` +* __Manual targets__: Targets are manually specified. This is only really recommended for advanced users or problems that involve multiple management zones. See the [zones vignette](zones.html) for more information on these targets. + +As with the functions for specifying the objective of a problem, if we try adding multiple targets to a problem, only the most recently added set of targets are used. + +### Add constraints + +A constraint can be added to a conservation planning problem to ensure that all solutions exhibit a specific property. For example, they can be used to make sure that all solutions select a specific planning unit or that all selected planning units in the solution follow a certain configuration. The following constraints are available. + +* __Locked in constraints__: Add constraints to ensure that certain planning units are prioritized in the solution. For example, it may be desirable to lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network. +```{r} +# create problem with constraints which specify that the first planning unit +# must be selected in the solution +p15 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints(1) + +# print problem +print(p15) +``` +* __Locked out constraints__: Add constraints to ensure that certain planning units are not prioritized in the solution. For example, it may be useful to lock out planning units that have been degraded and are not suitable for conserving species. +```{r} +# create problem with constraints which specify that the second planning unit +# must not be selected in the solution +p16 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_out_constraints(2) + +# print problem +print(p16) +``` +* __Neighbor constraints__: Add constraints to a conservation problem to ensure that all selected planning units have at least a certain number of neighbors. +```{r} +# create problem with constraints which specify that all selected planning units +# in the solution must have at least 1 neighbor +p17 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_neighbor_constraints(1) + +# print problem +print(p17) +``` +* __Contiguity constraints__: Add constraints to a conservation problem to ensure that all selected planning units are spatially connected to each other and form spatially contiguous unit. +```{r} +# create problem with constraints which specify that all selected planning units +# in the solution must form a single contiguous unit +p18 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_contiguity_constraints() + +# print problem +print(p18) +``` +* __Feature contiguity constraints__: Add constraints to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of those implemented in the `add_contiguity_constraints` function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit. +```{r} +# create problem with constraints which specify that the planning units used +# to conserve each feature must form a contiguous unit +p19 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_feature_contiguity_constraints() + +# print problem +print(p19) +``` + +* __Linear constraints__: Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study region (e.g., different countries). + +```{r} +# create problem with constraints which specify that the sum of +# values in sim_features[[1]] among selected planning units must not exceed a +#' threshold value of 190. +p20 <- problem(sim_pu_raster, sim_features) %>% + add_min_shortfall_objective(budget = 1800) %>% + add_relative_targets(0.1) %>% + add_linear_constraints(190, "<=", sim_features[[1]]) + +# print problem +print(p20) +``` + +* __Mandatory allocation constraints__: Add constraints to ensure that every planning unit is allocated to a management zone in the solution. Please note that this function can only be used with problems that contain multiple zones. For more information on problems with multiple zones and an example using this function, see the Management Zones vignette. + +In particular, The `add_locked_in_constraints` and `add_locked_out_constraints` functions are incredibly useful for real-world conservation planning exercises, so it's worth pointing out that there are several ways we can specify which planning units should be locked in or out of the solutions. If we use raster planning unit data, we can also use raster data to specify which planning units should be locked in our locked out. + +```{r, fig.width = 6.5, fig.height = 2.5} +# load data to lock in or lock out planning units +data(sim_locked_in_raster) +data(sim_locked_out_raster) + +# plot the locked data +plot(stack(sim_locked_in_raster, sim_locked_out_raster), + main = c("Locked In", "Locked Out")) + +# create a problem using raster planning unit data and use the locked raster +# data to lock in some planning units and lock out some other planning units +p21 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints(sim_locked_in_raster) %>% + add_locked_out_constraints(sim_locked_out_raster) + +# print problem +print(p21) +``` + +If our planning unit data are in a spatial vector format (similar to the `sim_pu_polygons` data) or a tabular format (similar to `pu_dat`), we can use the field names in the data to refer to which planning units should be locked in and / or out. For example, the `sim_pu_polygons` object has `TRUE` / `FALSE` values in the "locked_in" field which indicate which planning units should be selected in the solution. We could use the data in this field to specify that those planning units with `TRUE` values should be locked in using the following methods. + +```{r} +# preview first six rows of the attribute table for sim_pu_polygons +head(sim_pu_polygons@data) + +# specify locked in data using the field name +p22 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints("locked_in") + +# print problem +print(p22) + +# specify locked in data using the values in the field +p23 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_locked_in_constraints(which(sim_pu_polygons$locked_in)) + +# print problem +print(p23) +``` + +### Add penalties + +We can also add penalties to a problem to favor or penalize solutions according to a secondary objective. Unlike the constraint functions, these functions will add extra information to the objective function of the optimization function to penalize solutions that do not exhibit specific characteristics. For example, penalties can be added to a problem to avoid highly fragmented solutions at the expense of accepting slightly more expensive solutions. All penalty functions have a `penalty` argument that controls the relative importance of the secondary penalty function compared to the primary objective function. It is worth noting that incredibly low or incredibly high `penalty` values -- relative to the main objective function -- can cause problems to take a very long time to solve, so when trying out a range of different penalty values it can be helpful to limit the solver to run for a set period of time. The following penalties are available. + +* __Boundary penalties__: Add penalties to penalize solutions that are excessively fragmented. These penalties are similar to those used in _Marxan_ [@r3; @r1]. +```{r} +# create problem with penalties that penalize fragmented solutions with a +# penalty factor of 0.01 +p24 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_boundary_penalties(penalty = 0.01) + +# print problem +print(p24) +``` +* __Connectivity penalties__: Add penalties to favor solutions that select combinations of planning units with high connectivity between them. These penalties are similar to those used in _Marxan with Zones_ [@r2; @r1]. This function supports both symmetric and asymmetric connectivities among planning units. +```{r} +# create problem with penalties that favor combinations of planning units with +# high connectivity, here we will use only the first four layers in +# sim_features for the features and we will use the fifth layer in sim_features +# to represent the connectivity data, where the connectivity_matrix function +# will create a matrix showing the average strength of connectivity between +# adjacent planning units using the data in the fifth layer of sim_features +p25 <- problem(sim_pu_raster, sim_features[[1:4]]) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_boundary_penalties( + penalty = 5, + data = connectivity_matrix(sim_pu_raster, sim_features[[5]])) + +# print problem +print(p25) +``` +* __Linear penalties__: Add penalties to penalize solutions that select planning units according to a certain variable (e.g., anthropogenic pressure). +```{r} +# create data for penalizing planning units +# (note this requires the RandomFields package to be installed) +pen_raster <- simulate_cost(sim_pu_raster) + +# create problem with penalties that penalize solutions that select +# planning units with high values in the pen_raster object, +# here we will use a penalty value of 5 to indicate the trade-off (scaling) +# between the penalty values (in the sim_pu_raster) and the main objective +# (i.e., the cost of the solution) +p26 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_linear_penalties(penalty = 5, data = pen_raster) + +# print problem +print(p26) +``` + +### Add the decision types + +Conservation planning problems involve making decisions on how planning units will be managed. These decisions are then associated with management actions (e.g., turning a planning unit into a protected area). The type of decision describes how the action is applied to planning units. For instance, the default decision-type is a binary decision type, meaning that we are either selecting or not selecting planning units for management. The following decision types are available. + +* __Binary decisions__: Add a binary decision to a conservation planning problem. This is the classic decision of either prioritizing or not prioritizing a planning unit. Typically, this decision has the assumed action of buying the planning unit to include in a protected area network. If no decision is added to a problem object, then this decision class will be used by default. +```{r} +# add binary decisions to a problem +p27 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() + +# print problem +print(p27) +``` +* __Proportion decisions__: Add a proportion decision to a problem. This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the default of the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in a protected area network. Generally, problems can be solved much faster with proportion-type decisions than binary-type decisions, so they can be very useful when commercial solvers are not available. +```{r} +# add proportion decisions to a problem +p28 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_proportion_decisions() + +# print problem +print(p28) +``` +* __Semi-continuous decisions__: Add a semi-continuous decision to a problem. This decision is similar to proportion decisions except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction (e.g., 80%) of a planning unit can be purchased. This type of decision may be useful when it is not practical to conserve the entire area indicated by a planning unit. +```{r} +# add semi-continuous decisions to a problem, where we can only manage at most +# 50 % of the area encompassed by a planning unit +p29 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_semicontinuous_decisions(0.5) + +# print problem +print(p29) +``` + +### Add a solver + +Next, after specifying the mathematical formulation that underpins your conservation planning problem, you can specify how the problem should be solved. If you do not specify this information, the _prioritizr R_ package will automatically use the best solver currently installed on your system with some reasonable defaults. __We strongly recommend installing the [Gurobi software suite and the _gurobi_ _R_ package](https://www.gurobi.com/) to solve problems, and for more information on this topic please refer to the [Gurobi Installation Guide](gurobi_installation.html)__. The following solvers are available. + +* __*Gurobi* solver__: [_Gurobi_](https://www.gurobi.com/) is a state of the art commercial optimization software. It is by far the fastest of the solvers that can be used to solve conservation problems. However, it is not freely available. That said, special licenses are available to academics at no cost. +```{r} +# create a problem and specify that Gurobi should be used to solve the problem +# and specify an optimality gap of zero to obtain the optimal solution +p30 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_gurobi_solver(gap = 0) + +# print problem +print(p30) +``` +* __*IBM CPLEX* solver__: [_IBM CPLEX_](https://www.ibm.com/analytics/cplex-optimizer) is a commercial optimization software. It is faster than the open source solvers available for generating prioritizations (see below), however, it is not freely available. Similar to the _Gurobi_ software, special licenses are available to academics at no cost. +```{r} +# create a problem and specify that IBM CPLEX should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p31 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_cplex_solver(gap = 0) + +# print problem +print(p31) +``` +* __*CBC* solver__: [*CBC*](https://projects.coin-or.org/Cbc) is an +open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks have yet to be completed, preliminary analyses suggest that it is +the fastest open-source solver available for generating prioritizations. It requires the _rcbc R_ package, which is currently only available on [GitHub](https://github.com/dirkschumacher/rcbc). +```{r} +# create a problem and specify that CBC should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p32 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_cbc_solver(gap = 0) + +# print problem +print(p32) +``` +* __*lpsymphony* solver__: [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) is an open-source integer programming solver that is also part of the COIN-OR project. This solver uses the _lpsymphony R_ package (available on [Bioconductor](http://bioconductor.org/packages/release/bioc/html/lpsymphony.html)) to interface with the _SYMPHONY_ software. +```{r} +# create a problem and specify that lpsymphony should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p33 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_lpsymphony_solver(gap = 0) + +# print problem +print(p33) +``` +* __*Rsymphony* solver__: This solver provides a different interface to the [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) software. It uses the _Rsymphony R_ package which is available on The Comprehensive R Archive Network (CRAN). This solver is generally slower than the other solvers, because it cannot use parallel processing. +```{r} +# create a problem and specify that Rsymphony should be used to solve the +# problem and specify an optimality gap of zero to obtain the optimal solution +p34 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_rsymphony_solver(gap = 0) + +# print problem +print(p34) +``` + +### Add a portfolio + +Many conservation planning exercises require a portfolio of solutions. For example, real-world exercises can involve presenting decision makers with a range of near-optimal decisions. Additionally, the number of times that different planning units are selected in different solutions can provide insight into their relative importance. The following portfolio methods are available. + +* __Gap portfolio__: Generate a portfolio of solutions by finding a certain number of solutions that are all within a pre-specified optimality gap. This method is especially useful for generating multiple solutions that can used be to calculate selection frequencies (similar to _Marxan_). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. +```{r} +# create a problem and specify that a portfolio should be created by +# finding five solutions within 10% of optimality +p35 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) + +# print problem +print(p35) +``` +* __Top portfolio__: Generate a portfolio of solutions by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. +```{r} +# create a problem and specify that a portfolio should be created using +# the top five solutions +p36 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_top_portfolio(number_solutions = 5) + +# print problem +print(p36) +``` +* __Extra portfolio__: Generate a portfolio of solutions by storing feasible solutions found during the optimization process. Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. +```{r} +# create a problem and specify that a portfolio should be created using +# extra solutions found while solving the problem +p37 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_extra_portfolio() + +# print problem +print(p37) +``` +* __Cuts portfolio__: Generate a portfolio of distinct solutions within a pre-specified optimality gap. This method is only recommended if the _Gurobi_ optimization solver is not available. +```{r} +# create a problem and specify that a portfolio containing 10 solutions +# should be created using using Bender's cuts +p38 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_cuts_portfolio(number_solutions = 10) + +# print problem +print(p38) +``` +* __Shuffle portfolio__: Generate a portfolio of solutions by randomly reordering the data prior to attempting to solve the problem. If the _Gurobi_ optimization solver is not available, this method is the fastest method for generating a set number of solutions within a specified distance from optimality. +```{r} +# create a problem and specify a portfolio should be created that contains +# 10 solutions and that any duplicate solutions should not be removed +p39 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() %>% + add_shuffle_portfolio(number_solutions = 10, remove_duplicates = FALSE) + +# print problem +print(p39) +``` + +### Solve the problem + +After formulating our conservation planning problem and specifying how the problem should be solved, we can use the `solve` function to obtain a solution. Note that the solver will typically print out some information describing the size of the problem and report its progress when searching for a suitable solution. + +```{r, fig.height = h, fig.width = w} +# formulate the problem +p40 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_boundary_penalties(penalty = 500, edge_factor = 0.5) %>% + add_binary_decisions() + +# solve the problem (using the default solver) +s40 <- solve(p40) + +# plot solution +plot(s40, col = c("grey90", "darkgreen"), main = "Solution", + xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) +``` + +We can plot this solution because the planning unit input data are spatially referenced in a raster format. The output format will always match the planning unit data used to initialize the problem. For example, the solution to a problem with planning units in a spatial vector (shapefile) format would also be in a spatial vector format. Similarly, if the planning units were in a tabular format (i.e., `data.frame`), the solution would also be returned in a tabular format. + +We can also extract attributes from the solution that describe the quality of the solution and the optimization process. + +```{r} +# extract the objective (numerical value being minimized or maximized) +print(attr(s40, "objective")) + +# extract time spent solving solution +print(attr(s40, "runtime")) + +# extract state message from the solver that describes why this specific +# solution was returned +print(attr(s40, "status")) +``` + +## Evaluate the solution + +Conservation planning involves making trade-offs between different criteria (e.g. overall cost, feature representation, connectivity). After obtaining a solution to a conservation planning problem, it is important to evaluate it to help understand the trade-offs made by the prioritization. This is also useful to compare different solutions with each other. + +### Evaluating performance + +Summary statistics can be computed to evaluate the overall performance of a solution based on certain criteria. The following summaries can be computed. + +The following functions are available to summarize a solution: + +* __Number summary__: Calculate the number of planning units selected by a solution. +```{r} +# calculate statistic +eval_n_summary(p40, s40) +``` +* __Cost summary__: Calculate the total cost of a solution. +```{r} +# calculate statistic +eval_cost_summary(p40, s40) +``` +* __Feature representation summary__: Calculate how well features are represented by a solution. This function can be used for any type of problem. +```{r} +# calculate statistics +eval_feature_representation_summary(p40, s40) +``` +* __Target coverage summary__: Calculate how well representation targets are met by a solution. This function can only be used with problems contain targets. +```{r} +# calculate statistics +eval_target_coverage_summary(p40, s40) +``` +* __Boundary summary__: Calculate the total exposed boundary length (perimeter) associated with a solution. +```{r} +# calculate statistic +eval_boundary_summary(p40, s40) +``` +* __Connectivity summary__: Calculate the connectivity facilitated within a solution. +```{r} +# calculate statistic +# here we use the raster data for the first feature as an example +# to parametrize pair-wise connectivity between different planning units +eval_connectivity_summary( + p40, s40, data = connectivity_matrix(sim_pu_raster, sim_features[[1]])) +``` + +### Evaluating relative importance + +Conservation plans can take a long time to implement. Since funding availability and habitat quality can decline over time, it is critical that the most important places in a prioritization are scheduled for management as early as possible. For instance, some planning units in a solution might contain many rare species which do not occur in any other planning units. Alternatively, some planning units might offer an especially high return on investment that reduces costs considerably. As a consequence, conservation planners often need information on which planning units selected in a prioritization are most important to the overall success of the prioritization. To achieve this, conservation planners can use importance scores for each planning unit selected by a solution. + +Let's generate a prioritization so that can compare the different importance methods. + +```{r, fig.height = h, fig.width = w} +# formulate the problem +p41 <- problem(sim_pu_raster, sim_features) %>% + add_min_set_objective() %>% + add_relative_targets(0.1) %>% + add_binary_decisions() + +# solve the problem +s41 <- solve(p41) + +# plot solution +plot(s41, col = c("grey90", "darkgreen"), main = "Solution", + xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) +``` + + The following methods are available for computing importance scores. + +* __Replacement cost__: Evaluate importance using the replacement cost method [@r31]. This method quantifies the importance of a given planning unit as the decrease in the performance of the solution (based on the objective function) if the planning unit cannot be acquired (e.g., in terms of the additional costs required to meet feature targets). The advantages of this method are that it (i) accounts for the costs of the planning units, (ii) can be applied to multiple management zones, (iii) can be applied to any objective function, and (iv) can identify truly irreplaceable planning units (denoted with infinite values). However, the key disadvantage of this method, is that can take an infeasible amount of time to complete for large and complex problems. +```{r, fig.height = h, fig.width = w} +# calculate replacement cost scores and make the solver quiet +rc41 <- p41 %>% + add_default_solver(gap = 0, verbose = FALSE) %>% + eval_replacement_importance(s41) + +# plot replacement cost scores +plot(rc41, main = "Replacement cost scores") +``` +* __Ferrier method__: Evaluate importance by computing irreplaceability scores following Ferrier *et al.* [@r-34]. The advantages of this method are that it (i) can be computed relatively quickly for moderate and large-sized problems, and (ii) calculates a score for each feature within each planning unit to provide insight into why certain planning units are more important than others. The disadvatange with this method is that it can only be applied to conservation problems with a minimum set objective and a single zone (i.e., similar to _Marxan_-type problems). +```{r, fig.height = h, fig.width = w} +# calculate Ferrier scores and extract total score +fs41 <- eval_ferrier_importance(p40, s40)[["total"]] + +# plot Ferrier scores +plot(fs41, main = "Ferrier scores") +``` +* __Rarity weighted richness__: Evaluate importance by computing rarity weighted richness scores [@r33]. The only advantage with this method is that it can be computed very quickly for very large problems. The key disadvantage with this approach is that it merely describes the spatial patterns of biodiversity, and does not consider any of the goals that underpin conservation planning exercise. For instance, it does not account for planning costs, management zones, objective functions, or feature representation targets. +```{r, fig.height = h, fig.width = w} +# calculate rarity weighted richness scores +rwr41 <- eval_rare_richness_importance(p40, s40) + +# plot rarity weighted richness scores +plot(rwr41, main = "Rarity weighted richness") +``` + +In general, we recommend using replacement cost scores for small and moderate sized problems (e.g., less than 30,000 planning units) when it is feasible to do so. It can take a very long time to compute replacement cost scores, and so it is simply not feasible to compute these scores for particularly large problems. For moderate and large sized problems (e.g., more than 30,000 planning units), we recommend using the Ferrier method. This is because it explicitly accounts for representation targets, unlike the rarity weighted richness scores. We almost never recommend using the rarity weighted richness scores. This is because they do not consider criteria needed to inform conservation decision making [@r51]. + +## _Marxan_ problems + +Although we encourage users to build and tailor conservation planning problems to suit their own needs, sometimes it just simply easier to use something you're already familiar with. The `marxan_problem()` function is provided as a convenient wrapper for building and solving _Marxan_-style conservation problems. If users already have their conservation planning data formatted for use with _Marxan_, this function can also read _Marxan_ data files and solve the _Marxan_-style problems using exact algorithm solvers. __Please note that problems built using the `marxan_problem()` function are still solved the same way as a problem initialized using the `problem()` function, and therefore still require the installation of one of the solver packages.__ + +Here is a short example showing how the `marxan_problem()` function can be used to read _Marxan_ input files and the `solve` function can be used to solve the problem. + +```{r, results = "hide"} +# set file path for Marxan input file +minput <- system.file("extdata/input.dat", package = "prioritizr") + +# read Marxan input file +mp <- marxan_problem(minput) + +# print problem +print(mp) + +# solve the problem +ms <- solve(mp) + +# since the Marxan data was in a tabular format, the solution is also returned +# in a tabular format, so we will print the first six rows of the table +# containing the solution +head(ms) +``` + +Alternatively, rather then using a _Marxan_ input file to construct the problem, we can manually read in the _Marxan_ data files and input these to the `marxan_problem()` function. + +```{r} +# load data +pu <- system.file("extdata/input/pu.dat", package = "prioritizr") %>% + read.table(sep = ",", header = TRUE) +features <- system.file("extdata/input/spec.dat", package = "prioritizr") %>% + read.table(sep = ",", header = TRUE) +bound <- system.file("extdata/input/bound.dat", package = "prioritizr") %>% + read.table(sep = "\t", header = TRUE) +rij <- system.file("extdata/input/puvspr.dat", package = "prioritizr") %>% + read.table(sep = ",", header = TRUE) + +# build Marxan problem using data.frame objects +mp2 <- marxan_problem(x = pu, spec = features, puvspr = rij, bound = bound, + blm = 0) + +# print problem +print(mp2) +``` + +Conservation planning problems that are built using the `marxan_problem()` function can also be customized. For example, we could change the decision type for `mp2` to involve selecting a proportion of each planing unit (using the `add_proportion_decisions()` function). + +## Conclusion + +Hopefully, this vignette has provided an informative overview of the _prioritizr R_ package. For more examples using the package, please see the other vignettes. Perhaps, one of the best ways to learn a new piece of software is to just try it out. Test it, try breaking it, make mistakes, and learn from them. We would recommend trying to build conservation planning problems that resemble those you face in your own work---but using the built-in example data sets. This way you can quickly verify that the problems you build actually mean what you think they mean. For instance, you can try playing around with the targets and see what effect they have on the solutions, or try playing around with penalties and see what effect they have on the solutions. Finally, if you have any questions about using the package or suggestions for it, please [post an issue on the package's online coding repository](https://github.com/prioritizr/prioritizr/issues). + +## References diff --git a/vignettes/prioritizr.Rmd b/vignettes/prioritizr.Rmd index d6f896ebd..dc2883a47 100644 --- a/vignettes/prioritizr.Rmd +++ b/vignettes/prioritizr.Rmd @@ -1,5 +1,5 @@ --- -title: "prioritizr: Systematic Conservation Prioritization in R" +title: "Getting started" output: rmarkdown::html_vignette: toc: true @@ -10,900 +10,278 @@ documentclass: article bibliography: references.bib csl: reference-style.csl vignette: > - %\VignetteIndexEntry{prioritizr: Systematic Conservation Prioritization in R} + %\VignetteIndexEntry{Getting started} %\VignetteEngine{knitr::rmarkdown_notangle} --- ```{r, include = FALSE} -h = 3.5 -w = 3.5 +# define dummy variables so that vignette passes package checks +tas_features <- raster::raster(matrix(1)) +``` + +```{r, include = FALSE} +# define variables for vignette figures and code execution +h <- 3.5 +w <- 3.5 is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) -knitr::opts_chunk$set(fig.align = "center", eval = !is_check, - root.dir = normalizePath("../..")) +knitr::opts_chunk$set(fig.align = "center", eval = !is_check) ``` -## Summary - -The _prioritizr R_ package uses integer linear programming (ILP) techniques to provide a flexible interface for building and solving conservation planning problems [@r11; @r16]. It supports a broad range of objectives, constraints, and penalties that can be used to custom-tailor conservation planning problems to the specific needs of a conservation planning exercise. Once built, conservation planning problems can be solved using a variety of commercial and open-source exact algorithm solvers. In contrast to the algorithms conventionally used to solve conservation problems, such as heuristics or simulated annealing [@r3], the exact algorithms used here are guaranteed to find optimal solutions. Furthermore, conservation problems can be constructed to optimize the spatial allocation of different management zone (or actions), meaning that conservation practitioners can identify solutions that benefit multiple stakeholders. Finally, this package has the functionality to read input data formatted for the _Marxan_ conservation planning program [@r3], and find much cheaper solutions in a much shorter period of time than _Marxan_ [@r1]. - ## Introduction -Systematic conservation planning is a rigorous, repeatable, and structured approach to designing new protected areas that efficiently meet conservation objectives [@r4]. Historically, conservation decision-making has often evaluated parcels opportunistically as they became available for purchase, donation, or under threat. Although purchasing such areas may improve the _status quo_, such decisions may not substantially enhance the long-term persistence of target species or communities. Faced with this realization, conservation planners began using decision support tools to help simulate alternative reserve designs over a range of different biodiversity and management goals and, ultimately, guide protected area acquisitions and management actions. Due to the systematic, evidence-based nature of these tools, conservation prioritization can help contribute to a transparent, inclusive, and more defensible decision making process. - -A conservation planning exercise typically starts by defining a study area. This study area should encompass all the areas relevant to the decision maker or the hypothesis being tested. The extent of a study area could encompass a few important localities [e.g. @r18], a single state [e.g. @r17], an entire country [@r19], or the entire planet [@r20]. Next, the study area is divided into a set of discrete areas termed _planning units_. Each planning unit represents a discrete locality in the study area that can be managed independently of other areas. The general idea is that some combination of the planning units can be selected for conservation actions (e.g. protected area establishment, habitat restoration). Planning units are often created as square or hexagon cells that are sized according to the scale of the conservation actions, and the resolution of the data that underpin the planning exercise [but see @r5]. - -Cost data (or a surrogate thereof) are needed to inform the prioritization process. Specifically, these cost data describe the relative expenditure associated with managing each planning unit for conservation. For example, if the goal of the conservation planning exercise is to identify priority areas for expanding a local protected area system, then the cost data could represent the physical cost of purchasing the land. Alternatively, if such data are not available, then surrogate data could are often used instead (e.g. human population density, opportunity cost of foregone commercial activities, or planning unit size). - -Conservation planning exercises also require data on the biodiversity elements that are of conservation interest (termed _conservation features_). These features could be species (e.g. _Neofelis nebulosa_, the Clouded Leopard), populations, or habitats (e.g. mangroves or cloud forest). After identifying the set of relevant conservation features for a conservation planning exercise, spatially explicit data need to be obtained for each and every feature to describe their spatial distribution (e.g. habitat suitability data, probability of occurrence data, presence/absence data). This is important to ensure that conservation features are adequately covered (represented) by prioritizations. After assembling all the data, the next step is to define the conservation planning problem. - -The _prioritizr R_ package is designed to help you build and solve conservation planning problems. Specifically, prioritizations are generated by using formulating a mathematical optimization problem and then solving it to generate a solution. These mathematical optimization problems are formulated using the planning unit data, cost data, and feature data, and with information related to the overarching aim of the prioritization process. In general, the goal of an optimization problem is to minimize (or maximize) an _objective function_ that is calculated using a set of _decision variables_, subject to a series of constraints to ensure that solution exhibits specific properties. The objective function describes the quantity which we are trying to minimize (e.g. cost of the solution) or maximize (e.g. number of features conserved). The decision variables describe the entities that we can control, and indicate which planning units are selected for conservation management and which of those are not. The constraints can be thought of as rules that the need decision variables need to follow. For example, a commonly used constraint is specifying that the solution must not exceed a certain budget. - -A wide variety of approaches have been developed for solving optimization problems. Reserve design problems are frequently solved using simulated annealing [@r6] or heuristics [@r8; @r7]. These methods are conceptually simple and can be applied to a wide variety of optimization problems. However, they do not scale well for large or complex problems [@r1]. Additionally, these methods cannot tell you how close any given solution is to the optimal solution. The _prioritizr R_ package uses exact algorithms to efficiently solve conservation planning problems to within a pre-specified a optimality gap. In other words, you can specify that you need the optimal solution (i.e. a gap of 0%) and the algorithms will, given enough time, find a solution that meets this criteria. In the past, exact algorithms have been too slow for conservation planning exercises [@r9]. However, improvements over the last decade mean that they are now much faster [@r23; @r1]. - -In this package, optimization problems are expressed using _integer linear programming_ (ILP) so that they can be solved using (linear) exact algorithm solvers. The general form of an integer programming problem can be expressed in matrix notation using the following equation. - -$$\text{Minimize} \space \boldsymbol{c}^\text{T} \boldsymbol{x} \space -\space \text{subject to} \space A\boldsymbol{x} -\space \Box \space \boldsymbol{b}$$ - -Here, where $x$ is a vector of decision variables, $c$ and $b$ are vectors of known coefficients, and $A$ is the constraint matrix. The final term specifies a series of structural constants and the $\Box$ symbol is used to indicate that the relational operators for the constraints can be either $\geq$, $=$, or $\leq$. In the context of a conservation planning problem, $c$ could be used to represent the planning unit costs, $A$ could be used to store the data showing the presence / absence (or amount) of each feature in each planning unit, $b$ could be used to represent minimum amount of habitat required for each species in the solution, the $\Box$ could be set to $\geq$ symbols to indicate that the total amount of each feature in the solution must exceed the quantities in $b$. But there are many other ways of formulating the reserve selection problem [@r11]. - -## Package overview - -The _prioritizr R_ package contains eight main types of functions. These -functions are used to: - -* create a new conservation planning [problem](https://prioritizr.net/reference/problem.html) by specifying the planning units, features, and management zones of conservation interest (e.g. species, ecosystems). -* add an [objective](https://prioritizr.net/reference/objectives.html) to a conservation planning problem. -* add [targets](https://prioritizr.net/reference/targets.html) to a problem to specify how much of each feature is desired or required to be conserved in the solutions. -* add [constraints](https://prioritizr.net/reference/constraints.html) to a conservation planning problem to ensure that solutions exhibit specific properties (e.g. select specific planning units for protection). -* add [penalties](https://prioritizr.net/reference/penalties.html) to a problem to penalize solutions according to specific metric (e.g. connectivity). -* add [decisions](https://prioritizr.net/reference/decisions.html) to a problem to specify the nature of the decisions in the problem. -* add methods to generate a [portfolio](https://prioritizr.net/reference/portfolios.html) of solutions. -* add a [solver](https://prioritizr.net/reference/solvers.html) to a conservation problem to specify which software should be used to generate solutions and customize the optimization process. -* [solve](https://prioritizr.net/reference/solve.html) a conservation problem. -* evaluate a solution by computing [summary](https://prioritizr.net/reference/summaries.html) statistics. -* evaluate the relative [importance](https://prioritizr.net/reference/importance.html) (irreplaceability) of planning units selected in a solution. +The aim of this tutorial is to provide a short introduction to the _prioritizr R_ package. It is also intended for helping conservation planners familiar the _Marxan_ decision support tool [@r3] to start using the package for their work. -## Package workflow +## Data -The general workflow when using the _prioritizr R_ package starts with creating a new conservation planning `problem` object using data. Specifically, the `problem` object should be constructed using data that specify the planning units, biodiversity features, management zones (if applicable), and costs. After creating a new `problem` object, it can be customized---by adding objectives, penalties, constraints, and other information---to build a precise representation of the conservation planning problem required, and then solved to obtain a solutions. +Let's load the packages and data used in this tutorial. Since this tutorial uses data from the _prioritizrdata R_ package, please ensure that it is installed. The data used in this tutorial were obtained from the _Introduction to Marxan_ course. Specifically, the data were originally a subset of a larger spatial prioritization project performed under contract to Australia's Department of Environment and Water Resources [@r30]. -All conservation planning problems require an objective. An objective specifies the property which is used to compare different feasible solutions. Simply put, the objective is the property of the solution which should be maximized or minimized during the optimization process. For instance, with the minimum set objective (specified using `add_min_set_objective`), we are seeking to minimize the cost of the solution (similar to _Marxan_). On the other hand, with the maximum coverage objective (specified using `add_max_cover_objective`), we are seeking to maximize the number of different features represented in the solution. - -Many objectives require `targets` (e.g. the minimum set objective). Targets are a specialized set of constraints that relate to the total quantity of each feature secured in the solution (e.g. amount of suitable habitat or number of individuals). In the case of the minimum set objective ( `add_min_set_objective`), they are used to ensure that solutions secure a sufficient quantity of each feature, and in other objectives, such as the maximum features objective ( `add_max_features_objective`) they are used to assess whether a feature has been adequately conserved by a candidate solution. Targets can be expressed numerically as the total amount required for a given feature (using `add_absolute_targets`), or as a proportion of the total amount found in the planning units (using `add_relative_targets`). Note that not all objectives require targets, and a warning will be thrown if an attempt is made to add targets to a problem with an objective that does not use them. - -Constraints and penalties can be added to a conservation planning problem to ensure that solutions exhibit a specific property or penalize solutions which don't exhibit a specific property (respectively). The difference between constraints and penalties, strictly speaking, is constraints are used to rule out potential solutions that don't exhibit a specific property. For instance, constraints can be used to ensure that specific planning units are selected in the solution for prioritization (using `add_locked_in_constraints`) or not selected in the solution for prioritization (using `add_locked_out_constraints`). On the other hand, penalties are combined with the objective of a problem, with a penalty factor, and the overall objective of the problem then becomes to minimize (or maximize) the primary objective function and the penalty function. For example, penalties can be added to a problem to penalize solutions that are excessively fragmented (using `add_boundary_penalties`). These penalties have a `penalty` argument that specifies the relative importance of having spatially clustered solutions. When the argument to `penalty` is high, then solutions which are less fragmented are valued more highly---even if they cost more---and when the argument to `penalty` is low, then the solutions which are more fragmented are valued less highly. - -After building a conservation problem, it can then be solved to obtain a solution (or portfolio of solutions if desired). The solution is returned in the same format as the planning unit data used to construct the problem. This means that if raster or shapefile / vector data was used when initializing the problem, then the solution will also be in raster or shapefile / vector data. This can be very helpful when it comes to interpreting and visualizing solutions because it means that the solution data does not first have to be merged with spatial data before they can be plotted on a map. - -## Usage - -Here we will provide an introduction to using the _prioritizr R_ package to build and solve a conservation planning problem. Please note that we will not discuss conservation planning with multiple zones in this vignette, for more information on working with multiple management zones please see the [zones vignette](zones.html). - -First, we will load the _prioritizr_ package. - -```{r, results = "hide", message = FALSE} -# load package +```{r, message = FALSE} +# load packages +library(prioritizrdata) library(prioritizr) +library(vegan) +library(cluster) -# set default options for printing tabular data -options(tibble.width = Inf) -``` - -### Data - -Now we will load some built-in data sets that are distributed with the _prioritizr R_ package. This package contains several different planning unit data sets. To provide a comprehensive overview of the different ways that we can initialize a conservation planning problem, we will load each of them. - -First, we will load the raster planning unit data (`sim_pu_raster`). Here, the planning units are represented as a raster (i.e. a `RasterLayer` object) and each pixel corresponds to the spatial extent of each panning unit. The pixel values correspond to the acquisition costs of each planning unit. - -```{r} -# load raster planning unit data -data(sim_pu_raster) - -# print description of the data -print(sim_pu_raster) - -# plot the data -plot(sim_pu_raster) -``` - -Secondly, we will load one of the spatial vector planning unit data sets (`sim_pu_polygons`). Here, each polygon (i.e. feature using ArcGIS terminology) corresponds to a different planning unit. This data set has an attribute table that contains additional information about each polygon. Namely, the `cost` field (column) in the attribute table contains the acquisition cost for each planning unit. - -```{r} -# load polygon planning unit data -data(sim_pu_polygons) - -# print first six rows of attribute table -head(sim_pu_polygons@data) - -# plot the planning units -spplot(sim_pu_polygons, zcol = "cost") -``` - -Thirdly, we will load some planning unit data stored in tabular format (i.e. `data.frame` format). For those familiar with _Marxan_ or dealing with very large conservation planning problems (> 10 million planning units), it may be useful to work with data in this format because it does not contain any spatial information which will reduce computational burden. When using tabular data to initialize conservation planning problems, the data must follow the conventions used by _Marxan_. Specifically, each row in the planning unit table must correspond to a different planning unit. The table must also have an "id" column to provide a unique integer identifier for each planning unit, and it must also have a column that indicates the cost of each planning unit. For more information, please see the [official _Marxan_ documentation](https://marxansolutions.org/). - -```{r} -# specify file path for planning unit data -pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr") - -# load in the tabular planning unit data -# note that we use the data.table::fread function, as opposed to the read.csv -# function, because it is much faster -pu_dat <- data.table::fread(pu_path, data.table = FALSE) - -# preview first six rows of the tabular planning unit data -# note that it has some extra columns other than id and cost as per the -# Marxan format -head(pu_dat) -``` - -Finally, we will load data showing the spatial distribution of the conservation features. Our conservation features (`sim_features`) are represented as a stack of raster objects (i.e. a `RasterStack` object) where each layer corresponds to a different feature (e.g. a multi-band GeoTIFF where each band corresponds to a different feature). The pixel values in each layer correspond to the amount of suitable habitat available in a given planning unit. Note that our planning unit raster layer and our conservation feature stack have exactly the same spatial properties (i.e. resolution, extent, coordinate reference system) so their pixels line up perfectly. +# load planning unit data +data(tas_pu) -```{r, fig.width = 4, fig.height = 3} # load feature data -data(sim_features) - -# plot the distribution of suitable habitat for each feature -plot(sim_features, main = paste("Feature", seq_len(nlayers(sim_features))), - nr = 2, box = FALSE, axes = FALSE) -``` - -### Initialize a problem - -After having loaded our planning unit and feature data, we will now try initializing the some conservation planning problems. There are a lot of different ways to initialize a conservation planning problem, so here we will just showcase a few of the more commonly used methods. For an exhaustive description of all the ways you can initialize a conservation problem, see the help file for the `problem` function (which you can open using the code `?problem`). First off, we will initialize a conservation planning problem using the raster data. - -```{r} -# create problem -p1 <- problem(sim_pu_raster, sim_features) - -# print problem -print(p1) - -# print number of planning units -number_of_planning_units(p1) - -# print number of features -number_of_features(p1) -``` - -Generally, we recommend initializing problems using raster data where possible. This is because the `problem` function needs to calculate the amount of each feature in each planning unit, and by providing both the planning unit and feature data in raster format with the same spatial resolution, extents, and coordinate systems, this means that the `problem` function does not need to do any geo-processing behind the scenes. But sometimes we can't use raster planning unit data because our planning units aren't equal-sized grid cells. So, below is an example showing how we can initialize a conservation planning problem using planning units that are formatted as spatial vector data. Note that if we had pre-computed the amount of each feature in each planning unit and stored the data in the attribute table, we could pass in the names of the columns as an argument to the `problem` function. - -```{r} -# create problem with spatial vector data -# note that we have to specify which column in the attribute table contains -# the cost data -p2 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") - -# print problem -print(p2) -``` - -We can also initialize a conservation planning problem using tabular planning unit data (i.e. a `data.frame`). Since the tabular planning unit data does not contain any spatial information, we also have to provide the feature data in tabular format (i.e. a `data.frame`) and data showing the amount of each feature in each planning unit in tabular format (i.e. a `data.frame`). The feature data must have an "id" column containing a unique integer identifier for each feature, and the planning unit by feature data must contain the following three columns: "pu" corresponding to the planning unit identifiers, "species" corresponding to the feature identifiers, and "amount" showing the amount of a given feature in a given planning unit. - -```{r} -# set file path for feature data -spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr") - -# load in feature data -spec_dat <- data.table::fread(spec_path, data.table = FALSE) - -# print first six rows of the data -# note that it contains extra columns -head(spec_dat) - -# set file path for planning unit vs. feature data -puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr") - -# load in planning unit vs feature data -puvspr_dat <- data.table::fread(puvspr_path, data.table = FALSE) - -# print first six rows of the data -head(puvspr_dat) - -# create problem -p3 <- problem(pu_dat, spec_dat, cost_column = "cost", rij = puvspr_dat) - -# print problem -print(p3) +data(tas_features) ``` -For more information on initializing problems, please see the help page for the `problem` function (which you can open using the code `?problem`). Now that we have initialized a conservation planning problem, we will show you how you can customize it to suit the exact needs of your conservation planning scenario. Although we initialized the conservation planning problems using several different methods, moving forward, we will only use raster-based planning unit data to keep things simple. +The `tas_pu` object contains planning units represented as spatial polygons (i.e., a `SpatialPolygonsDataFrame` object). This object has three columns that denote the following information for each planning unit: a unique identifier (`id`), unimproved land value (`cost`), and current conservation status (`locked_in`). Planning units that have at least half of their area overlapping with existing protected areas are denoted with a locked in value of 1, otherwise they are denoted with a value of 0. If you are familiar with the _Marxan_ decision support tool, then you will notice that some of these columns are formatted similar conventions. -### Add an objective +Now, let's have a look at the planning unit data. We can see that the planning units correspond to hexagonal land parcels. We can also see that is a clear spatial pattern in the cost and conservation status the planning units. -The next step is to add an objective to the problem. A problem objective is used to specify the primary goal of the problem (i.e. the quantity that is to be maximized or minimized). All conservation planning problems involve minimizing or maximizing some kind of objective. For instance, we might require a solution that conserves enough habitat for each species while minimizing the overall cost of the reserve network. Alternatively, we might require a solution that maximizes the number of conserved species while ensuring that the cost of the reserve network does not exceed the budget. Please note that objectives are added in the same way regardless of the type of data used to initialize the problem. +```{r, fig.width = w, fig.height = h} +# print planning unit data +print(tas_pu) -The _prioritizr R_ package supports a variety of different objective functions. +# plot map of planning unit costs +plot(st_as_sf(tas_pu[, "cost"]), main = "Planning unit costs") -* __Minimum set objective__: Minimize the cost of the solution whilst ensuring that all targets are met [@r11]. This objective is similar to that used in _Marxan_ [@r3]. For example, we can add a minimum set objective to a problem using the following code. -```{r} -# create a new problem that has the minimum set objective -p3 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() - -# print the problem -print(p3) +# plot map of planning unit coverage by protected areas +plot(st_as_sf(tas_pu[, "locked_in"]), main = "Protected area coverage") ``` -* __Maximum cover objective__: Represent at least one instance of as many features as possible within a given budget [@r12]. -```{r} -# create a new problem that has the maximum coverage objective and a budget -# of 5000 -p4 <- problem(sim_pu_raster, sim_features) %>% - add_max_cover_objective(5000) -# print the problem -print(p4) -``` -* __Maximum features objective__: Fulfill as many targets as possible while ensuring that the cost of the solution does not exceed a budget [inspired by @r10]. This object is similar to the maximum cover objective except that we have the option of later specifying targets for each feature. In practice, this objective is more useful than the maximum cover objective because features often require a certain amount of area for them to persist and simply capturing a single instance of habitat for each feature is generally unlikely to enhance their long-term persistence. -```{r} -# create a new problem that has the maximum features objective and a budget -# of 5000 -p5 <- problem(sim_pu_raster, sim_features) %>% - add_max_features_objective(budget = 5000) +The `tas_features` object describes the spatial distribution of the features. Specifically, the feature data are expressed as a stack of `r raster::nlayers(tas_features)` rasters (i.e., a `RasterStack` object). Each layer in the stack corresponds to one of `r raster::nlayers(tas_features)` different vegetation communities. To describe the spatial distribution of a given vegetation community, each layer contains a spatially referenced grid of rectangular cells and each of these grid cells is associated with information on the distribution of the a given vegetation community. Specifically, these grid cells are assigned values that indicate if a given vegetation community is present (using value of 1) or absent (using value of 0) within the spatial extent of each grid cell. -# print the problem -print(p5) -``` -* __Minimum shortfall objective__: Minimize the overall (weighted sum) shortfall for as many targets as possible while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when there is a large amount of left-over budget when using the maximum feature representation objective and the remaining funds need to be allocated to places that will enhance the representation of features with unmet targets. -```{r} -# create a new problem that has the minimum shortfall objective and a budget -# of 5000 -p6 <- problem(sim_pu_raster, sim_features) %>% - add_min_shortfall_objective(budget = 5000) +Next, let's examine the feature data. Here we will only plot the first four features as an example. The pixel values denote the presence (denoted by a value of 1) or absence (denoted by a value of zero) of each feature within the extent of the study area. -# print the problem -print(p6) -``` -* __Minimum largest shortfall objective__: Minimize the largest (maximum) shortfall while ensuring that the cost of the solution does not exceed a budget. In practice, this objective useful when the minimum shortfall objective returns solutions that focus too much on representing a small number of features (e.g. because they occur in much cheaper planning units), and solutions are needed to spread conservation effort out more evenly amongst all features---even if it means that all features will have (relatively) poor representation. -```{r} -# create a new problem that has the minimum largest shortfall objective and a -# budget of 5000 -p7 <- problem(sim_pu_raster, sim_features) %>% - add_min_largest_shortfall_objective(budget = 5000) +```{r, fig.width = 4.5, fig.height = 4.5} +# print planning unit data +print(tas_features) -# print the problem -print(p7) +# plot map of the first four vegetation classes +plot(tas_features[[1:4]], main = paste("Feature", 1:4)) ``` -* __Maximum phylogenetic diversity objective__: Maximize the phylogenetic diversity of the features represented in the solution subject to a budget [inspired by @r13; @r14]. This objective is similar to the maximum features objective except that emphasis is placed on protecting features which are associated with a diverse range of evolutionary histories. The _prioritizr R_ package contains a simulated phylogeny that can be used with the simulated feature data (`sim_phylogny`). -```{r} -# load simulated phylogeny data -data(sim_phylogeny) -# create a new problem that has the maximum phylogenetic diversity -# objective and a budget of 5000 -p8 <- problem(sim_pu_raster, sim_features) %>% - add_max_phylo_div_objective(budget = 5000, tree = sim_phylogeny) +The planning units in this tutorial are stored as spatial polygons. Although spatial polygons provide considerable flexibility in the shape and size of the planning units, such flexibility comes at a cost. This is because the spatial data processing routines needed to combine spatial polygon data and raster data for optimization can be very computationally expensive (e.g., calculating zonal statistics). As a consequence, we generally recommend using raster-based planning unit data where possible to reduce processing time. Another strategy is to complete spatial data processing routines manually using other software (e.g., _ESRI ArcGIS_) and use the pre-processed data directly with the _prioritizr R_ package. -# print the problem -print(p8) -``` -* __Maximum phylogenetic endemism objective__: Maximize the phylogenetic endemism of the features represented in the solution subject to a budget [inspired by @r13; @r14; @r32]. This objective is similar to the maximum phylogenetic diversity except that emphasis is placed conserving features that are associated with geographically restricted periods of evolutionary history rather than a diverse range of evolutionary histories. -```{r} -# load simulated phylogeny data -data(sim_phylogeny) +## Problem formulation -# create a new problem that has the maximum phylogenetic diversity -# objective and a budget of 5000 -p9 <- problem(sim_pu_raster, sim_features) %>% - add_max_phylo_end_objective(budget = 5000, tree = sim_phylogeny) +Now we will formulate a conservation planing problem. To achieve this, we first specify which objects contain the planning unit and feature data (using the `problem()` function). Next, we specify that we want to use the minimum set objective function (using the `add_min_set_objective()` function). This objective function indicates that we wish to minimize the total cost of planning units selected by the prioritization. We then specify boundary penalties reduce spatial fragmentation in the resulting prioritization (using the `add_boundary_penalties()` function; see the [_Calibrating trade-offs_ vignette](calibrating_trade-offs.html) for details on calibrating the penalty value). We also specify representation targets to ensure the resulting prioritization provides adequate coverage of each vegetation community (using the `add_relative_targets()` function). Specifically, we specify targets to ensure at least 17% of the spatial extent of each vegetation community (based on the [Aichi Target 11](https://www.cbd.int/sp/targets/)). Additionally, we set constraints to ensure that planning units predominately covered by existing protected areas are selected by the prioritization (using the `add_locked_in_constraints()` function). Finally, we specify that the prioritization should either select -- or not select -- planning units for prioritization (using the `add_binary_decisions()` function). -# print the problem -print(p9) -``` -* __Maximum utility objective__: Secure as much of the features as possible without exceeding a budget. This objective is functionally equivalent to selecting the planning units with the greatest amounts of each feature (e.g. species richness). Generally, we don't encourage the use of this objective because it will only rarely identify complementary solutions---solutions which adequately conserve a range of different features---except perhaps to explore trade-offs or provide a baseline solution with which to compare other solutions. -```{r} -# create a new problem that has the maximum utility objective and a budget -# of 5000 -p10 <- problem(sim_pu_raster, sim_features) %>% - add_max_utility_objective(budget = 5000) +```{r, fig.width = w, fig.height = h} +# build problem +p1 <- problem(tas_pu, tas_features, cost_column = "cost") %>% + add_min_set_objective() %>% + add_boundary_penalties(penalty = 0.005) %>% + add_relative_targets(0.17) %>% + add_locked_in_constraints("locked_in") %>% + add_binary_decisions() # print the problem -print(p10) -``` - -### Add targets - -Most conservation planning problems require targets. Targets are used to specify the minimum amount or proportion of a feature's distribution that needs to be protected in the solution. For example, we may want to develop a reserve network that will secure 20% of the distribution for each feature for minimal cost. - -There are four ways for specifying targets in the _prioritizr R_ package: - -* __Absolute targets__: Targets are expressed as the total amount of each feature in the study area that need to be secured. For example, if we had binary feature data that showed the absence or presence of suitable habitat across the study area, we could set an absolute target as 5 to mean that we require 5 planning units with suitable habitat in the solution. -```{r} -# create a problem with targets which specify that the solution must conserve -# a need a sum total of 3 units of suitable habitat for each feature -p11 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_absolute_targets(3) - -# print problem -print(p11) -``` -* __Relative targets__: Targets are set as a proportion (between 0 and 1) of the total amount of each feature in the study area. For example, if we had binary feature data and the feature occupied a total of 20 planning units in the study area, we could set a relative target of 50 % to specify that the solution must secure 10 planning units for the feature. We could alternatively specify an absolute target of 10 to achieve the same result, but sometimes proportions are easier to work with. -```{r} -# create a problem with the minimum set objective and relative targets of 10 % -# for each feature -p12 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) - -# print problem -print(p12) - -# create a problem with targets which specify that we need 10 % of the habitat -# for the first feature, 15 % for the second feature, 20 % for the third feature -# 25 % for the fourth feature and 30 % of the habitat for the fifth feature -targets <- c(0.1, 0.15, 0.2, 0.25, 0.3) -p13 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(targets) - -# print problem -print(p13) -``` -* __Log-linear targets__: Targets are expressed using scaling factors and log-linear interpolation. This method for specifying targets is commonly used for global prioritization analyses [@r15]. -```{r} -# create problem with added log-linear targets -p14 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_loglinear_targets(10, 0.9, 100, 0.2) - -# print problem -print(p14) -``` -* __Manual targets__: Targets are manually specified. This is only really recommended for advanced users or problems that involve multiple management zones. See the [zones vignette](zones.html) for more information on these targets. - -As with the functions for specifying the objective of a problem, if we try adding multiple targets to a problem, only the most recently added set of targets are used. - -### Add constraints - -A constraint can be added to a conservation planning problem to ensure that all solutions exhibit a specific property. For example, they can be used to make sure that all solutions select a specific planning unit or that all selected planning units in the solution follow a certain configuration. - -The following constraints can be added to conservation planning problems in the _prioritizr R_ package. - -* __Locked in constraints__: Add constraints to ensure that certain planning units are prioritized in the solution. For example, it may be desirable to lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network. -```{r} -# create problem with constraints which specify that the first planning unit -# must be selected in the solution -p15 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints(1) - -# print problem -print(p15) -``` -* __Locked out constraints__: Add constraints to ensure that certain planning units are not prioritized in the solution. For example, it may be useful to lock out planning units that have been degraded and are not suitable for conserving species. -```{r} -# create problem with constraints which specify that the second planning unit -# must not be selected in the solution -p16 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_out_constraints(2) - -# print problem -print(p16) -``` -* __Neighbor constraints__: Add constraints to a conservation problem to ensure that all selected planning units have at least a certain number of neighbors. -```{r} -# create problem with constraints which specify that all selected planning units -# in the solution must have at least 1 neighbor -p17 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_neighbor_constraints(1) - -# print problem -print(p17) -``` -* __Contiguity constraints__: Add constraints to a conservation problem to ensure that all selected planning units are spatially connected to each other and form spatially contiguous unit. -```{r} -# create problem with constraints which specify that all selected planning units -# in the solution must form a single contiguous unit -p18 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_contiguity_constraints() - -# print problem -print(p18) -``` -* __Feature contiguity constraints__: Add constraints to ensure that each feature is represented in a contiguous unit of dispersible habitat. These constraints are a more advanced version of those implemented in the `add_contiguity_constraints` function, because they ensure that each feature is represented in a contiguous unit and not that the entire solution should form a contiguous unit. -```{r} -# create problem with constraints which specify that the planning units used -# to conserve each feature must form a contiguous unit -p19 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_feature_contiguity_constraints() - -# print problem -print(p19) -``` - -* __Linear constraints__: Add constraints to ensure that all selected planning units meet certain criteria. For example, they can be used to add multiple budgets, or limit the number of planning units selected in different administrative areas within a study region (e.g. different countries). - -```{r} -# create problem with constraints which specify that the sum of -# values in sim_features[[1]] among selected planning units must not exceed a -#' threshold value of 190. -p20 <- problem(sim_pu_raster, sim_features) %>% - add_min_shortfall_objective(budget = 1800) %>% - add_relative_targets(0.1) %>% - add_linear_constraints(190, "<=", sim_features[[1]]) - -# print problem -print(p20) +print(p1) ``` -* __Mandatory allocation constraints__: Add constraints to ensure that every planning unit is allocated to a management zone in the solution. Please note that this function can only be used with problems that contain multiple zones. For more information on problems with multiple zones and an example using this function, see the Management Zones vignette. +## Prioritization -In particular, The `add_locked_in_constraints` and `add_locked_out_constraints` functions are incredibly useful for real-world conservation planning exercises, so it's worth pointing out that there are several ways we can specify which planning units should be locked in or out of the solutions. If we use raster planning unit data, we can also use raster data to specify which planning units should be locked in our locked out. +We can now solve the problem formulation (`p1`) to generate a prioritization (using the `solve()` function). The _prioritizr_ R package supports a range of different exact algorithm solvers, including _Gurobi_, _IBM CPLEX_, _CBC_, _Rsymphony_, and _lpsymphony_. Although there are benefits and limitations associated with each of these different solvers, they should return similar results. Note that you will need at least one solver installed on your system to generate prioritizations. Since we did not specify a solver when building the problem, the _prioritizr R_ package will automatically select the best available solver installed. We recommend using the _Gurobi_ solver if possible, and have used it for this tutorial (see the _Gurobi Installation Guide_ vignette for installation instructions). After solving the problem, the prioritization will be stored in the `solution_1` column of the `s1` object. -```{r, fig.width = 6.5, fig.height = 2.5} -# load data to lock in or lock out planning units -data(sim_locked_in_raster) -data(sim_locked_out_raster) +```{r, fig.width = w, fig.height = h} +# solve problem +s1 <- solve(p1) -# plot the locked data -plot(stack(sim_locked_in_raster, sim_locked_out_raster), - main = c("Locked In", "Locked Out")) - -# create a problem using raster planning unit data and use the locked raster -# data to lock in some planning units and lock out some other planning units -p21 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints(sim_locked_in_raster) %>% - add_locked_out_constraints(sim_locked_out_raster) - -# print problem -print(p21) +# plot map of prioritization +plot(st_as_sf(s1[, "solution_1"]), main = "Prioritization", + pal = c("grey90", "darkgreen")) ``` -If our planning unit data are in a spatial vector format (similar to the `sim_pu_polygons` data) or a tabular format (similar to `pu_dat`), we can use the field names in the data to refer to which planning units should be locked in and / or out. For example, the `sim_pu_polygons` object has `TRUE` / `FALSE` values in the "locked_in" field which indicate which planning units should be selected in the solution. We could use the data in this field to specify that those planning units with `TRUE` values should be locked in using the following methods. - -```{r} -# preview first six rows of the attribute table for sim_pu_polygons -head(sim_pu_polygons@data) +## Feature representation -# specify locked in data using the field name -p22 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints("locked_in") +Let's examine how well the vegetation communities are represented by existing protected areas and the prioritization. -# print problem -print(p22) +```{r, fig.width = 7} +# create column with existing protected areas +tas_pu$pa <- round(tas_pu$locked_in) -# specify locked in data using the values in the field -p23 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_locked_in_constraints(which(sim_pu_polygons$locked_in)) +# calculate feature representation statistics based on existing protected areas +tc_pa <- eval_target_coverage_summary(p1, tas_pu[, "pa"]) +print(tc_pa) -# print problem -print(p23) -``` +# calculate feature representation statistics based on the prioritization +tc_s1 <- eval_target_coverage_summary(p1, s1[, "solution_1"]) +print(tc_s1) -### Add penalties +# explore representation by existing protected areas +## calculate number of features adequately represented by existing protected +## areas +sum(tc_pa$met) -We can also add penalties to a problem to favor or penalize solutions according to a secondary objective. Unlike the constraint functions, these functions will add extra information to the objective function of the optimization function to penalize solutions that do not exhibit specific characteristics. For example, penalties can be added to a problem to avoid highly fragmented solutions at the expense of accepting slightly more expensive solutions. All penalty functions have a `penalty` argument that controls the relative importance of the secondary penalty function compared to the primary objective function. It is worth noting that incredibly low or incredibly high `penalty` values---relative to the main objective function---can cause problems to take a very long time to solve, so when trying out a range of different penalty values it can be helpful to limit the solver to run for a set period of time. +## summarize representation (values show percent coverage) +summary(tc_pa$relative_held * 100) -The _prioritizr R_ package currently offers only two methods for adding penalties to a conservation planning problem. +## visualize representation (values show percent coverage) +hist(tc_pa$relative_held * 100, + main = "Feature representation by existing protected areas", + xlim = c(0, 100), + xlab = "Percent coverage of features (%)") -* __Boundary penalties__: Add penalties to penalize solutions that are excessively fragmented. These penalties are similar to those used in _Marxan_ [@r3; @r1]. -```{r} -# create problem with penalties that penalize fragmented solutions with a -# penalty factor of 0.01 -p24 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_boundary_penalties(penalty = 0.01) -# print problem -print(p24) -``` -* __Connectivity penalties__: Add penalties to favor solutions that select combinations of planning units with high connectivity between them. These penalties are similar to those used in _Marxan with Zones_ [@r2; @r1]. This function supports both symmetric and asymmetric connectivities among planning units. -```{r} -# create problem with penalties that favor combinations of planning units with -# high connectivity, here we will use only the first four layers in -# sim_features for the features and we will use the fifth layer in sim_features -# to represent the connectivity data, where the connectivity_matrix function -# will create a matrix showing the average strength of connectivity between -# adjacent planning units using the data in the fifth layer of sim_features -p25 <- problem(sim_pu_raster, sim_features[[1:4]]) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_boundary_penalties( - penalty = 5, - data = connectivity_matrix(sim_pu_raster, sim_features[[5]])) +# explore representation by prioritization +## summarize representation (values show percent coverage) +summary(tc_s1$relative_held * 100) -# print problem -print(p25) -``` -* __Linear penalties__: Add penalties to penalize solutions that select planning units according to a certain variable (e.g. anthropogenic pressure). -```{r} -# create data for penalizing planning units -# (note this requires the RandomFields package to be installed) -pen_raster <- simulate_cost(sim_pu_raster) - -# create problem with penalties that penalize solutions that select -# planning units with high values in the pen_raster object, -# here we will use a penalty value of 5 to indicate the trade-off (scaling) -# between the penalty values (in the sim_pu_raster) and the main objective -# (i.e. the cost of the solution) -p26 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_linear_penalties(penalty = 5, data = pen_raster) +## calculate number of features adequately represented by the prioritization +sum(tc_s1$met) -# print problem -print(p26) +## visualize representation (values show percent coverage) +hist(tc_s1$relative_held * 100, + main = "Feature representation by prioritization", + xlim = c(0, 100), + xlab = "Percent coverage of features (%)") ``` -### Add the decision types +We can see that representation of the vegetation communities by existing protected areas is remarkably poor. For example, many of the vegetation communities have nearly zero coverage by existing protected areas. In other words, are almost entirely absent from existing protected areas. We can also see that all vegetation communities have at least 17% coverage by the prioritization -- meaning that it meets the representation targets for all of the features. -Conservation planning problems involve making decisions on how planning units will be managed. These decisions are then associated with management actions (e.g. turning a planning unit into a protected area). The type of decision describes how the action is applied to planning units. For instance, the default decision-type is a binary decision type, meaning that we are either selecting or not selecting planning units for management. +## Irreplaceability -The _prioritizr R_ package currently offers the following types of decisions for customizing problems. +After generating the prioritization, we can examine the relative importance of planning units selected by the prioritization. This can be useful to identify critically important planning units for conservation -- in other words, places that contain biodiversity features which cannot be represented anywhere else -- and schedule implementation of the prioritization. To achieve this, we will use the Ferrier metric [@r34]. -* __Binary decisions__: Add a binary decision to a conservation planning problem. This is the classic decision of either prioritizing or not prioritizing a planning unit. Typically, this decision has the assumed action of buying the planning unit to include in a protected area network. If no decision is added to a problem object, then this decision class will be used by default. -```{r} -# add binary decisions to a problem -p27 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() - -# print problem -print(p27) -``` -* __Proportion decisions__: Add a proportion decision to a problem. This is a relaxed decision where a part of a planning unit can be prioritized, as opposed to the default of the entire planning unit. Typically, this decision has the assumed action of buying a fraction of a planning unit to include in a protected area network. Generally, problems can be solved much faster with proportion-type decisions than binary-type decisions, so they can be very useful when commercial solvers are not available. -```{r} -# add proportion decisions to a problem -p28 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_proportion_decisions() +```{r, fig.width = w, fig.height = h} +# calculate irreplaceability +irrep_s1 <- eval_ferrier_importance(p1, s1["solution_1"]) +print(irrep_s1) -# print problem -print(p28) -``` -* __Semi-continuous decisions__: Add a semi-continuous decision to a problem. This decision is similar to proportion decisions except that it has an upper bound parameter. By default, the decision can range from prioritizing none (0%) to all (100%) of a planning unit. However, a upper bound can be specified to ensure that at most only a fraction (e.g. 80%) of a planning unit can be purchased. This type of decision may be useful when it is not practical to conserve the entire area indicated by a planning unit. -```{r} -# add semi-continuous decisions to a problem, where we can only manage at most -# 50 % of the area encompassed by a planning unit -p29 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_semicontinuous_decisions(0.5) +# manually coerce values for planning units not selected in prioritization +# to NA, so that they are shown in white +irrep_s1$plot_total <- irrep_s1$total +irrep_s1$plot_total[s1$solution_1 < 0.5] <- NA_real_ -# print problem -print(p29) +# plot map of overall importance scores +plot(st_as_sf(irrep_s1[, "plot_total"]), main = "Overall importance") ``` -### Add a solver - -Next, after specifying the mathematical formulation that underpins your conservation planning problem, you can specify how the problem should be solved. If you do not specify this information, the _prioritizr R_ package will automatically use the best solver currently installed on your system with some reasonable defaults. __We strongly recommend installing the [Gurobi software suite and the _gurobi_ _R_ package](https://www.gurobi.com/) to solve problems, and for more information on this topic please refer to the [Gurobi Installation Guide](gurobi_installation.html)__. - -Currently, the _prioritizr R_ package supports five different solvers. - -* __*Gurobi* solver__: [_Gurobi_](https://www.gurobi.com/) is a state of the art commercial optimization software. It is by far the fastest of the solvers that can be used to solve conservation problems. However, it is not freely available. That said, special licenses are available to academics at no cost. -```{r} -# create a problem and specify that Gurobi should be used to solve the problem -# and specify an optimality gap of zero to obtain the optimal solution -p30 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_gurobi_solver(gap = 0) - -# print problem -print(p30) -``` -* __*IBM CPLEX* solver__: [_IBM CPLEX_](https://www.ibm.com/analytics/cplex-optimizer) is a commercial optimization software. It is faster than the open source solvers available for generating prioritizations (see below), however, it is not freely available. Similar to the _Gurobi_ software, special licenses are available to academics at no cost. -```{r} -# create a problem and specify that IBM CPLEX should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p31 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_cplex_solver(gap = 0) +## Portfolios -# print problem -print(p31) -``` -* __*CBC* solver__: [*CBC*](https://projects.coin-or.org/Cbc) is an -open-source mixed integer programming solver that is part of the Computational Infrastructure for Operations Research (COIN-OR) project. Although formal benchmarks have yet to be completed, preliminary analyses suggest that it is -the fastest open-source solver available for generating prioritizations. It requires the _rcbc R_ package, which is currently only available on [GitHub](https://github.com/dirkschumacher/rcbc). -```{r} -# create a problem and specify that CBC should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p32 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_cbc_solver(gap = 0) +Conservation planning exercises often involve generating multiple different prioritizations. This can help decision makers consider different options, and provide starting points for building consensus among stakeholders. To generate a range of different prioritizations given the same problem formulation, we can use portfolio functions. Here we will use the gap portfolio to generate 1000 solutions that are within 30% of optimality. Please note that you will need to have the *Gurobi* solver installed to use this specific portfolio. If you don'thave access to *Gurobi*, you could try using the shuffle portfolio instead (using the `add_shuffle_portfolio()` function). -# print problem -print(p32) -``` -* __*lpsymphony* solver__: [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) is an open-source integer programming solver that is also part of the COIN-OR project. This solver uses the _lpsymphony R_ package (available on [Bioconductor](http://bioconductor.org/packages/release/bioc/html/lpsymphony.html)) to interface with the _SYMPHONY_ software. ```{r} -# create a problem and specify that lpsymphony should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p33 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_lpsymphony_solver(gap = 0) +# create new problem with a portfolio added to it +p2 <- p1 %>% + add_gap_portfolio(number_solutions = 1000, pool_gap = 0.2) # print problem -print(p33) -``` -* __*Rsymphony* solver__: This solver provides a different interface to the [_SYMPHONY_](https://projects.coin-or.org/SYMPHONY) software. It uses the _Rsymphony R_ package which is available on The Comprehensive R Archive Network (CRAN). This solver is generally slower than the other solvers, because it cannot use parallel processing. -```{r} -# create a problem and specify that Rsymphony should be used to solve the -# problem and specify an optimality gap of zero to obtain the optimal solution -p34 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_rsymphony_solver(gap = 0) +print(p2) -# print problem -print(p34) +# generate prioritizations +prt <- solve(p2) +print(prt) ``` -### Add a portfolio - -Many conservation planning exercises require a portfolio of solutions. For example, real-world exercises can involve presenting decision makers with a range of near-optimal decisions. Additionally, the number of times that different planning units are selected in different solutions can provide insight into their relative importance. - -The following methods are available for generating a portfolio of solutions. - -* __Extra portfolio__: Generate a portfolio of solutions by storing feasible solutions found during the optimization process. Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. -```{r} -# create a problem and specify that a portfolio should be created using -# extra solutions found while solving the problem -p35 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_extra_portfolio() - -# print problem -print(p35) -``` -* __Top portfolio__: Generate a portfolio of solutions by finding a pre-specified number of solutions that are closest to optimality (i.e the top solutions). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. -```{r} -# create a problem and specify that a portfolio should be created using -# the top five solutions -p36 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_top_portfolio(number_solutions = 5) +After generating all these prioritizations, we now want some way to visualize them. Because it would be onerous to look at each and every prioritization individually, we will use statistical analyses to help us. We can visualize the differences between these different prioritizations -- based on which planning units they selected -- using a hierarchical cluster analysis [@r35]. -# print problem -print(p36) -``` -* __Gap portfolio__: Generate a portfolio of solutions by finding a certain number of solutions that are all within a pre-specified optimality gap. This method is especially useful for generating multiple solutions that can used be to calculate selection frequencies (similar to _Marxan_). Note that this method requires that the _Gurobi_ optimization software is used to generate solutions. -```{r} -# create a problem and specify that a portfolio should be created by -# finding five solutions within 10% of optimality -p37 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_gap_portfolio(number_solutions = 5, pool_gap = 0.2) +```{r, fig.height = 4.5, fig.width = 7, fig.show = "hold"} +# extract solutions +prt_results <- prt@data[, startsWith(names(prt), "solution_"), ] -# print problem -print(p37) -``` -* __Cuts portfolio__: Generate a portfolio of distinct solutions within a pre-specified optimality gap. This method is only recommended if the _Gurobi_ optimization solver is not available. -```{r} -# create a problem and specify that a portfolio containing 10 solutions -# should be created using using Bender's cuts -p38 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_cuts_portfolio(number_solutions = 10) +# calculate pair-wise distances between different prioritizations for analysis +prt_dists <- vegan::vegdist(t(prt_results), method = "jaccard", binary = TRUE) -# print problem -print(p38) -``` -* __Shuffle portfolio__: Generate a portfolio of solutions by randomly reordering the data prior to attempting to solve the problem. If the _Gurobi_ optimization solver is not available, this method is the fastest method for generating a set number of solutions within a specified distance from optimality. -```{r} -# create a problem and specify a portfolio should be created that contains -# 10 solutions and that any duplicate solutions should not be removed -p39 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() %>% - add_shuffle_portfolio(number_solutions = 10, remove_duplicates = FALSE) +# run cluster analysis +prt_clust <- hclust(as.dist(prt_dists), method = "average") -# print problem -print(p39) +# visualize clusters +opar <- par() +par(oma = c(0, 0, 0, 0), mar= c(0, 4.1, 1.5, 2.1)) +plot(prt_clust, labels = FALSE, sub = NA, xlab = "", + main = "Different prioritizations in portfolio") +suppressWarnings(par(opar)) ``` -### Solve the problem +We can see that there are approximately six main groups of prioritizations in the portfolio. To explore these different groups, let's conduct another cluster analysis (i.e., a _k_-medoids analysis) to extract the most representative prioritization from each of these groups. In other words, we will run another statistical analysis to find the most central prioritization within each group. -Finally, after formulating our conservation planning problem and specifying how the problem should be solved, we can use the `solve` function to obtain a solution. Note that the solver will typically print out some information describing the size of the problem and report its progress when searching for a suitable solution. +```{r, fig.width = 7, fig.height = 5} +# run k-medoids analysis +prt_med <- pam(prt_dists, k = 6) -```{r, fig.height = h, fig.width = w} -# formulate the problem -p40 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_boundary_penalties(penalty = 500, edge_factor = 0.5) %>% - add_binary_decisions() +# extract names of prioritizations that are most central for each group. +prt_med_names <- prt_med$medoids +print(prt_med_names) -# solve the problem (using the default solver) -s40 <- solve(p40) +# create a copy of prt and set values for locked in planning units to -1 +# so we can easily visualize differences between prioritizations +prt2 <- prt[, prt_med_names] +prt2@data[which(tas_pu$locked_in > 0.5), prt_med_names] <- -1 -# plot solution -plot(s40, col = c("grey90", "darkgreen"), main = "Solution", - xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) +# plot a map showing main different prioritizations +# dark grey: locked in planning units +# grey: planning units not selected +# green: selected planning units +plot(st_as_sf(prt2), pal = c("grey60", "grey90", "darkgreen")) ``` -We can plot this solution because the planning unit input data are spatially referenced in a raster format. The output format will always match the planning unit data used to initialize the problem. For example, the solution to a problem with planning units in a spatial vector (shapefile) format would also be in a spatial vector format. Similarly, if the planning units were in a tabular format (i.e. `data.frame`), the solution would also be returned in a tabular format. - -We can also extract attributes from the solution that describe the quality of the solution and the optimization process. - -```{r} -# extract the objective (numerical value being minimized or maximized) -print(attr(s40, "objective")) - -# extract time spent solving solution -print(attr(s40, "runtime")) +## _Marxan_ compatibility -# extract state message from the solver that describes why this specific -# solution was returned -print(attr(s40, "status")) -``` - -### Evaluate the performance of a solution +The _prioritizr R_ package provides functionality to help _Marxan_ users generate prioritizations. Specifically, it can import conservation planning data prepared for _Marxan_, and can generate prioritizations using a similar problem formulation as _Marxan_ [based on @r1]. Indeed, the problem formulation presented earlier in this vignette is very similar to that used by _Marxan_. The key difference is that the problem formulation we specified earlier uses "hard constraints" for feature representation, and _Marxan_ uses "soft constraints" for feature representation. This means that prioritization we generated earlier was mathematically guaranteed to reach the targets for all features. However, if we used _Marxan_ to generate the prioritization, then we could have produced a prioritization that would fail to reach targets (depending the _Species Penalty Factors_ used to generate the prioritization). In addition to these differences in terms problem formulation, the _prioritizr R_ package uses exact algorithms -- instead of the simulated annealing algorithm -- which ensures that we obtain prioritizations that are near optimal. -After obtaining a solution to a conservation planning problem, it can be useful to calculate various summary statistics to understand its performance. The following functions are available to summarize a solution: +Here we will show the _prioritizr R_ package can import _Marxan_ data and generate a prioritization. To begin with, let's import a conservation planning data prepared for _Marxan_. -* Calculate the number of planning units selected within a solution. -```{r} -# calculate statistic -eval_n_summary(p40, s40) -``` -* Calculate the total cost of a solution. -```{r} -# calculate statistic -eval_cost_summary(p40, s40) -``` -* Calculate how well features are represented by a solution. This function can be used for problems that are built using targets and those that are not built using targets. -```{r} -# calculate statistics -eval_feature_representation_summary(p40, s40) -``` -* Calculate how well feature representation targets are met by a solution. This function can only be used with problems contain targets. ```{r} -# calculate statistics -eval_target_coverage_summary(p40, s40) -``` -* Calculate the exposed boundary length (perimeter) associated with a solution. -```{r} -# calculate statistic -eval_boundary_summary(p40, s40) -``` -* Calculate the connectivity held within a solution. -```{r} -# calculate statistic -# here we use the raster data for the first feature as an example -# to parametrize pair-wise connectivity between different planning units -eval_connectivity_summary( - p40, s40, data = connectivity_matrix(sim_pu_raster, sim_features[[1]])) -``` - -### _Marxan_ problems - -Although users are encouraged to build and tailor conservation planning problems to suit their own needs using the `problem` function, sometimes it just simply easier to use a more familiar formulation. The `marxan_problem` function is provided as a convenient wrapper for building and solving _Marxan_-style conservation problems. If users already have their conservation planning data formatted for use with _Marxan_, this function can also read _Marxan_ data files and solve the _Marxan_-style problems using exact algorithm solvers. __Please note that problems built using the `marxan_problem` function are still solved the same way as a problem initialized using the `problem` function, and therefore still require the installation of one of the solver packages.__ - -Here is a short example showing how the `marxan_problem` function can be used to read _Marxan_ input files and the `solve` function can be used to solve the problem. -```{r, results = "hide"} -# set file path for Marxan input file -minput <- system.file("extdata/input.dat", package = "prioritizr") - -# read Marxan input file -mp <- marxan_problem(minput) +# import data +## planning unit data +pu_path <- system.file("extdata/input/pu.dat", package = "prioritizr") +pu_data <- read.csv(pu_path, header = TRUE, stringsAsFactors = FALSE) +print(head(pu_data)) -# print problem -print(mp) +## feature data +spec_path <- system.file("extdata/input/spec.dat", package = "prioritizr") +spec_data <- read.csv(spec_path, header = TRUE, stringsAsFactors = FALSE) +print(head(spec_data)) -# solve the problem -ms <- solve(mp) +## amount of each feature within each planning unit data +puvspr_path <- system.file("extdata/input/puvspr.dat", package = "prioritizr") +puvspr_data <- read.csv(puvspr_path, header = TRUE, stringsAsFactors = FALSE) +print(head(puvspr_data)) -# since the Marxan data was in a tabular format, the solution is also returned -# in a tabular format, so we will print the first six rows of the table -# containing the solution -head(ms) +## boundary data +bound_path <- system.file("extdata/input/bound.dat", package = "prioritizr") +bound_data <- read.table(bound_path, header = TRUE, stringsAsFactors = FALSE) +print(head(bound_data)) ``` -Alternatively, rather then using a _Marxan_ input file to construct the problem, we can manually read in the _Marxan_ data files and input these to the `marxan_problem` function. +After importing the data, we can now generate a prioritization based on the _Marxan_ problem formulation (using the `marxan_problem()` function). **Please note that this function does not generate prioritizations using _Marxan_.** Instead, it uses the data to create an optimization problem formulation similar to _Marxan_ -- using hard constraints instead of soft constraints -- and uses an exact algorithm solver to generate a prioritization. ```{r} -# load data -pu <- system.file("extdata/input/pu.dat", package = "prioritizr") %>% - read.table(sep = ",", header = TRUE) -features <- system.file("extdata/input/spec.dat", package = "prioritizr") %>% - read.table(sep = ",", header = TRUE) -bound <- system.file("extdata/input/bound.dat", package = "prioritizr") %>% - read.table(sep = "\t", header = TRUE) -rij <- system.file("extdata/input/puvspr.dat", package = "prioritizr") %>% - read.table(sep = ",", header = TRUE) - -# build Marxan problem using data.frame objects -mp2 <- marxan_problem(x = pu, spec = features, puvspr = rij, bound = bound, - blm = 0) +# create problem +p2 <- marxan_problem(pu_data, spec_data, puvspr_data, bound_data, + blm = 0.0005) # print problem -print(mp2) -``` - -### Importance (irreplaceability) - -Conservation plans can take a long time to implement. Since funding availability and habitat quality can decline over time, it is critical that the most important places in a prioritization are scheduled for protection as early as possible. For instance, some planning units in a solution might contain many rare species which do not occur in any other planning units. Alternatively, some planning units might offer an especially high return on investment that reduces costs considerably. As a consequence, conservation planners often need information on which planning units selected in a prioritization are most important to the overall success of the prioritization. To achieve this, conservation planners can use importance (irreplaceability) scores for each planning unit selected in a solution. - -The _prioritizr R_ package offers multiple methods for assessing importance. These includes scores calculated based on replacement costs [`eval_replacement_importance()`; @r31], Ferrier _et al._ [-@r34] (`eval_ferrier_importance()`), and rarity weighted richness scores [`eval_rare_richness_importance()`; @r33]. The replacement cost scores quantify the change in the objective function (e.g. additional costs required to meet feature targets) of the optimal solution if a given planning unit in a solution cannot be acquired. They can (i) account for the cost of different planning units, (ii) account for multiple management zones, (iii) apply to any objective function, and (iv) identify truly irreplaceable planning units (denoted with infinite values). The Ferrier scores quantify the importance of planning units for meeting feature targets. They can only be applied to conservation problems with a minimum set objective and a single zone (i.e. the classic _Marxan_-type problem). Furthermore---unlike the replacement cost scores---the Ferrier scores provide a score for each feature within each planning unit, providing insight into why certain planning units are more important than other planning units. The rarity weighted richness scores are simply a measure of biological diversity. They do not account for planning costs, multiple management zones, objective functions, or feature targets (or weightings). They merely describe the spatial patterns of biodiversity, and do not account for many of the factors needed to quantify the importance of a planning unit for achieving conservation goals. - -We recommend using replacement cost scores for small and moderate sized problems (e.g. less than 30,000 planning units) when it is feasible to do so. It can take a very long time to compute replacement cost scores, and so it is simply not feasible to compute these scores for particularly large problems. For moderate and large sized problems (e.g. more than 30,000 planning units), we recommend using the Ferrier method. This is because it explicitly accounts for representation targets, unlike the rarity weighted richness scores. - -Below we will generate a solution, and then calculate importance scores for the planning units selected in the solution using the three different methods. - -```{r, fig.height = h, fig.width = w} -# formulate the problem -p41 <- problem(sim_pu_raster, sim_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.1) %>% - add_binary_decisions() - -# solve the problem -s41 <- solve(p41) - -# plot solution -plot(s41, col = c("grey90", "darkgreen"), main = "Solution", - xlim = c(-0.1, 1.1), ylim = c(-0.1, 1.1)) - -# calculate replacement cost scores and make the solver quiet -rc41 <- p41 %>% - add_default_solver(gap = 0, verbose = FALSE) %>% - eval_replacement_importance(s41) - -# plot replacement cost scores -plot(rc41, main = "replacement cost") -``` - -```{r, fig.height = h, fig.width = w} -# calculate Ferrier scores and extract total score -fs41 <- eval_ferrier_importance(p40, s40)[["total"]] - -# plot Ferrier scores -plot(fs41, main = "Ferrier scores") -``` +print(p2) -```{r, fig.height = h, fig.width = w} -# calculate rarity weighted richness scores -rwr41 <- eval_rare_richness_importance(p40, s40) +# solve problem +s2 <- solve(p2) -# plot replacement cost scores -plot(rwr41, main = "rarity weighted richness") +# print first six rows of solution object +print(head(s2)) ``` -Although rarity weighted richness scores can approximate scores derived from the other two methods in certain conservation planning exercises, we can see that the rarity weighted richness scores provide completely different results in this case. - ## Conclusion -Hopefully, this vignette has provided an informative introduction to the _prioritizr R_ package. For more worked examples using the _prioritizr R_ package, check out the [Tasmania](tasmania.html) and [Salt Spring Island](saltspring.html) vignettes. Perhaps, one of the best ways to learn a new piece of software is to just try it out. Test it, try breaking it, make mistakes, and learn from them. We would recommend trying to build conservation planning problems that resemble those you face in your own work---but using the built-in example data sets. This way you can quickly verify that the problems you build actually mean what you think they mean. For instance, you can try playing around with the targets and see what effect they have on the solutions, or try playing around with penalties and see what effect they have on the solutions. Finally, if you have any questions about using the _prioritizr R_ package or suggestions for it, please file an issue on the package's online coding repository (https://github.com/prioritizr/prioritizr/issues). +This tutorial shows how the _prioritizr R_ package can be used to build a conservation problem, generate a prioritization, and evaluate it. Although we explored just a few functions, the package provides many different functions so that you can build and custom-tailor conservation planning problems to suit your needs. To learn more about the package, please see the package vignettes for [an overview of the package](package_overview.html), [instructions for installing the _Gurobi_ optimization suite](gurobi_installation_guide.html), [benchmarks comparing the performance of different solvers](solver_benchmarks.html), and [a record of publications that have cited the package](publication_record.html). In addition to this tutorial, the package also provides tutorials on [incorporating connectivity into prioritizations](connectivity_tutorial.html), [calibrating trade-offs between different criteria](calibrating_trade-offs.html) (e.g., total cost and spatial fragmentation), and [creating prioritizations that have multiple management zones or management actions](management_zones_tutorial.html). ## References diff --git a/vignettes/publication_record.Rmd b/vignettes/publication_record.Rmd index 7768b00fc..f7ca8b88d 100644 --- a/vignettes/publication_record.Rmd +++ b/vignettes/publication_record.Rmd @@ -1,5 +1,5 @@ --- -title: "Publication Record" +title: "Publication record" output: rmarkdown::html_vignette: toc: true @@ -8,7 +8,7 @@ output: fontsize: 11pt documentclass: article vignette: > - %\VignetteIndexEntry{Publication Record} + %\VignetteIndexEntry{Publication record} %\VignetteEngine{knitr::rmarkdown_notangle} --- diff --git a/vignettes/references.bib b/vignettes/references.bib index cf3f73fd2..8c2a1a1d2 100644 --- a/vignettes/references.bib +++ b/vignettes/references.bib @@ -357,3 +357,316 @@ @article{r34 pages={303--325}, year={2000}, } + +@article{r35, + year = {2014}, + volume = {51}, + number = {6}, + pages = {1504--1514}, + author = {Linda R. Harris and Matthew E. Watts and Ronel Nel and David S. Schoeman and Hugh P. Possingham}, + editor = {Paul Armsworth}, + title = {Using multivariate statistics to explore trade-offs among spatial planning scenarios}, + journal = {Journal of Applied Ecology} +} + +@article{r36, + year = {1991}, + volume = {55}, + number = {3}, + pages = {235--254}, + author = {R.I. Vane-Wright and C.J. Humphries and P.H. Williams}, + title = {What to protect? --- {Systematics} and the agony of choice}, + journal = {Biological Conservation} +} + +@article{r37, + year = {2013}, + pages = {324--332}, + author = {Carissa J. Klein and Vivitskaia J. Tulloch and Benjamin S. Halpern and Kimberly A. Selkoe and Matthew E. Watts and Charles Steinback and Astrid Scholz and Hugh P. Possingham}, + title = {Tradeoffs in marine reserve design: habitat condition, representation, and socioeconomic costs}, + journal = {Conservation Letters} +} + +@article{r38, + title={Incorporating asymmetric connectivity into spatial decision making for conservation}, + author={Beger, Maria and Linke, Simon and Watts, Matt and Game, Eddie and Treml, Eric and Ball, Ian and Possingham, Hugh P}, + journal={Conservation Letters}, + volume={3}, + number={5}, + pages={359--368}, + year={2010}, +} + +@article{r39, + title={Catchment zoning to enhance co-benefits and minimize trade-offs between ecosystem services and freshwater biodiversity conservation}, + author={Hermoso, Virgilio and Cattarino, Lorenzo and Linke, Simon and Kennard, Mark J}, + journal={Aquatic Conservation: Marine and Freshwater Ecosystems}, + volume={28}, + number={4}, + pages={1004--1014}, + year={2018}, + publisher={Wiley Online Library} +} + +@article{r40, + title={Freshwater conservation planning in the context of nature needs half and protected area dynamism in Bhutan}, + author={Dorji, Tshering and Linke, Simon and Sheldon, Fran}, + journal={Biological Conservation}, + volume={251}, + pages={108785}, + year={2020}, + publisher={Elsevier} +} + +@article{r41, + title={Addressing longitudinal connectivity in the systematic conservation planning of fresh waters}, + author={Hermoso, V and Linke, S and Prenda, J and Possingham, H P}, + journal={Freshwater Biology}, + volume={56}, + number={1}, + pages={57--70}, + year={2011}, + publisher={Wiley Online Library} +} + +@article{r42, + year = {2005}, + volume = {10}, + number = {3}, + pages = {203--213}, + author = {Romola R. Stewart and Hugh P. Possingham}, + title = {Efficiency, costs and trade-offs in marine reserve system design}, + journal = {Environmental Modeling and Assessment} +} + +@article{r43, + year = {2012}, + volume = {18}, + number = {5}, + pages = {448--458}, + author = {Virgilio Hermoso and Mark J. Kennard and Simon Linke}, + title = {Integrating multidirectional connectivity requirements in systematic conservation planning for freshwater systems}, + journal = {Diversity and Distributions} +} + +@article{r44, + title={{GIS}-based multiple-criteria decision analysis}, + author={Greene, Randal and Devillers, Rodolphe and Luther, Joan E and Eddy, Brian G}, + journal={Geography Compass}, + volume={5}, + number={6}, + pages={412--432}, + year={2011}, + publisher={Wiley Online Library} +} + +@book{r45, + title={{Marxan Good Practices Handbook}}, + author={Ardron, Jeff A and Possingham, Hugh P and Klein, Carissa J}, + publisher={Pacific Marine Analysis and Research Association, Vancouver}, + pages={149}, + notes={Version 2}, + year={2010}, + address={Victoria, BC} +} + +@book{r46, + title={{Multiple Attribute Decision Making: Methods and Applications}}, + author={Hwang, C L and Yoon, K G}, + year={1981}, + publisher={Springer-Verlag}, + address={New York, NY} +} + +@article{r47, + year = {2020}, + volume = {3}, + number = {3}, + pages = {371--382}, + author = {Brooke A. Williams and Oscar Venter and James R. Allan and Scott C. Atkinson and Jose A. Rehbein and Michelle Ward and Moreno Di Marco and Hedley S. Grantham and Jamison Ervin and Scott J. Goetz and Andrew J. Hansen and Patrick Jantz and Rajeev Pillay and Susana Rodr{\'{\i}}guez-Buritic{\'{a}} and Christina Supples and Anne L.S. Virnig and James E.M. Watson}, + title = {Change in Terrestrial Human Footprint Drives Continued Loss of Intact Ecosystems}, + journal = {One Earth} +} + +@article{r48, + year = {2019}, + volume = {13}, + number = {2}, + author = {Hawthorne L. Beyer and Oscar Venter and Hedley S. Grantham and James E.M. Watson}, + title = {Substantial losses in ecoregion intactness highlight urgency of globally coordinated action}, + journal = {Conservation Letters} +} + +@article{r49, + title={Generating multiobjective trade-offs: An algorithm for bicriterion problems}, + author={Cohon, Jared L and Church, Richard L and Sheer, Daniel P}, + journal={Water Resources Research}, + volume={15}, + number={5}, + pages={1001--1010}, + year={1979}, + publisher={Wiley Online Library} +} + +@article{r50, + year = {2005}, + volume = {10}, + number = {3}, + pages = {215--228}, + author = {Douglas T. Fischer and Richard L. Church}, + title = {The {SITES} reserve selection system: A critical review}, + journal = {Environmental Modeling and Assessment} +} + +@article{r51, + title={Effective conservation requires clear objectives and prioritizing actions, not places or species}, + author={Brown, Christopher J and Bode, Michael and Venter, Oscar and Barnes, Megan D and McGowan, Jennifer and Runge, Claire A and Watson, James E M and Possingham, Hugh P}, + journal={Proceedings of the National Academy of Sciences}, + volume={112}, + number={32}, + pages={E4342}, + year={2015}, +} + +@article{r52, + title={Operationalizing ecological connectivity in spatial conservation planning with {Marxan Connect}}, + author={Daigle, R{\'e}mi M and Metaxas, Anna and Balbar, Arieanna C and McGowan, Jennifer and Treml, Eric A and Kuempel, Caitlin D and Possingham, Hugh P and Beger, Maria}, + journal={Methods in Ecology and Evolution}, + volume={11}, + number={4}, + pages={570--579}, + year={2020}, + publisher={Wiley Online Library} +} + +@article{r53, + doi = {10.1016/j.xpro.2021.100882}, + url = {https://doi.org/10.1016/j.xpro.2021.100882}, + year = {2021}, + month = dec, + publisher = {Elsevier {BV}}, + volume = {2}, + number = {4}, + pages = {100882}, + author = {Kathleen Anne Carroll}, + title = {Systematic prioritization protocol applied to wolverine habitat connectivity}, + journal = {{STAR} Protocols} +} + +@article{r54, + year = {2020}, + volume = {57}, + number = {11}, + pages = {2159--2169}, + author = {Jeffrey O. Hanson and Adam Marques and Ana Ver{\'{\i}}ssimo and Miguel Camacho-Sanchez and Guillermo Velo-Ant{\'{o}}n and {\'{I}}{\~{n}}igo Mart{\'{\i}}nez-Solano and Silvia B. Carvalho}, + title = {Conservation planning for adaptive and neutral evolutionary processes}, + journal = {Journal of Applied Ecology} +} + +@article{r55, + title={Incorporating connectivity into reserve selection procedures}, + author={Briers, Robert A}, + journal={Biological Conservation}, + volume={103}, + number={1}, + pages={77--83}, + year={2002}, + publisher={Elsevier} +} + +@article{r56, + title={The current application of ecological connectivity in the design of marine protected areas}, + author={Balbar, Arieanna C and Metaxas, Anna}, + journal={Global Ecology and Conservation}, + volume={17}, + pages={e00569}, + year={2019}, + publisher={Elsevier} +} + +@article{r57, + author = {Hayri {\"O}nal and Robert A. Briers}, + journal = {Operations Research}, + number = {2}, + pages = {379-388}, + title = {Optimal selection of a connected reserve network}, + volume = {54}, + year = {2006} +} + +@article{r58, + title={Using individual-based movement information to identify spatial conservation priorities for mobile species}, + author={Dwyer, Ross G and Campbell, Hamish A and Pillans, Richard D and Watts, Matthew E and Lyon, Barry J and Guru, Siddeswara M and Dinh, Minh N and Possingham, Hugh P and Franklin, Craig E}, + journal={Conservation Biology}, + volume={33}, + number={6}, + pages={1426--1437}, + year={2019}, + publisher={Wiley Online Library} +} + +@article{r59, + title={Landscape-scale conservation design across biotic realms: sequential integration of aquatic and terrestrial landscapes}, + author={Leonard, Paul B and Baldwin, Robert F and Hanks, R Daniel}, + journal={Scientific Reports}, + volume={7}, + number={1}, + pages={1--12}, + year={2017}, + publisher={Nature Publishing Group} +} + +@article{r60, + title={Habitat fragmentation reduces genetic diversity and connectivity among toad populations in the Brazilian Atlantic Coastal Forest}, + author={Dixo, Marianna and Metzger, Jean Paul and Morgante, Jo{\~a}o S and Zamudio, Kelly R}, + journal={Biological Conservation}, + volume={142}, + number={8}, + pages={1560--1569}, + year={2009}, + publisher={Elsevier} +} + +@article{r61, + title={Habitat connectivity improves reserve performance}, + author={Olds, Andrew D and Connolly, Rod M and Pitt, Kylie A and Maxwell, Paul S}, + journal={Conservation Letters}, + volume={5}, + number={1}, + pages={56--63}, + year={2012}, + publisher={Wiley Online Library} +} + +@article{r62, + title={Climate change, connectivity and conservation decision making: back to basics}, + author={Hodgson, Jenny A and Thomas, Chris D and Wintle, Brendan A and Moilanen, Atte}, + journal={Journal of Applied Ecology}, + volume={46}, + number={5}, + pages={964--969}, + year={2009}, + publisher={Wiley Online Library} +} + +@article{r63, + title={Linking like with like: optimising connectivity between environmentally-similar habitats}, + author={Alagador, Diogo and Trivino, Maria and Cerdeira, Jorge Orestes and Br{\'a}s, Raul and Cabeza, Mar and Ara{\'u}jo, Miguel Bastos}, + journal={Landscape Ecology}, + volume={27}, + number={2}, + pages={291--301}, + year={2012}, + publisher={Springer} +} + +@article{r64, + title={Species specific connectivity in reserve-network design using graphs}, + author={Cerdeira, J Orestes and Pinto, Leonor S and Cabeza, Mar and Gaston, Kevin J}, + journal={Biological Conservation}, + volume={143}, + number={2}, + pages={408--415}, + year={2010}, + publisher={Elsevier} +} diff --git a/vignettes/saltspring.Rmd b/vignettes/saltspring.Rmd deleted file mode 100644 index 91284e3d2..000000000 --- a/vignettes/saltspring.Rmd +++ /dev/null @@ -1,212 +0,0 @@ ---- -title: "Salt Spring Island Tutorial" -output: - rmarkdown::html_vignette: - toc: true - fig_caption: true - self_contained: yes -fontsize: 11pt -documentclass: article -bibliography: references.bib -csl: reference-style.csl -vignette: > - %\VignetteIndexEntry{Salt Spring Island Tutorial} - %\VignetteEngine{knitr::rmarkdown_notangle} ---- - -```{r, include = FALSE} -h <- 3.5 -w <- 3.5 -is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", - "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) -knitr::opts_chunk$set(fig.align = "center", eval = !is_check) -``` - -## Introduction - -This aim of this tutorial is to show how raster data can used to build conservation problems with the _prioritizr R_ package. The data used here is a subset of a much larger dataset for the Georgia Basin obtained as part of an online _Marxan_-based planning tool created for the Coastal Douglas-fir Conservation Partnership [CDFCP; @r29]. For simplicity, we focus only on Salt Spring Island, British Columbia. Salt Spring Island is central to the region and supports a diverse and globally unique mix of dry forest and savanna habitats. Today, these habitats are critically threatened due to land conversion, invasive species, and altered disturbance regimes. Known broadly as the Georgia Depression-Puget Lowlands, this region includes threatened Coastal Douglas-fir forest and Oak-Savannah habitats, also referred to as Garry oak ecosystems. Please note that this tutorial uses data from the _prioritizrdata R_ package, so ensure that it is installed before trying out the code yourself. For more information on the dataset refer to the [Marxan tool portal](https://arcese.forestry.ubc.ca/marxan-tool/) and the [tool tutorial](https://peter-arcese-lab.sites.olt.ubc.ca/files/2016/09/CDFCP_tutorial_2017_05.pdf). - -
    - -![_Extent of Coastal Douglas-fir Conservation Partnership Tool area and location of Salt Spring Island_](figures/map.jpg) - -
    - -## Exploring the data - -This dataset contains two items. First, a single-band raster planning unit layer where each one hectare pixel represents a planning unit and contains its corresponding cost [@r28]. Second, a raster stack containing ecological community feature data. Field and remote sensed data were used to calculate the probability of occurrence of five key ecological communities found on Salt Spring island. Each layer in the stack represents a different community type. In order these are; Old Forest, Savannah, Wetland, Shrub, and a layer representing the inverse probability of occurrence of human commensal species. For a given layer, the cell value indicates the composite probability of encountering the suite of bird species most commonly associated with that community type. - -First, load the required packages and the data. - -```{r, message = FALSE} -# load packages -library(prioritizrdata) -library(prioritizr) - -# load planning unit data -data(salt_pu) - -# load conservation feature data -data(salt_features) -``` - -Let's have a look at the planning unit data. Note that we log-transformed the raster to better visualize the variation in planning unit cost. - -```{r, fig.width = w, fig.height = h} -# print planning unit data -print(salt_pu) - -# plot histogram of the planning unit costs -hist(values(salt_pu), main = "Distribution of costs", - xlab = "Planning unit costs") -``` - -```{r, fig.width = w, fig.height = h} -# plot map showing the planning units costs on a log-scale -plot(log(salt_pu), main = "Planning unit costs (log)") -``` - -Next, let's look at the feature data. - -```{r, fig.width = 4.5, fig.height = 4.5} -# print features -print(salt_features) - -# plot map showing the distribution of the features -plot(salt_features, main = names(salt_features)) -``` - -## Formulating the Problem - -In this tutorial, we will only cover a few of the different ways that conservation planning problems can be formulated. The examples used here are provided to highlight how different parameters can substantially---or only slightly---alter solutions. Here, we use the minimum set objective to fulfill all targets and constraints for the smallest cost. This objective is similar to that used in the _Marxan_ decision support tool. To keep this simple, we will set biodiversity targets at 17 % to reflect the [Aichi Biodiversity Target 11](https://www.cbd.int/sp/targets/). Because properties on Salt Spring Island can either be acquired in their entirety or not at all, we leave the decision framework as the default; binary decision making. This means that planning units are either selected in the solution or not selected in the solution---planning units cannot be partially acquired. - -Now we will formulate the conservation planning problem. - -```{r} -# create problem -p1 <- problem(salt_pu, salt_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.17) %>% - add_binary_decisions() %>% - add_default_solver() - -# print problem -print(p1) -``` - -Note that the `%>%` notation is used to attach the objectives, targets, and decisions to the problem. Since binary-type decisions are the default decision-type, we don't have to explicitly specify the decision-type, but we specify it here for clarity. - -## Solving the problem - -The _prioritizr R_ package supports three different integer linear programming solvers: _gurobi_ (via the _gurobi_ package), _IBM CPLEX_ (via the _cplexAPI_ package), _SYMPHONY_ (via the _Rsymphony_ and _lpsymphony_ packages) and _CBC_ (via the _rcbc_ package). There are costs and benefits associated with each of these solvers, but the solver itself should have little impact on the actual solution returned (though certain solvers may take longer to return solutions than others). - -First, remember that the solvers must be installed. You can check if these packages are installed by running the code below. Some of these solvers are a bit complicated to install. For instance, the _gurobi_ package is distributed with the [_Gurobi_ commercial software suite](https://www.gurobi.com/) and is not available on the Comprehensive R Archive Network (CRAN). For more information on installing the _gurobi_ package, please refer to the [_Gurobi Installation Guide_](gurobi_installation.html) for more information on installing the _gurobi R_ package. Additionally, the _rcbc_ package is not available on CRAN, and must be installed from GitHub ([please refer to its documentation for installation instructions](https://dirkschumacher.github.io/rcbc/). Furthermore, although the _Rcplex_ package is available on CRAN, it cannot be successfully installed unless the [IBM CPLEX software](https://www.ibm.com/analytics/cplex-optimizer) has [already installed on your system](https://github.com/cran/cplexAPI/blob/master/inst/INSTALL). - -```{r, eval = FALSE} -print(require(gurobi)) -print(require(cplexAPI)) -print(require(Rsymphony)) -print(require(lpsymphony)) -print(require(rcbc)) -``` - -Now we will try solving the problem using the different solvers (see [`?solve`](https://prioritizr.net/reference/solve.html) for more information). We will also experiment with limiting the maximum amount of time that can be spent looking for each solution when solving the problem (using the `time_limit` parameter), and see how this alters the solutions. - -```{r, message = FALSE, results = "hide"} -titles <- c() # create vector to store plot titles -s1 <- stack() # create empty stack to store solutions - -# create new problem object with added solver -if (require("Rsymphony")) { - titles <- c(titles, "Rsymphony (5s)") - p2 <- p1 %>% add_rsymphony_solver(time_limit = 5) - s1 <- addLayer(s1, solve(p2)) -} - -if (require("Rsymphony")) { - titles <- c(titles, "Rsymphony (10s)") - p3 <- p1 %>% add_rsymphony_solver(time_limit = 10) - s1 <- addLayer(s1, solve(p3)) -} - -if (require("gurobi")) { - titles <- c(titles, "Gurobi (5s)") - p4 <- p1 %>% add_gurobi_solver(time_limit = 5) - s1 <- addLayer(s1, solve(p4)) -} - -if (require("cplexAPI")) { - titles <- c(titles, "IBM CPLEX (5s)") - p5 <- p1 %>% add_cplex_solver(time_limit = 5) - s1 <- addLayer(s1, solve(p5)) -} - -if (require("lpsymphony")) { - titles <- c(titles, "lpsymphony (10s)") - p6 <- p1 %>% add_lpsymphony_solver(time_limit = 10) - s1 <- addLayer(s1, solve(p6)) -} - -if (require("rcbc")) { - titles <- c(titles, "CBC (10s)") - p7 <- p1 %>% add_cbc_solver(time_limit = 10) - s1 <- addLayer(s1, solve(p7)) -} -``` - -Now let's visualize the solutions. - -```{r, fig.height = 5.5, fig.width = 5} -plot(s1, main = titles, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")) -``` - -We can see that all of the solutions are very similar. Therefore it would appear that for this particular problem, the solution is not highly sensitive to solver choice. For larger and more complex problems, however, we would expect the solution from _Gurobi_ to be far superior when setting time limits. At a glance, it also appears that the time limit settings did not largely impact the solutions (5 seconds vs. 10 seconds), but a more rigorous analysis is needed to investigate this. - -## Adding connectivity - -Isolated and fragmented populations are often more vulnerable to extinction. As a consequence, landscape connectivity is a key focus of many conservation planning exercises. There are a number of methods that can be used to increase connectivity in prioritizations. These methods typically involve adding constraints to a problem to ensure that solutions exhibit a specific property (e.g. selected planning units that form a contiguous reserve), or adding penalties to a problem to penalize solutions that exhibit specific properties (e.g. high levels of fragmentation). Here we will explore a couple different strategies for increasing connectivity in solutions. For brevity, we will use default solver which is automatically added to a problem if a solver is not manually specified. - -```{r} -# basic problem formulation -p6 <- problem(salt_pu, salt_features) %>% - add_min_set_objective() %>% - add_relative_targets(0.17) %>% - add_binary_decisions() %>% - add_default_solver() - -# print problem -print(p6) -``` - -```{r, results = "hide"} -titles2 <- c() # create vector to store plot titles -s2 <- stack() # create empty stack to store solutions - -# no connectivity requirement -titles2 <- c(titles2, "No connectivity") -s2 <- addLayer(s2, solve(p6)) - -# require at least two for each selected planning unit -titles2 <- c(titles2, "Neighbor constraints (two)") -p7 <- p6 %>% add_neighbor_constraints(2) -s2 <- addLayer(s2, solve(p7)) - -# impose small penalty for fragmented solutions -titles2 <- c(titles2, "Boundary penalty (low)") -p8 <- p6 %>% add_boundary_penalties(0.0005, 0.5) -s2 <- addLayer(s2, solve(p8)) - -# impose high penalty for fragmented solutions -titles2 <- c(titles2, "Boundary penalty (high)") -p9 <- p6 %>% add_boundary_penalties(0.05, 0.5) -s2 <- addLayer(s2, solve(p9)) -``` - -```{r, fig.height = 5.5, fig.width = 5} -# plot solutions -plot(s2, main = titles2, breaks = c(0, 0.5, 1), col = c("grey70", "darkgreen")) -``` - -Here we can see that adding the constraints and penalties to the problem has a small, but noticeable effect on the solutions. We would expect to see larger difference between the solutions for problems that contain more than five conservation features. You may also wish to explore the `add_connectivity_penalties` and `add_feature_contiguity_constraints` functions. These functions use additional data on landscape resistance to provide a more accurate parametrization of connectivity and, in turn, deliver more effective solutions. - -## References diff --git a/vignettes/solver_benchmark.Rmd b/vignettes/solver_benchmarks.Rmd similarity index 77% rename from vignettes/solver_benchmark.Rmd rename to vignettes/solver_benchmarks.Rmd index a91dafadc..f0c6cd3e3 100644 --- a/vignettes/solver_benchmark.Rmd +++ b/vignettes/solver_benchmarks.Rmd @@ -1,5 +1,5 @@ --- -title: "Solver Benchmarks" +title: "Solver benchmarks" output: rmarkdown::html_vignette: toc: true @@ -10,7 +10,7 @@ documentclass: article bibliography: references.bib csl: reference-style.csl vignette: > - %\VignetteIndexEntry{Solver Benchmarks} + %\VignetteIndexEntry{Solver benchmarks} %\VignetteEngine{knitr::rmarkdown_notangle} --- @@ -35,17 +35,17 @@ boundary_penalty_values <- list( devtools::load_all() ``` -# Introduction +## Introduction -The _prioritizr R_ package supports a variety of optimization solvers for generating prioritizations. Specifically, the following functions can be used: [`add_gurobi_solver()`](https://prioritizr.net/reference/add_gurobi_solver.html) (interfaces with the [_Gurobi_ software](https://www.gurobi.com/)), [`add_cplex_solver()`](https://prioritizr.net/reference/add_cplex_solver.html) (interfaces with the [_IBM CPLEX_ software](https://www.ibm.com/analytics/cplex-optimizer)), [`add_cbc_solver()`](https://prioritizr.net/reference/add_cbc_solver.html) (interfaces with the [_CBC_ software](https://projects.coin-or.org/Cbc) using the [_rcbc R_ package](https://dirkschumacher.github.io/rcbc/)), [`add_rsymphony_solver()`](https://prioritizr.net/reference/add_rsymphony_solver.html) (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_Rsymphony R_ package](https://CRAN.R-project.org/package=Rsymphony)), and the [`add_lpsymphony_solver()`](https://prioritizr.net/reference/add_lsymphony_solver.html) function (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_lpsymphony R_ package](https://www.bioconductor.org/packages/release/bioc/html/lpsymphony.html)). Broadly speaking, _IBM CPLEX_ and _Gurobi_ tend to be the fastest among the supported solvers. Although they are both commercial software, special academic licenses are available at no cost. +The _prioritizr R_ package supports a variety of optimization solvers for generating prioritizations. Specifically, the following functions can be used: `add_gurobi_solver()` (interfaces with the [_Gurobi_ software](https://www.gurobi.com/)), `add_cplex_solver()` (interfaces with the [_IBM CPLEX_ software](https://www.ibm.com/analytics/cplex-optimizer)), `add_cbc_solver()` (interfaces with the [_CBC_ software](https://projects.coin-or.org/Cbc) using the [_rcbc R_ package](https://dirkschumacher.github.io/rcbc/)), `add_rsymphony_solver()` (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_Rsymphony R_ package](https://CRAN.R-project.org/package=Rsymphony)), and the `add_lpsymphony_solver()` function (interfaces with the [_SYMPHONY_ software](https://projects.coin-or.org/SYMPHONY) using the [_lpsymphony R_ package](https://www.bioconductor.org/packages/release/bioc/html/lpsymphony.html)). Broadly speaking, _IBM CPLEX_ and _Gurobi_ tend to be the fastest among the supported solvers. Although they are both commercial software, special academic licenses are available at no cost. -In this vignette, we will explore the performance of different solvers. Using a benchmark analysis, we will see how well they can tackle problems of varying size (e.g. number of planning units) and complexity (e.g. adding boundary length penalties to reduce spatial fragmentation). Since users working in governmental and non-governmental organizations may need to purchase licenses for _IBM CPLEX_ or _Gurobi_ to use them, this vignette also aims to provide insight into whether the potential benefits of purchasing such licenses is worthwhile. Indeed -- depending on the size and complexity of a given conservation planning problem -- solvers based on open source software may only take slightly longer than commercial solvers. +In this vignette, we will explore the performance of different solvers. Using a benchmark analysis, we will see how well they can tackle problems of varying size (e.g., number of planning units) and complexity (e.g., adding boundary length penalties to reduce spatial fragmentation). Since users working in governmental and non-governmental organizations may need to purchase licenses for _IBM CPLEX_ or _Gurobi_ to use them, this vignette also aims to provide insight into whether the potential benefits of purchasing such licenses is worthwhile. Indeed -- depending on the size and complexity of a given conservation planning problem -- solvers based on open source software may only take slightly longer than commercial solvers. -# Methods +## Methods -This vignette will report the results of a benchmark analysis. To reduce computational burden, we previously completed the benchmark analysis and [uploaded the results to an online repository](https://github.com/prioritizr/benchmark/releases) ([code available online](https://github.com/prioritizr/benchmark)). This analysis involved generating prioritizations using different solvers and recording how long it took for the solvers to finish. To help understand the factors that influence how long it takes for solvers to generate a prioritization, we examined a suite of conservation planning problems with varying size (i.e. number of planning units), complexity (i.e. varying penalties to reduce spatial fragmentation), and with different objective functions (i.e. [metric used to evaluate competing solutions](https://prioritizr.net/reference/objectives.html)). In this section, we will download the results for the previously completed benchmark analysis and examine the parameters used to conduct it. +This vignette will report the results of a benchmark analysis. To reduce computational burden, we previously completed the benchmark analysis and [uploaded the results to an online repository](https://github.com/prioritizr/benchmark/releases) ([code available online](https://github.com/prioritizr/benchmark)). This analysis involved generating prioritizations using different solvers and recording how long it took for the solvers to finish. To help understand the factors that influence how long it takes for solvers to generate a prioritization, we examined a suite of conservation planning problems with varying size (i.e., number of planning units), complexity (i.e., varying penalties to reduce spatial fragmentation), and with different objective functions (i.e., [metric used to evaluate competing solutions](https://prioritizr.net/reference/objectives.html)). In this section, we will download the results for the previously completed benchmark analysis and examine the parameters used to conduct it. -## Set up +### Set up To start off, we will the load packages used in this vignette. @@ -58,7 +58,7 @@ library(units) library(dplyr) ``` -## Download benchmark results +### Download benchmark results Let's download the results of the benchmark analysis. This code will save the results to a temporary folder on your computer. Please note that downloading the results might take several minutes to complete depending on your Internet connection. If you are unable to download the results onto your computer, you can view the graphs shown in this vignette. You only need to run the code in this vignette if you wish to explore certain aspects of the results yourself. @@ -73,7 +73,7 @@ pb_download( load(file.path(tempdir(), "results.rda")) ``` -## Benchmark parameters +### Benchmark parameters After downloading the benchmark results, let's have a look at the parameters that were used to conduct it. Note that all benchmark scenarios have 72 features. @@ -82,11 +82,11 @@ After downloading the benchmark results, let's have a look at the parameters tha n_planning_units <- unique(benchmark_results$number_of_planning_units) print(n_planning_units) -# number of features (e.g. number of different species examined) +# number of features (e.g., number of different species examined) unique(benchmark_results$number_features) # representation targets, -# units are proportion of the total amount of each feature (e.g. 0.1 = 10%) +# units are proportion of the total amount of each feature (e.g., 0.1 = 10%) unique(benchmark_results$relative_target) # number of planning units @@ -107,7 +107,7 @@ boundary_penalty_values$add_min_set_objective ## boundary penalty values for min shortfall objective function boundary_penalty_values$add_min_shortfall_objective -# budgets examined for budget-limited objectives (e.g. 0.1 = 10% of total cost) +# budgets examined for budget-limited objectives (e.g., 0.1 = 10% of total cost) ## note that the min set objective function does not use a budget, ## and thus it has a NA value tibble(objective = unique(benchmark_results$objective), @@ -115,7 +115,7 @@ tibble(objective = unique(benchmark_results$objective), ``` -## Helper function +### Helper function Now we will define a helper function to quickly plot the results from the benchmark analysis. This will be helpful for interpreting the results of the benchmark analysis in the following section. @@ -208,9 +208,9 @@ plot_benchmark <- function( } ``` -# Results +## Results -We will now inspect the results of the benchmark analysis. The `benchmark_results` object is a table (i.e. `tibble()`) containing information for each benchmark run (e.g. run time), and the `solution_raster_data` object (i.e. `RasterStack`) contains the prioritizations generated for each benchmark run. +We will now inspect the results of the benchmark analysis. The `benchmark_results` object is a table (i.e., `tibble()`) containing information for each benchmark run (e.g., run time), and the `solution_raster_data` object (i.e., `RasterStack`) contains the prioritizations generated for each benchmark run. ```{r "preview_results"} # preview results @@ -233,7 +233,7 @@ theme(axis.text.x = element_text(size = 7)) + labs(x = "Solver", y = "Run time (hours)") ``` -## Minimum set results (no boundary penalty) +### Minimum set results (no boundary penalty) Now, let's investigate the solver behavior in more detail. Specifically, we will examine the benchmark results generated for the minimum set objective function. This is because the minimum set objective function is the most commonly used objective function in systematic conservation, due to the fact that it is used by the [_Marxan_ decision support software](https://marxansolutions.org/). To begin with, let's examine the results for the smallest and simplest conservation planning problems examined in the benchmark analysis. Here, all prioritizations were generated using problems that involved `r formatC(n_planning_units[1], big.mark = ",")` planning units and did not contain any boundary length penalties. Since all benchmark scenarios have 72 features -- as mentioned earlier -- all of these prioritizations were generated with 72 features. When looking at the results, we can see that all solvers solve the problem in a comparable amount of time across all targets investigated. @@ -244,7 +244,7 @@ plot_benchmark( boundary_penalty = 0) ``` -Next, let's look at the results for a more realistic problem involving `r formatC(n_planning_units[2], big.mark = ",")` planning units and see how the timing of the different solvers used compares. Note that all other factors (e.g. absence of boundary length penalties) are the same as for the previous graph. +Next, let's look at the results for a more realistic problem involving `r formatC(n_planning_units[2], big.mark = ",")` planning units and see how the timing of the different solvers used compares. Note that all other factors (e.g., absence of boundary length penalties) are the same as for the previous graph. ```{r "min_set_pu_n_2"} plot_benchmark( @@ -271,7 +271,7 @@ plot_benchmark( boundary_penalty = 0) ``` -To get a better sense of how the faster solvers (i.e. based on _CBC_, _IBM CPLEX_, _Gurobi_) compare for such a large problem, let's take a closer look at these three solvers. Interestingly, we can see that the solver based on the open source _CBC_ software is slightly faster -- by a few minutes -- than the other solvers. +To get a better sense of how the faster solvers (i.e., based on _CBC_, _IBM CPLEX_, _Gurobi_) compare for such a large problem, let's take a closer look at these three solvers. Interestingly, we can see that the solver based on the open source _CBC_ software is slightly faster -- by a few minutes -- than the other solvers. ```{r "min_set_pu_n_4_fast_only"} plot_benchmark( @@ -282,9 +282,9 @@ plot_benchmark( ``` -## Minimum set results with low boundary penalty +### Minimum set results with low boundary penalty -Now let's at the same problem types, but this time with a low boundary length penalty value added to the problem formulation. To start with, we will look at scenarios with a low `boundary_penalty` value (i.e. $`r boundary_penalty_values$add_min_set_objective[2]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. +Now let's at the same problem types, but this time with a low boundary length penalty value added to the problem formulation. To start with, we will look at scenarios with a low `boundary_penalty` value (i.e., $`r boundary_penalty_values$add_min_set_objective[2]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. ```{r "min_set_pu_n_1_with_low_boundary_penalty"} plot_benchmark( @@ -330,9 +330,9 @@ plot_benchmark( solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver")) ``` -## Minimum set results with high boundary penalty +### Minimum set results with high boundary penalty -Now let's look at the same problem types, but this time with a high boundary length penalty parameter added to the problem formulation (i.e. $`r boundary_penalty_values$add_min_set_objective[3]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. Although there are some differences between the solvers, they all have very similar run times (i.e. all less than one second). +Now let's look at the same problem types, but this time with a high boundary length penalty parameter added to the problem formulation (i.e., $`r boundary_penalty_values$add_min_set_objective[3]`$). Let's start again with the smallest problem size we've benchmarked. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. Although there are some differences between the solvers, they all have very similar run times (i.e., all less than one second). ```{r "min_set_pu_n_1_with_high_boundary_penalty"} plot_benchmark( @@ -378,7 +378,7 @@ plot_benchmark( solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver")) ``` -## Minimize shortfall results (no boundary penalty) +### Minimize shortfall results (no boundary penalty) Now, let's investigate the solver behavior for the minimum shortfall objective function. Let's start with the smallest problem size examined. All benchmark scenarios have 72 features. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. We can see that all solvers solve the problem in a comparable amount of time across all targets investigated. @@ -407,7 +407,7 @@ plot_benchmark( boundary_penalty = 0) ``` -Finally, let's look at timing comparisons for a large problem with `r formatC(n_planning_units[4], big.mark = ",")` planning units. We can see that all the open source solvers (i.e. _CBC_, _Rsymphony_ and _lpsymphony_) take a lot longer than the commercial solvers (i.e. _IBM CPLEX_ and _Guorbi_). +Finally, let's look at timing comparisons for a large problem with `r formatC(n_planning_units[4], big.mark = ",")` planning units. We can see that all the open source solvers (i.e., _CBC_, _Rsymphony_ and _lpsymphony_) take a lot longer than the commercial solvers (i.e., _IBM CPLEX_ and _Guorbi_). ```{r "min_short_pu_n_4"} plot_benchmark( @@ -416,9 +416,9 @@ plot_benchmark( boundary_penalty = 0) ``` -## Minimize shortfall results with low boundary penalty +### Minimize shortfall results with low boundary penalty -Now let's look at the same problem type, but this time with a low `boundary_penalty` parameter added to the problem formulation (i.e. $`r boundary_penalty_values$add_min_shortfall_objective[2]`$). Let's start again with the smallest problem size examined. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. +Now let's look at the same problem type, but this time with a low `boundary_penalty` parameter added to the problem formulation (i.e., $`r boundary_penalty_values$add_min_shortfall_objective[2]`$). Let's start again with the smallest problem size examined. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. ```{r "min_short_pu_n_1_with_low_boundary_penalty"} plot_benchmark( @@ -454,7 +454,7 @@ plot_benchmark( boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[2]) ``` -To get a better sense of how the faster solvers compare (i.e. _CBC_, _IBM CPLEX_, and _Gurobi_), let's take a closer look at these three solvers. We can see that the _CBC_ solver takes a lot longer to generate prioritizations than the _IBM CPLEX_ and _Gurobi_ solvers. This result suggests that _IBM CPLEX_ and _Gurobi_ could be really beneficial for large-scale conservation planning problems with boundary length penalties and the minimum shortfall objective function. +To get a better sense of how the faster solvers compare (i.e., _CBC_, _IBM CPLEX_, and _Gurobi_), let's take a closer look at these three solvers. We can see that the _CBC_ solver takes a lot longer to generate prioritizations than the _IBM CPLEX_ and _Gurobi_ solvers. This result suggests that _IBM CPLEX_ and _Gurobi_ could be really beneficial for large-scale conservation planning problems with boundary length penalties and the minimum shortfall objective function. ```{r "min_short_pu_n_4_and_boundary_penalty_and_fast_solvers"} plot_benchmark( @@ -464,7 +464,7 @@ plot_benchmark( solver = c("add_cbc_solver", "add_cplex_solver", "add_gurobi_solver")) ``` -## Minimize shortfall results with high boundary penalty +### Minimize shortfall results with high boundary penalty Now let's look at the same problem types, but this time with a higher `boundary_penalty` parameter added to the problem formulation ($`r boundary_penalty_values$add_min_shortfall_objective[3]`$). Let's start again with the smallest problem size examined. This problem has only `r formatC(n_planning_units[1], big.mark = ",")` planning units. @@ -475,7 +475,7 @@ plot_benchmark( boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[3]) ``` -Next, let's look at the results for a greater number of planning units (i.e. `r formatC(n_planning_units[2], big.mark = ",")` planning units) and see how the timings compare. All the solvers have a similar run time now. Interestingly, the _Gurobi_ solver is the slowest -- but only by a couple of minutes -- for these benchmark parameters. +Next, let's look at the results for a greater number of planning units (i.e., `r formatC(n_planning_units[2], big.mark = ",")` planning units) and see how the timings compare. All the solvers have a similar run time now. Interestingly, the _Gurobi_ solver is the slowest -- but only by a couple of minutes -- for these benchmark parameters. ```{r "min_short_pu_n_2 with boundary penalty"} plot_benchmark( @@ -502,6 +502,6 @@ plot_benchmark( boundary_penalty = boundary_penalty_values$add_min_shortfall_objective[3]) ``` -# Conclusion +## Conclusion -The benchmark results demonstrate that the time required to solve a conservation planning problem can vary considerably depending on the size and complexity of the problem, and also the solver used to generate the prioritization. Indeed, some solvers (e.g. _Rsymphony_ solver) may require many hours to solve a problem that other solvers (e.g. _CBC_ or _Gurobi_ solvers) can solve within minutes. Broadly speaking, we recommend using the _Gurobi_ and _IBM CPLEX_ solvers where possible. This is because they often have the best performance. Although academics can obtain a special license to use these solvers at no cost, conservation planners working in governmental or non-governmental organizations may not have access to these solvers. In such cases, we recommend using the _CBC_ solver because it generally has better performance than the other open source solvers (i.e. the _Rsymphony_ and _lpsymphony_ solvers). Since the _CBC_ solver did not always have better performance than the other open source solvers, we recommend trying the _lpsymphony_ solver if the _CBC_ solver is taking a long time to solve a particular problem. +The benchmark results demonstrate that the time required to solve a conservation planning problem can vary considerably depending on the size and complexity of the problem, and also the solver used to generate the prioritization. Indeed, some solvers (e.g., _Rsymphony_ solver) may require many hours to solve a problem that other solvers (e.g., _CBC_ or _Gurobi_ solvers) can solve within minutes. Broadly speaking, we recommend using the _Gurobi_ and _IBM CPLEX_ solvers where possible. This is because they often have the best performance. Although academics can obtain a special license to use these solvers at no cost, conservation planners working in governmental or non-governmental organizations may not have access to these solvers. In such cases, we recommend using the _CBC_ solver because it generally has better performance than the other open source solvers (i.e., the _Rsymphony_ and _lpsymphony_ solvers). Since the _CBC_ solver did not always have better performance than the other open source solvers, we recommend trying the _lpsymphony_ solver if the _CBC_ solver is taking a long time to solve a particular problem. diff --git a/vignettes/tasmania.Rmd b/vignettes/tasmania.Rmd deleted file mode 100644 index 62aaddab1..000000000 --- a/vignettes/tasmania.Rmd +++ /dev/null @@ -1,225 +0,0 @@ ---- -title: "Tasmania Tutorial" -output: - rmarkdown::html_vignette: - toc: true - fig_caption: true - self_contained: yes -fontsize: 11pt -documentclass: article -bibliography: references.bib -csl: reference-style.csl -vignette: > - %\VignetteIndexEntry{Tasmania Tutorial} - %\VignetteEngine{knitr::rmarkdown_notangle} ---- - -```{r, include = FALSE} -h <- 3.5 -w <- 3.5 -is_check <- ("CheckExEnv" %in% search()) || any(c("_R_CHECK_TIMINGS_", - "_R_CHECK_LICENSE_") %in% names(Sys.getenv())) -knitr::opts_chunk$set(fig.align = "center", eval = !is_check) -``` - -## Introduction - -The aim of this tutorial is to provide a worked example of how vector-based data can be used to develop conservation prioritizations using the _prioritizr R_ package. It is also written for conservation planners that have used the _Marxan_ decision support tool [@r3] and are interested in applying the _prioritizr R_ package to their own work. The dataset used in this tutorial was obtained from the _Introduction to Marxan_ course. This data was originally a subset of a larger spatial prioritization project performed under contract to Australia's Department of Environment and Water Resources [@r30]. - -Please note that this tutorial uses data from the _prioritizrdata R_, so ensure that it is installed before trying out the code yourself. - -## Exploring the data - -This dataset contains two items. First, a spatial planning unit layer that has an attribute table which contains three columns: integer unique identifiers ("id"), unimproved land values ("cost"), and their existing level of protection ("status"). Units with 50 % or more of their area contained in protected areas are associated with a status of 2, otherwise they are associated with a value of 0. If you are familiar with the _Marxan_ decision support tool, then you will notice that these columns are formatted in a similar manner to the input data for _Marxan_. For _Marxan_ input data, planning units must be described in a table containing one row for each planning unit with a unique identifier and the corresponding cost. - -The second item in this dataset is the raster-based feature data. Specifically, the feature data is expressed as a stack of rasters (termed a `RasterStack` object). Here each layer in the stack represents the distribution of a different vegetation class in Tasmania, Australia. There are 62 vegetation classes in total. For a given layer, pixel values indicate the presence (value of 1) or absence (value of 0) of the vegetation class in an area. - -First, load the required packages and the data. - -```{r, message = FALSE} -# load packages -library(prioritizrdata) -library(prioritizr) - -# load planning unit data -data(tas_pu) - -# load conservation feature data -data(tas_features) -``` - -Now, let's have a look at the planning unit data. - -```{r, fig.width = w, fig.height = h} -# print planning unit data -print(tas_pu) - -# plot map of planning unit costs -plot(st_as_sf(tas_pu[, "cost"]), main = "Planning unit costs") -``` - -Next, let examine the feature data. Here we will only plot the first four features as an example. The pixel values denote the presence or absence of each feature within the extent of the study area. - -```{r, fig.width = 4.5, fig.height = 4.5} -# print planning unit data -print(tas_features) - -# plot map of the first four vegetation classes -plot(tas_features[[1:4]], main = paste("Feature", 1:4)) -``` - -The planning units in this example are a spatial polygon layer---not a raster---and so there can be considerable flexibility in the shape of the planning units. That said, there is a trade-off with the efficiency of data pre-processing in vector-based planning unit data compared to raster-based planning unit data. Vector-based planning unit data generally require more time to complete pre-processing computations (e.g. overlaying the planning unit data with the feature data, or generating boundary data). As a consequence, we generally recommend using raster-based planning unit data where possible to reduce processing time---but note that this is not possible when not all planning units are equal-sized squares. Another strategy is to complete the pre-processing in other software environments (e.g. _ArcGIS_) and use the pre-processed data directly with the _prioritizr_ package. - -## _Marxan_ problem formulation - -Here, we will provide an example of using the `marxan_problem` function to build and solve a typical _Marxan_ conservation planning problem. Then we will show how this same problem can be built and solved using the fully customizable `problem` function as a comparison. - -The dataset used in this example follows many of the conventions used by the _Marxan_ decision support tool. As a consequence, it is not too difficult to format the data for use with the `marxan_problem` function. The `marxan_problem` function is essentially a wrapper to the `problem` function. This means that when we solve problems created using the `marxan_problem` function, we will solve them using exact algorithms and not the simulated annealing algorithm used by _Marxan_. - -All problem objects formulated with `marxan_problem` use the minimum set objective. Targets can be either relative or absolute, and planning units can be specified for masking in or out using the `locked_in` and `locked_out` arguments. To favor clumped solutions, use the `penalty` argument to impose a penalty on solutions with high boundary lengths (equivalent to the Boundary Length Modifier (BLM) used in _Marxan_), and the `edge_factor` argument to scale the penalty for edges that do not have neighboring planning units, such as the coastline. For simplicity we set all of the targets at the same level, 17 %, to reflect the [Aichi](https://www.cbd.int/sp/targets/) biodiversity target to "safeguard" at least 17% of terrestrial ecosystems by 2020. For example, to prioritize planning units in Tasmania that meet the 17 % representation target at the least cost. - -First, we will convert the vector-based planning unit data and raster-based feature data into the tabular formats required by the `marxan_problem` function. These formats are very similar to the formats used by _Marxan_. - -```{r} -# create table with planning unit data -pu_data <- tas_pu@data - -# print first six rows -head(pu_data) - -# create table with the feature identifiers, names, and targets -spec_data <- data.frame(id = seq_len(nlayers(tas_features)), - name = paste0("veg", seq_len(nlayers(tas_features))), - prop = 0.17) - -# print first six rows -head(spec_data) - -# create table with the planning unit vs. feature data -puvspr_data <- rij_matrix(tas_pu, tas_features) -puvspr_data <- as(puvspr_data, "dgTMatrix") -puvspr_data <- data.frame(pu = puvspr_data@j + 1, species = puvspr_data@i + 1, - amount = puvspr_data@x) - -# print first six rows -head(puvspr_data) - -# create table with the boundary data -bound_data <- boundary_matrix(tas_pu) -bound_data <- as(bound_data, "dgTMatrix") -bound_data <- data.frame(id1 = bound_data@i + 1, id2 = bound_data@j + 1, - boundary = bound_data@x) -# print first six rows -head(bound_data) -``` - -Now that we have converted the data to tabular format, we can use the `marxan_problem` function to create a conservation planning problem. - -```{r} -# create problem -p1 <- marxan_problem(pu_data, spec_data, puvspr_data, bound_data, - blm = 0.0005) - -# print problem -print(p1) -``` - -Next, we can solve the problem (see [`?solve`](https://prioritizr.net/reference/solve.html) for more information). The _prioritizr_ R package supports three different exact algorithm software packages: _gurobi_, _Rsymphony_, and _lpsymphony_. There are costs and benefits associated with each of these different solvers, but each software should return similar results. Note that you will need at least one of these package installed on your system to solve problems. We recommend using the _gurobi_ solver if possible, and have used this solver when building this tutorial. After solving the problem, we will calculate some statistics to describe the solution. Note that the planning unit selections are stored in the "solution_1" column of the solution object. - -```{r, fig.width = w, fig.height = h} -# solve problem -s1 <- solve(p1) - -# print first six rows of solution object -head(s1) - -# count number of planning units in solution -sum(s1$solution_1) - -# proportion of planning units in solution -mean(s1$solution_1) - -# calculate feature representation -r1 <- eval_feature_representation_summary(p1, s1[, "solution_1", drop = FALSE]) -print(r1) - -# visualize the solution by converting the solution to a spatial object -s1 <- SpatialPolygonsDataFrame(as(tas_pu, "SpatialPolygons"), data = s1) -s1$solution_1 <- factor(s1$solution_1) -plot(st_as_sf(s1[, "solution_1"]), pal = c("grey90", "darkgreen"), - main = "marxan_problem solution") -``` - -## General problem formulation - -Now we will formulate the exact same problem using the `problem` function and the spatially referenced data. We recommend using this approach instead of the `marxan_problem` because it is more verbose and you can specify exactly how the conservation planning problem should be formulated. - -```{r, fig.width = w, fig.height = h} -# build problem -p2 <- problem(tas_pu, tas_features, cost_column = "cost") %>% - add_min_set_objective() %>% - add_relative_targets(0.17) %>% - add_locked_in_constraints("locked_in") %>% - add_binary_decisions() %>% - add_boundary_penalties(penalty = 0.0005, edge_factor = 1) - -# print the problem -print(p2) - -# solve problem -s2 <- solve(p2) - -# calculate feature representation -r2 <- eval_feature_representation_summary(p2, s2[, "solution_1"]) -print(r2) - -# visualize solution -s2$solution_1 <- factor(s2$solution_1) -plot(st_as_sf(s2[, "solution_1"]), pal = c("grey90", "darkgreen"), - main = "problem solution") -``` - -## Selection frequencies - -Similar to the _Marxan_ decision support tool, we can generate a portfolio of solutions and compute the planning unit selection frequencies to understand their relative importance. This can be useful when trying to understand which planning units in the solution are the most irreplaceable. To do this, we will create a portfolio containing 1000 solutions within 20% of optimality, and calculate the number of times that each planning unit was selected. Note that this requires the _Gurobi_ software to be installed. - -```{r} -# create new problem with a portfolio added to it -p3 <- p2 %>% - add_gap_portfolio(number_solutions = 1000, pool_gap = 0.2) - -# print problem -print(p3) -``` - -```{r, message = FALSE, results = "hide"} -# generate solutions -s3 <- solve(p3) - -# find column numbers with the solutions -solution_columns <- which(grepl("solution", names(s3))) - -# calculate selection frequencies -s3$selection_frequencies <- rowSums(as.matrix(s3@data[, solution_columns])) -``` - -```{r, fig.width = 4.5, fig.height = 4.5} -# plot first four solutions in the portfolio -s3$solution_1 <- factor(s3$solution_1) -s3$solution_2 <- factor(s3$solution_2) -s3$solution_3 <- factor(s3$solution_3) -s3$solution_4 <- factor(s3$solution_4) -plot(st_as_sf(s3[, c("solution_1", "solution_2", "solution_3", "solution_4")]), - pal = c("grey90", "darkgreen")) -``` - -```{r, fig.width = w, fig.height = h} -# plot histogram of selection frequencies -hist(s3$selection_frequencies, main = "Selection frequencies", - xlab = "Number of times units were selected") - -# plot spatial distribution of the selection frequencies -plot(st_as_sf(s3[, "selection_frequencies"]), main = "Selection frequencies") -``` - -## References
    -
    -

    Calculate how well features are represented by a solution -to a conservation planning problem(). +to a conservation planning problem(). These summary statistics are reported for each and every feature, and each and every zone, within a conservation planning problem.

    -
    eval_feature_representation_summary(x, solution)
    +    
    Usage,
    eval_feature_representation_summary(x, solution)
     
    -# S4 method for ConservationProblem,numeric
    -eval_feature_representation_summary(x, solution)
    +# S4 method for ConservationProblem,numeric
    +eval_feature_representation_summary(x, solution)
     
    -# S4 method for ConservationProblem,matrix
    -eval_feature_representation_summary(x, solution)
    +# S4 method for ConservationProblem,matrix
    +eval_feature_representation_summary(x, solution)
     
    -# S4 method for ConservationProblem,data.frame
    -eval_feature_representation_summary(x, solution)
    +# S4 method for ConservationProblem,data.frame
    +eval_feature_representation_summary(x, solution)
     
    -# S4 method for ConservationProblem,Spatial
    -eval_feature_representation_summary(x, solution)
    +# S4 method for ConservationProblem,Spatial
    +eval_feature_representation_summary(x, solution)
     
    -# S4 method for ConservationProblem,sf
    -eval_feature_representation_summary(x, solution)
    +# S4 method for ConservationProblem,sf
    +eval_feature_representation_summary(x, solution)
     
    -# S4 method for ConservationProblem,Raster
    -eval_feature_representation_summary(x, solution)
    +# S4 method for ConservationProblem,Raster +eval_feature_representation_summary(x, solution)
    -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    solution

    numeric, matrix, data.frame, -Raster, Spatial, -or sf::sf() object. +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    solution
    +

    numeric, matrix, data.frame, +Raster, Spatial, +or sf::sf() object. The argument should be in the same format as the planning unit cost data in the argument to x. -See the Solution format section for more information.

    - -

    Value

    - -

    tibble::tibble() object describing feature representation. +See the Solution format section for more information.

    +
    +
    +

    Value

    +

    tibble::tibble() object describing feature representation. Here, each row describes a specific summary statistic -(e.g. different management zone) for a specific feature. -It contains the following columns:

    -
    - -
    summary

    character description of the summary statistic. +(e.g., different management zone) for a specific feature. +It contains the following columns:

    summary
    +

    character description of the summary statistic. The statistic associated with the "overall" value in this column is calculated using the entire solution (including all management zones if there are multiple zones). @@ -244,47 +153,57 @@

    Value

    are also provided for each zone separately (indicated using zone names).

    -
    feature

    character name of the feature.

    -
    total_amount

    numeric total amount of each feature available +

    feature
    +

    character name of the feature.

    + + +
    total_amount
    +

    numeric total amount of each feature available in the entire conservation planning problem (not just planning units selected within the solution). It is calculated as the sum of the feature data, -supplied when creating a problem() object -(e.g. presence/absence values).

    +supplied when creating a problem() object +(e.g., presence/absence values).

    -
    absolute_held

    numeric total amount of each feature secured within + +

    absolute_held
    +

    numeric total amount of each feature secured within the solution. It is calculated as the sum of the feature data, -supplied when creating a problem() object -(e.g. presence/absence values), weighted by the status of each -planning unit in the solution (e.g. selected or not for prioritization).

    +supplied when creating a problem() object +(e.g., presence/absence values), weighted by the status of each +planning unit in the solution (e.g., selected or not for +prioritization).

    + -
    relative_held

    numeric proportion of +

    relative_held
    +

    numeric proportion of each feature secured within the solution. It is calculated by dividing values in the "absolute_held" column by those in the "total_amount" column.

    -
    - -

    Solution format

    +
    +
    +

    Solution format

    Broadly speaking, the argument to solution must be in the same format as the planning unit data in the argument to x. Further details on the correct format are listed separately for each of the different planning unit data formats:

    -
    - -
    x has numeric planning units

    The argument to solution must be a +

    x has numeric planning units
    +

    The argument to solution must be a numeric vector with each element corresponding to a different planning unit. It should have the same number of planning units as those in the argument to x. Additionally, any planning units missing cost (NA) values should also have missing (NA) values in the argument to solution.

    -
    x has matrix planning units

    The argument to solution must be a + +

    x has matrix planning units
    +

    The argument to solution must be a matrix vector with each row corresponding to a different planning unit, and each column correspond to a different management zone. It should have the same number of planning units and zones @@ -292,8 +211,10 @@

    Raster planning units +

    The argument to solution +be a Raster object where different grid cells (pixels) correspond to different planning units and layers correspond to a different management zones. It should have the same dimensionality (rows, columns, layers), resolution, extent, and coordinate reference @@ -301,35 +222,41 @@

    sf::sf() data). Additionally, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has Spatial planning units

    The argument to solution -must be a Spatial object with each column corresponding to a + +

    x has Spatial planning units
    +

    The argument to solution +must be a Spatial object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -Spatial object containing the solution also contains additional +Spatial object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this -function (see below for example with sf::sf() data). +function (see below for example with sf::sf() data). Additionally, the argument to solution must also have the same coordinate reference system as the planning unit data. Furthermore, any planning units missing cost (NA) values for a particular zone should also have missing (NA) values in the argument to solution.

    -
    x has sf::sf() planning units

    The argument to solution must be -a sf::sf() object with each column corresponding to a different + +

    x has sf::sf() planning units
    +

    The argument to solution must be +a sf::sf() object with each column corresponding to a different zone, each row corresponding to a different planning unit, and cell values corresponding to the solution value. This means that if the -sf::sf() object containing the solution also contains additional +sf::sf() object containing the solution also contains additional columns, then these columns will need to be subsetted prior to using this function (see below for example). Additionally, the argument to solution must also have the same @@ -338,342 +265,340 @@

    See also

    -

    See summaries for an overview of all functions for summarizing solutions.

    +

    +
    +

    See also

    +

    See summaries for an overview of all functions for summarizing solutions.

    Other summaries: -eval_boundary_summary(), -eval_connectivity_summary(), -eval_cost_summary(), -eval_n_summary(), -eval_target_coverage_summary()

    - -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_raster, sim_pu_polygons, sim_pu_zones_sf, sim_features,
    -     sim_pu_zones_stack, sim_features_zones)
    -
    -# create a simple conservation planning dataset so we can see exactly
    -# how feature representation is calculated
    -pu <- data.frame(id = seq_len(10), cost = c(0.2, NA, runif(8)),
    -                 spp1 = runif(10), spp2 = c(rpois(9, 4), NA))
    -
    -# create problem
    -p1 <- problem(pu, c("spp1", "spp2"), cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create a solution
    -# specifically, a data.frame with a single column that contains
    -# binary values indicating if each planning units was selected or not
    -s1 <- data.frame(s = c(1, NA, rep(c(1, 0), 4)))
    -print(s1)
    -#>     s
    -#> 1   1
    -#> 2  NA
    -#> 3   1
    -#> 4   0
    -#> 5   1
    -#> 6   0
    -#> 7   1
    -#> 8   0
    -#> 9   1
    -#> 10  0
    -
    -# calculate feature representation
    -r1 <- eval_feature_representation_summary(p1, s1)
    -print(r1)
    -#> # A tibble: 2 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall spp1            5.76          3.12         0.541
    -#> 2 overall spp2           33            14            0.424
    -
    -# let's verify that feature representation calculations are correct
    -# by manually performing the calculations and compare the results with r1
    -## calculate total amount for each feature
    -print(setNames(
    -  c(sum(pu$spp1, na.rm = TRUE),
    -    sum(pu$spp2, na.rm = TRUE)),
    -  c("spp1", "spp2")))
    -#>      spp1      spp2 
    -#>  5.755739 33.000000 
    -
    -## calculate absolute amount held for each feature
    -print(setNames(
    -  c(sum(pu$spp1 * s1$s, na.rm = TRUE),
    -    sum(pu$spp2 * s1$s, na.rm = TRUE)),
    -  c("spp1", "spp2")))
    -#>      spp1      spp2 
    -#>  3.116052 14.000000 
    -
    -## calculate relative amount held for each feature
    -print(setNames(
    -  c(sum(pu$spp1 * s1$s, na.rm = TRUE) / sum(pu$spp1, na.rm = TRUE),
    -    sum(pu$spp2 * s1$s, na.rm = TRUE) / sum(pu$spp2, na.rm = TRUE)),
    -  c("spp1", "spp2")))
    -#>      spp1      spp2 
    -#> 0.5413818 0.4242424 
    -
    -# \dontrun{
    -# solve the problem using an exact algorithm solver
    -s1_2 <- solve(p1)
    -print(s1_2)
    -#>    id      cost      spp1 spp2 solution_1
    -#> 1   1 0.2000000 0.8288314    4          1
    -#> 2   2        NA 0.7115770    3         NA
    -#> 3   3 0.8336000 0.2820609    1          0
    -#> 4   4 0.7250118 0.8928427    6          0
    -#> 5   5 0.9753142 0.7647062    1          0
    -#> 6   6 0.4676038 0.1643470    4          0
    -#> 7   7 0.8122781 0.7320744    3          0
    -#> 8   8 0.2056958 0.2531062    6          0
    -#> 9   9 0.5121819 0.5083795    5          0
    -#> 10 10 0.9254660 0.6178138   NA          0
    -
    -# calculate feature representation in this solution
    -r1_2 <- eval_feature_representation_summary(
    -  p1, s1_2[, "solution_1", drop = FALSE])
    -print(r1_2)
    -#> # A tibble: 2 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall spp1            5.76         0.829         0.144
    -#> 2 overall spp2           33            4             0.121
    -
    -# build minimal conservation problem with raster data
    -p2 <- problem(sim_pu_raster, sim_features) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# solve the problem
    -s2 <- solve(p2)
    -
    -# print solution
    -print(s2)
    -#> class      : RasterLayer 
    -#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> source     : memory
    -#> names      : layer 
    -#> values     : 0, 1  (min, max)
    -#> 
    -
    -# calculate feature representation in the solution
    -r2 <- eval_feature_representation_summary(p2, s2)
    -print(r2)
    -#> # A tibble: 5 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall layer.1         83.3          8.91         0.107
    -#> 2 overall layer.2         31.2          3.13         0.100
    -#> 3 overall layer.3         72.0          7.34         0.102
    -#> 4 overall layer.4         42.7          4.35         0.102
    -#> 5 overall layer.5         56.7          6.01         0.106
    -
    -# plot solution
    -plot(s2, main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# build minimal conservation problem with polygon (Spatial) data
    -p3 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s3 <- solve(p3)
    -
    -# print first six rows of the attribute table
    -print(head(s3))
    -#>       cost locked_in locked_out solution_1
    -#> 1 215.8638     FALSE      FALSE          0
    -#> 2 212.7823     FALSE      FALSE          0
    -#> 3 207.4962     FALSE      FALSE          0
    -#> 4 208.9322     FALSE       TRUE          0
    -#> 5 214.0419     FALSE      FALSE          0
    -#> 6 213.7636     FALSE      FALSE          0
    -
    -# calculate feature representation in the solution
    -r3 <- eval_feature_representation_summary(p3, s3[, "solution_1"])
    -print(r3)
    -#> # A tibble: 5 × 5
    -#>   summary feature total_amount absolute_held relative_held
    -#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    -#> 1 overall layer.1         74.5          8.05         0.108
    -#> 2 overall layer.2         28.1          2.83         0.101
    -#> 3 overall layer.3         64.9          6.65         0.103
    -#> 4 overall layer.4         38.2          3.87         0.101
    -#> 5 overall layer.5         50.7          5.41         0.107
    -
    -# plot solution
    -spplot(s3, zcol = "solution_1", main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# build multi-zone conservation problem with raster data
    -p4 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s4 <- solve(p4)
    -
    -# print solution
    -print(s4)
    -#> class      : RasterStack 
    -#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    -#> resolution : 0.1, 0.1  (x, y)
    -#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    -#> crs        : NA 
    -#> names      : zone_1, zone_2, zone_3 
    -#> min values :      0,      0,      0 
    -#> max values :      1,      1,      1 
    -#> 
    -
    -# calculate feature representation in the solution
    -r4 <- eval_feature_representation_summary(p4, s4)
    -print(r4)
    -#> # A tibble: 20 × 5
    -#>    summary feature   total_amount absolute_held relative_held
    -#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    -#>  1 overall feature_1        250.          45.8          0.183
    -#>  2 overall feature_2         93.6         17.7          0.189
    -#>  3 overall feature_3        216.          40.2          0.186
    -#>  4 overall feature_4        128.          23.7          0.186
    -#>  5 overall feature_5        170.          31.2          0.183
    -#>  6 zone_1  feature_1         83.3         16.0          0.192
    -#>  7 zone_1  feature_2         31.2          5.07         0.162
    -#>  8 zone_1  feature_3         72.0         13.2          0.183
    -#>  9 zone_1  feature_4         42.7          7.04         0.165
    -#> 10 zone_1  feature_5         56.7         11.2          0.197
    -#> 11 zone_2  feature_1         83.3         16.0          0.192
    -#> 12 zone_2  feature_2         31.2          6.45         0.207
    -#> 13 zone_2  feature_3         72.0         15.1          0.210
    -#> 14 zone_2  feature_4         42.7          8.22         0.193
    -#> 15 zone_2  feature_5         56.7         10.4          0.184
    -#> 16 zone_3  feature_1         83.3         13.8          0.166
    -#> 17 zone_3  feature_2         31.2          6.16         0.197
    -#> 18 zone_3  feature_3         72.0         11.9          0.165
    -#> 19 zone_3  feature_4         42.7          8.49         0.199
    -#> 20 zone_3  feature_5         56.7          9.58         0.169
    -
    -# plot solution
    -plot(category_layer(s4), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    -# build multi-zone conservation problem with polygon (sf) data
    -p5 <- problem(sim_pu_zones_sf, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -# \dontrun{
    -# solve the problem
    -s5 <- solve(p5)
    -
    -# print first six rows of the attribute table
    -print(head(s5))
    -#> Simple feature collection with 6 features and 9 fields
    -#> Geometry type: POLYGON
    -#> Dimension:     XY
    -#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    -#> CRS:           NA
    -#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    -#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    -#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    -#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    -#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    -#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    -#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    -#>   solution_1_zone_2 solution_1_zone_3                       geometry
    -#> 1                 1                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    -#> 2                 0                 0 POLYGON ((0.1 1, 0.2 1, 0.2...
    -#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    -#> 4                 0                 0 POLYGON ((0.3 1, 0.4 1, 0.4...
    -#> 5                 0                 0 POLYGON ((0.4 1, 0.5 1, 0.5...
    -#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    -
    -# calculate feature representation in the solution
    -r5 <- eval_feature_representation_summary(
    -  p5, s5[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -print(r5)
    -#> # A tibble: 20 × 5
    -#>    summary feature   total_amount absolute_held relative_held
    -#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    -#>  1 overall feature_1        225.          40.9          0.182
    -#>  2 overall feature_2         83.9         13.8          0.164
    -#>  3 overall feature_3        195.          34.3          0.176
    -#>  4 overall feature_4        114.          18.7          0.164
    -#>  5 overall feature_5        154.          28.3          0.184
    -#>  6 zone_1  feature_1         75.1         14.9          0.198
    -#>  7 zone_1  feature_2         28.0          4.77         0.170
    -#>  8 zone_1  feature_3         65.0         12.7          0.195
    -#>  9 zone_1  feature_4         38.0          6.39         0.168
    -#> 10 zone_1  feature_5         51.2         10.2          0.200
    -#> 11 zone_2  feature_1         75.1         15.0          0.199
    -#> 12 zone_2  feature_2         28.0          5.02         0.179
    -#> 13 zone_2  feature_3         65.0         12.9          0.199
    -#> 14 zone_2  feature_4         38.0          6.03         0.158
    -#> 15 zone_2  feature_5         51.2         10.2          0.199
    -#> 16 zone_3  feature_1         75.1         11.1          0.148
    -#> 17 zone_3  feature_2         28.0          3.98         0.142
    -#> 18 zone_3  feature_3         65.0          8.71         0.134
    -#> 19 zone_3  feature_4         38.0          6.33         0.166
    -#> 20 zone_3  feature_5         51.2          7.84         0.153
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s5$solution <- category_vector(
    -  s5[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    -s5$solution <- factor(s5$solution)
    -
    -# plot solution
    -plot(s5[, "solution"])
    -
    -# }
    +eval_boundary_summary(),
    +eval_connectivity_summary(),
    +eval_cost_summary(),
    +eval_n_summary(),
    +eval_target_coverage_summary()

    +
    + +
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_raster, sim_pu_polygons, sim_pu_zones_sf, sim_features,
    +     sim_pu_zones_stack, sim_features_zones)
    +
    +# create a simple conservation planning dataset so we can see exactly
    +# how feature representation is calculated
    +pu <- data.frame(id = seq_len(10), cost = c(0.2, NA, runif(8)),
    +                 spp1 = runif(10), spp2 = c(rpois(9, 4), NA))
    +
    +# create problem
    +p1 <- problem(pu, c("spp1", "spp2"), cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create a solution
    +# specifically, a data.frame with a single column that contains
    +# binary values indicating if each planning units was selected or not
    +s1 <- data.frame(s = c(1, NA, rep(c(1, 0), 4)))
    +print(s1)
    +#>     s
    +#> 1   1
    +#> 2  NA
    +#> 3   1
    +#> 4   0
    +#> 5   1
    +#> 6   0
    +#> 7   1
    +#> 8   0
    +#> 9   1
    +#> 10  0
    +
    +# calculate feature representation
    +r1 <- eval_feature_representation_summary(p1, s1)
    +print(r1)
    +#> # A tibble: 2 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall spp1            5.76          3.12         0.541
    +#> 2 overall spp2           33            14            0.424
    +
    +# let's verify that feature representation calculations are correct
    +# by manually performing the calculations and compare the results with r1
    +## calculate total amount for each feature
    +print(setNames(
    +  c(sum(pu$spp1, na.rm = TRUE),
    +    sum(pu$spp2, na.rm = TRUE)),
    +  c("spp1", "spp2")))
    +#>      spp1      spp2 
    +#>  5.755739 33.000000 
    +
    +## calculate absolute amount held for each feature
    +print(setNames(
    +  c(sum(pu$spp1 * s1$s, na.rm = TRUE),
    +    sum(pu$spp2 * s1$s, na.rm = TRUE)),
    +  c("spp1", "spp2")))
    +#>      spp1      spp2 
    +#>  3.116052 14.000000 
    +
    +## calculate relative amount held for each feature
    +print(setNames(
    +  c(sum(pu$spp1 * s1$s, na.rm = TRUE) / sum(pu$spp1, na.rm = TRUE),
    +    sum(pu$spp2 * s1$s, na.rm = TRUE) / sum(pu$spp2, na.rm = TRUE)),
    +  c("spp1", "spp2")))
    +#>      spp1      spp2 
    +#> 0.5413818 0.4242424 
    +
    +# \dontrun{
    +# solve the problem using an exact algorithm solver
    +s1_2 <- solve(p1)
    +print(s1_2)
    +#>    id      cost      spp1 spp2 solution_1
    +#> 1   1 0.2000000 0.8288314    4          1
    +#> 2   2        NA 0.7115770    3         NA
    +#> 3   3 0.8336000 0.2820609    1          0
    +#> 4   4 0.7250118 0.8928427    6          0
    +#> 5   5 0.9753142 0.7647062    1          0
    +#> 6   6 0.4676038 0.1643470    4          0
    +#> 7   7 0.8122781 0.7320744    3          0
    +#> 8   8 0.2056958 0.2531062    6          0
    +#> 9   9 0.5121819 0.5083795    5          0
    +#> 10 10 0.9254660 0.6178138   NA          0
    +
    +# calculate feature representation in this solution
    +r1_2 <- eval_feature_representation_summary(
    +  p1, s1_2[, "solution_1", drop = FALSE])
    +print(r1_2)
    +#> # A tibble: 2 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall spp1            5.76         0.829         0.144
    +#> 2 overall spp2           33            4             0.121
    +
    +# build minimal conservation problem with raster data
    +p2 <- problem(sim_pu_raster, sim_features) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# solve the problem
    +s2 <- solve(p2)
    +
    +# print solution
    +print(s2)
    +#> class      : RasterLayer 
    +#> dimensions : 10, 10, 100  (nrow, ncol, ncell)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> source     : memory
    +#> names      : layer 
    +#> values     : 0, 1  (min, max)
    +#> 
    +
    +# calculate feature representation in the solution
    +r2 <- eval_feature_representation_summary(p2, s2)
    +print(r2)
    +#> # A tibble: 5 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall layer.1         83.3          8.91         0.107
    +#> 2 overall layer.2         31.2          3.13         0.100
    +#> 3 overall layer.3         72.0          7.34         0.102
    +#> 4 overall layer.4         42.7          4.35         0.102
    +#> 5 overall layer.5         56.7          6.01         0.106
    +
    +# plot solution
    +plot(s2, main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# build minimal conservation problem with polygon (Spatial) data
    +p3 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s3 <- solve(p3)
    +
    +# print first six rows of the attribute table
    +print(head(s3))
    +#>       cost locked_in locked_out solution_1
    +#> 1 215.8638     FALSE      FALSE          0
    +#> 2 212.7823     FALSE      FALSE          0
    +#> 3 207.4962     FALSE      FALSE          0
    +#> 4 208.9322     FALSE       TRUE          0
    +#> 5 214.0419     FALSE      FALSE          0
    +#> 6 213.7636     FALSE      FALSE          0
    +
    +# calculate feature representation in the solution
    +r3 <- eval_feature_representation_summary(p3, s3[, "solution_1"])
    +print(r3)
    +#> # A tibble: 5 × 5
    +#>   summary feature total_amount absolute_held relative_held
    +#>   <chr>   <chr>          <dbl>         <dbl>         <dbl>
    +#> 1 overall layer.1         74.5          8.05         0.108
    +#> 2 overall layer.2         28.1          2.83         0.101
    +#> 3 overall layer.3         64.9          6.65         0.103
    +#> 4 overall layer.4         38.2          3.87         0.101
    +#> 5 overall layer.5         50.7          5.41         0.107
    +
    +# plot solution
    +spplot(s3, zcol = "solution_1", main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# build multi-zone conservation problem with raster data
    +p4 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s4 <- solve(p4)
    +
    +# print solution
    +print(s4)
    +#> class      : RasterStack 
    +#> dimensions : 10, 10, 100, 3  (nrow, ncol, ncell, nlayers)
    +#> resolution : 0.1, 0.1  (x, y)
    +#> extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
    +#> crs        : NA 
    +#> names      : zone_1, zone_2, zone_3 
    +#> min values :      0,      0,      0 
    +#> max values :      1,      1,      1 
    +#> 
    +
    +# calculate feature representation in the solution
    +r4 <- eval_feature_representation_summary(p4, s4)
    +print(r4)
    +#> # A tibble: 20 × 5
    +#>    summary feature   total_amount absolute_held relative_held
    +#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    +#>  1 overall feature_1        250.          45.8          0.183
    +#>  2 overall feature_2         93.6         17.7          0.189
    +#>  3 overall feature_3        216.          40.2          0.186
    +#>  4 overall feature_4        128.          23.7          0.186
    +#>  5 overall feature_5        170.          31.2          0.183
    +#>  6 zone_1  feature_1         83.3         16.0          0.192
    +#>  7 zone_1  feature_2         31.2          5.07         0.162
    +#>  8 zone_1  feature_3         72.0         13.2          0.183
    +#>  9 zone_1  feature_4         42.7          7.04         0.165
    +#> 10 zone_1  feature_5         56.7         11.2          0.197
    +#> 11 zone_2  feature_1         83.3         16.0          0.192
    +#> 12 zone_2  feature_2         31.2          6.45         0.207
    +#> 13 zone_2  feature_3         72.0         15.1          0.210
    +#> 14 zone_2  feature_4         42.7          8.22         0.193
    +#> 15 zone_2  feature_5         56.7         10.4          0.184
    +#> 16 zone_3  feature_1         83.3         13.8          0.166
    +#> 17 zone_3  feature_2         31.2          6.16         0.197
    +#> 18 zone_3  feature_3         72.0         11.9          0.165
    +#> 19 zone_3  feature_4         42.7          8.49         0.199
    +#> 20 zone_3  feature_5         56.7          9.58         0.169
    +
    +# plot solution
    +plot(category_layer(s4), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
    +# build multi-zone conservation problem with polygon (sf) data
    +p5 <- problem(sim_pu_zones_sf, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(matrix(runif(15, 0.1, 0.2), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +# \dontrun{
    +# solve the problem
    +s5 <- solve(p5)
    +
    +# print first six rows of the attribute table
    +print(head(s5))
    +#> Simple feature collection with 6 features and 9 fields
    +#> Geometry type: POLYGON
    +#> Dimension:     XY
    +#> Bounding box:  xmin: 0 ymin: 0.9 xmax: 0.6 ymax: 1
    +#> CRS:           NA
    +#>     cost_1   cost_2   cost_3 locked_1 locked_2 locked_3 solution_1_zone_1
    +#> 1 215.8638 183.3344 205.4113    FALSE    FALSE    FALSE                 0
    +#> 2 212.7823 189.4978 209.6404    FALSE    FALSE    FALSE                 0
    +#> 3 207.4962 193.6007 215.4212     TRUE    FALSE    FALSE                 0
    +#> 4 208.9322 197.5897 218.5241    FALSE    FALSE    FALSE                 0
    +#> 5 214.0419 199.8033 220.7100    FALSE    FALSE    FALSE                 0
    +#> 6 213.7636 203.1867 224.6809    FALSE    FALSE    FALSE                 0
    +#>   solution_1_zone_2 solution_1_zone_3                       geometry
    +#> 1                 1                 0 POLYGON ((0 1, 0.1 1, 0.1 0...
    +#> 2                 0                 0 POLYGON ((0.1 1, 0.2 1, 0.2...
    +#> 3                 0                 0 POLYGON ((0.2 1, 0.3 1, 0.3...
    +#> 4                 0                 0 POLYGON ((0.3 1, 0.4 1, 0.4...
    +#> 5                 0                 0 POLYGON ((0.4 1, 0.5 1, 0.5...
    +#> 6                 0                 0 POLYGON ((0.5 1, 0.6 1, 0.6...
    +
    +# calculate feature representation in the solution
    +r5 <- eval_feature_representation_summary(
    +  p5, s5[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +print(r5)
    +#> # A tibble: 20 × 5
    +#>    summary feature   total_amount absolute_held relative_held
    +#>    <chr>   <chr>            <dbl>         <dbl>         <dbl>
    +#>  1 overall feature_1        225.          40.9          0.182
    +#>  2 overall feature_2         83.9         13.8          0.164
    +#>  3 overall feature_3        195.          34.3          0.176
    +#>  4 overall feature_4        114.          18.7          0.164
    +#>  5 overall feature_5        154.          28.3          0.184
    +#>  6 zone_1  feature_1         75.1         14.9          0.198
    +#>  7 zone_1  feature_2         28.0          4.77         0.170
    +#>  8 zone_1  feature_3         65.0         12.7          0.195
    +#>  9 zone_1  feature_4         38.0          6.39         0.168
    +#> 10 zone_1  feature_5         51.2         10.2          0.200
    +#> 11 zone_2  feature_1         75.1         15.0          0.199
    +#> 12 zone_2  feature_2         28.0          5.02         0.179
    +#> 13 zone_2  feature_3         65.0         12.9          0.199
    +#> 14 zone_2  feature_4         38.0          6.03         0.158
    +#> 15 zone_2  feature_5         51.2         10.2          0.199
    +#> 16 zone_3  feature_1         75.1         11.1          0.148
    +#> 17 zone_3  feature_2         28.0          3.98         0.142
    +#> 18 zone_3  feature_3         65.0          8.71         0.134
    +#> 19 zone_3  feature_4         38.0          6.33         0.166
    +#> 20 zone_3  feature_5         51.2          7.84         0.153
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s5$solution <- category_vector(
    +  s5[, c("solution_1_zone_1", "solution_1_zone_2", "solution_1_zone_3")])
    +s5$solution <- factor(s5$solution)
    +
    +# plot solution
    +plot(s5[, "solution"])
    +
    +# }
     
    +
    -
    -
    -

    Add constraints to a conservation planning problem() to ensure +

    Add constraints to a conservation planning problem() to ensure that specific planning units are not selected (or allocated to a specific zone) in the solution. For example, it may be useful to lock out planning units that have been degraded and are not suitable for conserving species. If specific planning units should be locked in to the solution, use add_locked_out_constraints(). For -problems with non-binary planning unit allocations (e.g. proportions), the -add_manual_locked_constraints() function can be used to lock +problems with non-binary planning unit allocations (e.g., proportions), the +add_manual_locked_constraints() function can be used to lock planning unit allocations to a specific value.

    -
    add_locked_out_constraints(x, locked_out)
    -
    -# S4 method for ConservationProblem,numeric
    -add_locked_out_constraints(x, locked_out)
    -
    -# S4 method for ConservationProblem,logical
    -add_locked_out_constraints(x, locked_out)
    +    
    Usage,
    add_locked_out_constraints(x, locked_out)
     
    -# S4 method for ConservationProblem,matrix
    -add_locked_out_constraints(x, locked_out)
    +# S4 method for ConservationProblem,numeric
    +add_locked_out_constraints(x, locked_out)
     
    -# S4 method for ConservationProblem,character
    -add_locked_out_constraints(x, locked_out)
    +# S4 method for ConservationProblem,logical
    +add_locked_out_constraints(x, locked_out)
     
    -# S4 method for ConservationProblem,Spatial
    -add_locked_out_constraints(x, locked_out)
    +# S4 method for ConservationProblem,matrix
    +add_locked_out_constraints(x, locked_out)
     
    -# S4 method for ConservationProblem,sf
    -add_locked_out_constraints(x, locked_out)
    +# S4 method for ConservationProblem,character
    +add_locked_out_constraints(x, locked_out)
     
    -# S4 method for ConservationProblem,Raster
    -add_locked_out_constraints(x, locked_out)
    +# S4 method for ConservationProblem,Spatial +add_locked_out_constraints(x, locked_out) -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    locked_out

    Object that determines which planning units that should be -locked out. See the Data format section for more information.

    +# S4 method for ConservationProblem,sf +add_locked_out_constraints(x, locked_out) -

    Value

    +# S4 method for ConservationProblem,Raster +add_locked_out_constraints(x, locked_out)
    -

    Object (i.e. ConservationProblem) with the constraints +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    locked_out
    +

    Object that determines which planning units that should be +locked out. See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Data format

    - +
    +
    +

    Data format

    The locked planning units can be specified using the following formats. Generally, the locked data should correspond to the planning units in the argument to x. To help make working with -Raster planning unit data easier, +Raster planning unit data easier, the locked data should correspond to cell indices in the -Raster data. For example, integer arguments +Raster data. For example, integer arguments should correspond to cell indices and logical arguments should have a value for each cell---regardless of which planning unit cells contain NA values.

    -
    - -
    data as an integer vector

    containing indices that indicate which +

    data as an integer vector
    +

    containing indices that indicate which planning units should be locked for the solution. This argument is only compatible with problems that contain a single zone.

    -
    data as a logical vector

    containing TRUE and/or + +

    data as a logical vector
    +

    containing TRUE and/or FALSE values that indicate which planning units should be locked in the solution. This argument is only compatible with problems that contain a single zone.

    -
    data as a matrix object

    containing logical TRUE and/or + +

    data as a matrix object
    +

    containing logical TRUE and/or FALSE values which indicate if certain planning units are should be locked to a specific zone in the solution. Each row corresponds to a planning unit, each column corresponds to a zone, and @@ -274,212 +189,216 @@

    TRUE value.

    -
    data as a character vector

    containing field (column) name(s) + +

    data as a character vector
    +

    containing field (column) name(s) that indicate if planning units should be locked for the solution. This format is only compatible if the planning units in the argument to x are a -Spatial, sf::sf(), or +Spatial, sf::sf(), or data.frame object. The fields -(columns) must have logical (i.e. TRUE or FALSE) +(columns) must have logical (i.e., TRUE or FALSE) values indicating if the planning unit is to be locked for the solution. For problems that contain a single zone, the argument to data must contain a single field name. Otherwise, for problems that contain multiple zones, the argument to data must contain a field name for each zone.

    -
    data as a Spatial or sf::sf() object

    containing geometries that will be used to lock planning units for + +

    data as a Spatial or sf::sf() object
    +

    containing geometries that will be used to lock planning units for the solution. Specifically, planning units in x that spatially -intersect with y will be locked (per intersecting_units()). +intersect with y will be locked (per intersecting_units()). Note that this option is only available for problems that contain a single management zone.

    -
    data as a Raster object

    containing cells used to lock + +

    data as a Raster object
    +

    containing cells used to lock planning units for the solution. Specifically, planning units in x that intersect with cells that have non-zero and non-NA values are locked. For problems that contain multiple zones, the -Raster object must contain a layer +Raster object must contain a layer for each zone. Note that for multi-band arguments, each pixel must only contain a non-zero value in a single band. Additionally, if the -cost data in x is a Raster object, we +cost data in x is a Raster object, we recommend standardizing NA values in this dataset with the cost data. In other words, the pixels in x that have NA values should also have NA values in the locked data.

    -
    -

    See also

    - -

    See constraints for an overview of all functions for adding constraints.

    +
    +
    +

    See also

    + - -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_polygons, sim_features, sim_locked_out_raster)
    -
    -# create minimal problem
    -p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with added locked out constraints using integers
    -p2 <- p1 %>% add_locked_out_constraints(which(sim_pu_polygons$locked_out))
    -
    -# create problem with added locked out constraints using a field name
    -p3 <- p1 %>% add_locked_out_constraints("locked_out")
    -
    -# create problem with added locked out constraints using raster data
    -p4 <- p1 %>% add_locked_out_constraints(sim_locked_out_raster)
    -
    -# create problem with added locked out constraints using spatial polygon data
    -locked_out <- sim_pu_polygons[sim_pu_polygons$locked_out == 1, ]
    -p5 <- p1 %>% add_locked_out_constraints(locked_out)
    -# \dontrun{
    -# solve problems
    -s1 <- solve(p1)
    -s2 <- solve(p2)
    -s3 <- solve(p3)
    -s4 <- solve(p4)
    -s5 <- solve(p5)
    -
    -# plot solutions
    -par(mfrow = c(3,2), mar = c(0, 0, 4.1, 0))
    -plot(s1, main = "none locked out")
    -plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s2, main = "locked out (integer input)")
    -plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s3, main = "locked out (character input)")
    -plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s4, main = "locked out (raster input)")
    -plot(s4[s4$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s5, main = "locked out (polygon input)")
    -plot(s5[s5$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -# reset plot
    -par(mfrow = c(1, 1))
    -
    -# }
    -
    -# create minimal multi-zone problem with spatial data
    -p6 <- problem(sim_pu_zones_polygons, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(matrix(rpois(15, 1), nrow = 5, ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create multi-zone problem with locked out constraints using matrix data
    -locked_matrix <- sim_pu_zones_polygons@data[, c("locked_1", "locked_2",
    -                                                "locked_3")]
    -locked_matrix <- as.matrix(locked_matrix)
    -
    -p7 <- p6 %>% add_locked_out_constraints(locked_matrix)
    -# \dontrun{
    -# solve problem
    -s6 <- solve(p6)
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s6$solution <- category_vector(s6@data[, c("solution_1_zone_1",
    -                                           "solution_1_zone_2",
    -                                           "solution_1_zone_3")])
    -s6$solution <- factor(s6$solution)
    -
    -# plot solution
    -spplot(s6, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create multi-zone problem with locked out constraints using field names
    -p8 <- p6 %>% add_locked_out_constraints(c("locked_1", "locked_2",
    -                                          "locked_3"))
    -# \dontrun{
    -# solve problem
    -s8 <- solve(p8)
    -
    -# create new column in s8 representing the zone id that each planning unit
    -# was allocated to in the solution
    -s8$solution <- category_vector(s8@data[, c("solution_1_zone_1",
    -                                           "solution_1_zone_2",
    -                                           "solution_1_zone_3")])
    -s8$solution[s8$solution == 1 & s8$solution_1_zone_1 == 0] <- 0
    -s8$solution <- factor(s8$solution)
    -
    -# plot solution
    -spplot(s8, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create multi-zone problem with raster planning units
    -p9 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(matrix(rpois(15, 1), nrow = 5, ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create raster stack with locked out units
    -locked_out_stack <- sim_pu_zones_stack[[1]]
    -locked_out_stack[!is.na(locked_out_stack)] <- 0
    -locked_out_stack <- locked_out_stack[[c(1, 1, 1)]]
    -locked_out_stack[[1]][1] <- 1
    -locked_out_stack[[2]][2] <- 1
    -locked_out_stack[[3]][3] <- 1
    -
    -# plot locked out stack
    -# \dontrun{
    -plot(locked_out_stack)
    -
    -# }
    -# add locked out raster units to problem
    -p9 <- p9 %>% add_locked_out_constraints(locked_out_stack)
    -
    -# \dontrun{
    -# solve problem
    -s9 <- solve(p9)
    -
    -# plot solution
    -plot(category_layer(s9), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    +add_contiguity_constraints(),
    +add_feature_contiguity_constraints(),
    +add_linear_constraints(),
    +add_locked_in_constraints(),
    +add_mandatory_allocation_constraints,ConservationProblem-method,
    +add_manual_bounded_constraints(),
    +add_manual_locked_constraints()

    +
    + +
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_polygons, sim_features, sim_locked_out_raster)
    +
    +# create minimal problem
    +p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with added locked out constraints using integers
    +p2 <- p1 %>% add_locked_out_constraints(which(sim_pu_polygons$locked_out))
    +
    +# create problem with added locked out constraints using a field name
    +p3 <- p1 %>% add_locked_out_constraints("locked_out")
    +
    +# create problem with added locked out constraints using raster data
    +p4 <- p1 %>% add_locked_out_constraints(sim_locked_out_raster)
    +
    +# create problem with added locked out constraints using spatial polygon data
    +locked_out <- sim_pu_polygons[sim_pu_polygons$locked_out == 1, ]
    +p5 <- p1 %>% add_locked_out_constraints(locked_out)
    +# \dontrun{
    +# solve problems
    +s1 <- solve(p1)
    +s2 <- solve(p2)
    +s3 <- solve(p3)
    +s4 <- solve(p4)
    +s5 <- solve(p5)
    +
    +# plot solutions
    +par(mfrow = c(3,2), mar = c(0, 0, 4.1, 0))
    +plot(s1, main = "none locked out")
    +plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s2, main = "locked out (integer input)")
    +plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s3, main = "locked out (character input)")
    +plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s4, main = "locked out (raster input)")
    +plot(s4[s4$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s5, main = "locked out (polygon input)")
    +plot(s5[s5$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +# reset plot
    +par(mfrow = c(1, 1))
    +
    +# }
    +
    +# create minimal multi-zone problem with spatial data
    +p6 <- problem(sim_pu_zones_polygons, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(matrix(rpois(15, 1), nrow = 5, ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create multi-zone problem with locked out constraints using matrix data
    +locked_matrix <- sim_pu_zones_polygons@data[, c("locked_1", "locked_2",
    +                                                "locked_3")]
    +locked_matrix <- as.matrix(locked_matrix)
    +
    +p7 <- p6 %>% add_locked_out_constraints(locked_matrix)
    +# \dontrun{
    +# solve problem
    +s6 <- solve(p6)
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s6$solution <- category_vector(s6@data[, c("solution_1_zone_1",
    +                                           "solution_1_zone_2",
    +                                           "solution_1_zone_3")])
    +s6$solution <- factor(s6$solution)
    +
    +# plot solution
    +spplot(s6, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create multi-zone problem with locked out constraints using field names
    +p8 <- p6 %>% add_locked_out_constraints(c("locked_1", "locked_2",
    +                                          "locked_3"))
    +# \dontrun{
    +# solve problem
    +s8 <- solve(p8)
    +
    +# create new column in s8 representing the zone id that each planning unit
    +# was allocated to in the solution
    +s8$solution <- category_vector(s8@data[, c("solution_1_zone_1",
    +                                           "solution_1_zone_2",
    +                                           "solution_1_zone_3")])
    +s8$solution[s8$solution == 1 & s8$solution_1_zone_1 == 0] <- 0
    +s8$solution <- factor(s8$solution)
    +
    +# plot solution
    +spplot(s8, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create multi-zone problem with raster planning units
    +p9 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(matrix(rpois(15, 1), nrow = 5, ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create raster stack with locked out units
    +locked_out_stack <- sim_pu_zones_stack[[1]]
    +locked_out_stack[!is.na(locked_out_stack)] <- 0
    +locked_out_stack <- locked_out_stack[[c(1, 1, 1)]]
    +locked_out_stack[[1]][1] <- 1
    +locked_out_stack[[2]][2] <- 1
    +locked_out_stack[[3]][3] <- 1
    +
    +# plot locked out stack
    +# \dontrun{
    +plot(locked_out_stack)
    +
    +# }
    +# add locked out raster units to problem
    +p9 <- p9 %>% add_locked_out_constraints(locked_out_stack)
    +
    +# \dontrun{
    +# solve problem
    +s9 <- solve(p9)
    +
    +# plot solution
    +plot(category_layer(s9), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +
    -
    -
    -

    Add constraints to a conservation planning problem() to ensure +

    Add constraints to a conservation planning problem() to ensure that specific planning units are selected (or allocated to a specific zone) in the solution. For example, it may be desirable to lock in planning units that are inside existing protected areas so that the solution fills in the gaps in the existing reserve network. If specific planning units should be locked out of a solution, use -add_locked_out_constraints(). For problems with non-binary -planning unit allocations (e.g. proportions), the -add_manual_locked_constraints() function can be used to lock +add_locked_out_constraints(). For problems with non-binary +planning unit allocations (e.g., proportions), the +add_manual_locked_constraints() function can be used to lock planning unit allocations to a specific value.

    -
    add_locked_in_constraints(x, locked_in)
    -
    -# S4 method for ConservationProblem,numeric
    -add_locked_in_constraints(x, locked_in)
    -
    -# S4 method for ConservationProblem,logical
    -add_locked_in_constraints(x, locked_in)
    +    
    Usage,
    add_locked_in_constraints(x, locked_in)
     
    -# S4 method for ConservationProblem,matrix
    -add_locked_in_constraints(x, locked_in)
    +# S4 method for ConservationProblem,numeric
    +add_locked_in_constraints(x, locked_in)
     
    -# S4 method for ConservationProblem,character
    -add_locked_in_constraints(x, locked_in)
    +# S4 method for ConservationProblem,logical
    +add_locked_in_constraints(x, locked_in)
     
    -# S4 method for ConservationProblem,Spatial
    -add_locked_in_constraints(x, locked_in)
    +# S4 method for ConservationProblem,matrix
    +add_locked_in_constraints(x, locked_in)
     
    -# S4 method for ConservationProblem,sf
    -add_locked_in_constraints(x, locked_in)
    +# S4 method for ConservationProblem,character
    +add_locked_in_constraints(x, locked_in)
     
    -# S4 method for ConservationProblem,Raster
    -add_locked_in_constraints(x, locked_in)
    +# S4 method for ConservationProblem,Spatial +add_locked_in_constraints(x, locked_in) -

    Arguments

    - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    locked_in

    Object that determines which planning units that should be -locked in. See the Data format section for more information.

    +# S4 method for ConservationProblem,sf +add_locked_in_constraints(x, locked_in) -

    Value

    +# S4 method for ConservationProblem,Raster +add_locked_in_constraints(x, locked_in)
    -

    Object (i.e. ConservationProblem) with the constraints +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    locked_in
    +

    Object that determines which planning units that should be +locked in. See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the constraints added to it.

    -

    Data format

    - +
    +
    +

    Data format

    The locked planning units can be specified using the following formats. Generally, the locked data should correspond to the planning units in the argument to x. To help make working with -Raster planning unit data easier, +Raster planning unit data easier, the locked data should correspond to cell indices in the -Raster data. For example, integer arguments +Raster data. For example, integer arguments should correspond to cell indices and logical arguments should have a value for each cell---regardless of which planning unit cells contain NA values.

    -
    - -
    data as an integer vector

    containing indices that indicate which +

    data as an integer vector
    +

    containing indices that indicate which planning units should be locked for the solution. This argument is only compatible with problems that contain a single zone.

    -
    data as a logical vector

    containing TRUE and/or + +

    data as a logical vector
    +

    containing TRUE and/or FALSE values that indicate which planning units should be locked in the solution. This argument is only compatible with problems that contain a single zone.

    -
    data as a matrix object

    containing logical TRUE and/or + +

    data as a matrix object
    +

    containing logical TRUE and/or FALSE values which indicate if certain planning units are should be locked to a specific zone in the solution. Each row corresponds to a planning unit, each column corresponds to a zone, and @@ -276,213 +191,217 @@

    TRUE value.

    -
    data as a character vector

    containing field (column) name(s) + +

    data as a character vector
    +

    containing field (column) name(s) that indicate if planning units should be locked for the solution. This format is only compatible if the planning units in the argument to x are a -Spatial, sf::sf(), or +Spatial, sf::sf(), or data.frame object. The fields -(columns) must have logical (i.e. TRUE or FALSE) +(columns) must have logical (i.e., TRUE or FALSE) values indicating if the planning unit is to be locked for the solution. For problems that contain a single zone, the argument to data must contain a single field name. Otherwise, for problems that contain multiple zones, the argument to data must contain a field name for each zone.

    -
    data as a Spatial or sf::sf() object

    containing geometries that will be used to lock planning units for + +

    data as a Spatial or sf::sf() object
    +

    containing geometries that will be used to lock planning units for the solution. Specifically, planning units in x that spatially -intersect with y will be locked (per intersecting_units()). +intersect with y will be locked (per intersecting_units()). Note that this option is only available for problems that contain a single management zone.

    -
    data as a Raster object

    containing cells used to lock + +

    data as a Raster object
    +

    containing cells used to lock planning units for the solution. Specifically, planning units in x that intersect with cells that have non-zero and non-NA values are locked. For problems that contain multiple zones, the -Raster object must contain a layer +Raster object must contain a layer for each zone. Note that for multi-band arguments, each pixel must only contain a non-zero value in a single band. Additionally, if the -cost data in x is a Raster object, we +cost data in x is a Raster object, we recommend standardizing NA values in this dataset with the cost data. In other words, the pixels in x that have NA values should also have NA values in the locked data.

    -
    -

    See also

    - -

    See constraints for an overview of all functions for adding constraints.

    +
    +
    +

    See also

    + - -

    Examples

    -
    # set seed for reproducibility
    -set.seed(500)
    -
    -# load data
    -data(sim_pu_polygons, sim_features, sim_locked_in_raster)
    -
    -# create minimal problem
    -p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.2) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create problem with added locked in constraints using integers
    -p2 <- p1 %>% add_locked_in_constraints(which(sim_pu_polygons$locked_in))
    -
    -# create problem with added locked in constraints using a field name
    -p3 <- p1 %>% add_locked_in_constraints("locked_in")
    -
    -# create problem with added locked in constraints using raster data
    -p4 <- p1 %>% add_locked_in_constraints(sim_locked_in_raster)
    -
    -# create problem with added locked in constraints using spatial polygon data
    -locked_in <- sim_pu_polygons[sim_pu_polygons$locked_in == 1, ]
    -p5 <- p1 %>% add_locked_in_constraints(locked_in)
    -# \dontrun{
    -# solve problems
    -s1 <- solve(p1)
    -s2 <- solve(p2)
    -s3 <- solve(p3)
    -s4 <- solve(p4)
    -s5 <- solve(p5)
    -
    -# plot solutions
    -par(mfrow = c(3, 2), mar = c(0, 0, 4.1, 0))
    -plot(s1, main = "none locked in")
    -plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s2, main = "locked in (integer input)")
    -plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s3, main = "locked in (character input)")
    -plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s4, main = "locked in (raster input)")
    -plot(s4[s4$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -plot(s5, main = "locked in (polygon input)")
    -plot(s5[s5$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    -
    -# reset plot
    -par(mfrow = c(1, 1))
    -
    -# }
    -
    -# create minimal multi-zone problem with spatial data
    -p6 <- problem(sim_pu_zones_polygons, sim_features_zones,
    -              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(matrix(rpois(15, 1), nrow = 5,
    -                                  ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create multi-zone problem with locked in constraints using matrix data
    -locked_matrix <- sim_pu_zones_polygons@data[, c("locked_1", "locked_2",
    -                                                "locked_3")]
    -locked_matrix <- as.matrix(locked_matrix)
    -
    -p7 <- p6 %>% add_locked_in_constraints(locked_matrix)
    -# \dontrun{
    -# solve problem
    -s6 <- solve(p6)
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s6$solution <- category_vector(s6@data[, c("solution_1_zone_1",
    -                                           "solution_1_zone_2",
    -                                           "solution_1_zone_3")])
    -s6$solution <- factor(s6$solution)
    -
    -# plot solution
    -spplot(s6, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create multi-zone problem with locked in constraints using field names
    -p8 <- p6 %>% add_locked_in_constraints(c("locked_1", "locked_2", "locked_3"))
    -# \dontrun{
    -# solve problem
    -s8 <- solve(p8)
    -
    -# create new column representing the zone id that each planning unit
    -# was allocated to in the solution
    -s8$solution <- category_vector(s8@data[, c("solution_1_zone_1",
    -                                           "solution_1_zone_2",
    -                                           "solution_1_zone_3")])
    -s8$solution[s8$solution == 1 & s8$solution_1_zone_1 == 0] <- 0
    -s8$solution <- factor(s8$solution)
    -
    -# plot solution
    -spplot(s8, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -# create multi-zone problem with raster planning units
    -p9 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_absolute_targets(matrix(rpois(15, 1), nrow = 5, ncol = 3)) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# create raster stack with locked in units
    -locked_in_stack <- sim_pu_zones_stack[[1]]
    -locked_in_stack[!is.na(locked_in_stack)] <- 0
    -locked_in_stack <- locked_in_stack[[c(1, 1, 1)]]
    -locked_in_stack[[1]][1] <- 1
    -locked_in_stack[[2]][2] <- 1
    -locked_in_stack[[3]][3] <- 1
    -
    -# plot locked in stack
    -# \dontrun{
    -plot(locked_in_stack)
    -
    -# }
    -# add locked in raster units to problem
    -p9 <- p9 %>% add_locked_in_constraints(locked_in_stack)
    -
    -# \dontrun{
    -# solve problem
    -s9 <- solve(p9)
    -
    -# plot solution
    -plot(category_layer(s9), main = "solution", axes = FALSE, box = FALSE)
    -
    -# }
    -
    +add_contiguity_constraints(),
    +add_feature_contiguity_constraints(),
    +add_linear_constraints(),
    +add_locked_out_constraints(),
    +add_mandatory_allocation_constraints,ConservationProblem-method,
    +add_manual_bounded_constraints(),
    +add_manual_locked_constraints()

    +
    + +
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(500)
    +
    +# load data
    +data(sim_pu_polygons, sim_features, sim_locked_in_raster)
    +
    +# create minimal problem
    +p1 <- problem(sim_pu_polygons, sim_features, "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.2) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create problem with added locked in constraints using integers
    +p2 <- p1 %>% add_locked_in_constraints(which(sim_pu_polygons$locked_in))
    +
    +# create problem with added locked in constraints using a field name
    +p3 <- p1 %>% add_locked_in_constraints("locked_in")
    +
    +# create problem with added locked in constraints using raster data
    +p4 <- p1 %>% add_locked_in_constraints(sim_locked_in_raster)
    +
    +# create problem with added locked in constraints using spatial polygon data
    +locked_in <- sim_pu_polygons[sim_pu_polygons$locked_in == 1, ]
    +p5 <- p1 %>% add_locked_in_constraints(locked_in)
    +# \dontrun{
    +# solve problems
    +s1 <- solve(p1)
    +s2 <- solve(p2)
    +s3 <- solve(p3)
    +s4 <- solve(p4)
    +s5 <- solve(p5)
    +
    +# plot solutions
    +par(mfrow = c(3, 2), mar = c(0, 0, 4.1, 0))
    +plot(s1, main = "none locked in")
    +plot(s1[s1$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s2, main = "locked in (integer input)")
    +plot(s2[s2$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s3, main = "locked in (character input)")
    +plot(s3[s3$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s4, main = "locked in (raster input)")
    +plot(s4[s4$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +plot(s5, main = "locked in (polygon input)")
    +plot(s5[s5$solution_1 == 1, ], col = "darkgreen", add = TRUE)
    +
    +# reset plot
    +par(mfrow = c(1, 1))
    +
    +# }
    +
    +# create minimal multi-zone problem with spatial data
    +p6 <- problem(sim_pu_zones_polygons, sim_features_zones,
    +              cost_column = c("cost_1", "cost_2", "cost_3")) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(matrix(rpois(15, 1), nrow = 5,
    +                                  ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create multi-zone problem with locked in constraints using matrix data
    +locked_matrix <- sim_pu_zones_polygons@data[, c("locked_1", "locked_2",
    +                                                "locked_3")]
    +locked_matrix <- as.matrix(locked_matrix)
    +
    +p7 <- p6 %>% add_locked_in_constraints(locked_matrix)
    +# \dontrun{
    +# solve problem
    +s6 <- solve(p6)
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s6$solution <- category_vector(s6@data[, c("solution_1_zone_1",
    +                                           "solution_1_zone_2",
    +                                           "solution_1_zone_3")])
    +s6$solution <- factor(s6$solution)
    +
    +# plot solution
    +spplot(s6, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create multi-zone problem with locked in constraints using field names
    +p8 <- p6 %>% add_locked_in_constraints(c("locked_1", "locked_2", "locked_3"))
    +# \dontrun{
    +# solve problem
    +s8 <- solve(p8)
    +
    +# create new column representing the zone id that each planning unit
    +# was allocated to in the solution
    +s8$solution <- category_vector(s8@data[, c("solution_1_zone_1",
    +                                           "solution_1_zone_2",
    +                                           "solution_1_zone_3")])
    +s8$solution[s8$solution == 1 & s8$solution_1_zone_1 == 0] <- 0
    +s8$solution <- factor(s8$solution)
    +
    +# plot solution
    +spplot(s8, zcol = "solution", main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +# create multi-zone problem with raster planning units
    +p9 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_absolute_targets(matrix(rpois(15, 1), nrow = 5, ncol = 3)) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# create raster stack with locked in units
    +locked_in_stack <- sim_pu_zones_stack[[1]]
    +locked_in_stack[!is.na(locked_in_stack)] <- 0
    +locked_in_stack <- locked_in_stack[[c(1, 1, 1)]]
    +locked_in_stack[[1]][1] <- 1
    +locked_in_stack[[2]][2] <- 1
    +locked_in_stack[[3]][3] <- 1
    +
    +# plot locked in stack
    +# \dontrun{
    +plot(locked_in_stack)
    +
    +# }
    +# add locked in raster units to problem
    +p9 <- p9 %>% add_locked_in_constraints(locked_in_stack)
    +
    +# \dontrun{
    +# solve problem
    +s9 <- solve(p9)
    +
    +# plot solution
    +plot(category_layer(s9), main = "solution", axes = FALSE, box = FALSE)
    +
    +# }
    +
     
    +
    +
    -
    -
    -

    Add penalties to a conservation planning problem() to penalize +

    Add penalties to a conservation planning problem() to penalize solutions that select planning units with higher values from a specific -data source (e.g. anthropogenic impact). These penalties assume +data source (e.g., anthropogenic impact). These penalties assume a linear trade-off between the penalty values and the primary -objective of the conservation planning problem() (e.g. -solution cost for minimum set problems; add_min_set_objective().

    +objective of the conservation planning problem() (e.g., +solution cost for minimum set problems; add_min_set_objective().

    -
    # S4 method for ConservationProblem,ANY,character
    -add_linear_penalties(x, penalty, data)
    +    
    Usage,
    # S4 method for ConservationProblem,ANY,character
    +add_linear_penalties(x, penalty, data)
     
    -# S4 method for ConservationProblem,ANY,numeric
    -add_linear_penalties(x, penalty, data)
    +# S4 method for ConservationProblem,ANY,numeric
    +add_linear_penalties(x, penalty, data)
     
    -# S4 method for ConservationProblem,ANY,matrix
    -add_linear_penalties(x, penalty, data)
    +# S4 method for ConservationProblem,ANY,matrix
    +add_linear_penalties(x, penalty, data)
     
    -# S4 method for ConservationProblem,ANY,Matrix
    -add_linear_penalties(x, penalty, data)
    +# S4 method for ConservationProblem,ANY,Matrix
    +add_linear_penalties(x, penalty, data)
     
    -# S4 method for ConservationProblem,ANY,Raster
    -add_linear_penalties(x, penalty, data)
    +# S4 method for ConservationProblem,ANY,Raster
    +add_linear_penalties(x, penalty, data)
     
    -# S4 method for ConservationProblem,ANY,dgCMatrix
    -add_linear_penalties(x, penalty, data)
    +# S4 method for ConservationProblem,ANY,dgCMatrix +add_linear_penalties(x, penalty, data)
    -

    Arguments

    - - - - - - - - - - - - - - -
    x

    problem() (i.e. ConservationProblem) object.

    penalty

    numeric penalty value that is used to scale the +

    +

    Arguments

    +
    x
    +

    problem() (i.e., ConservationProblem) object.

    +
    penalty
    +

    numeric penalty value that is used to scale the importance not selecting planning units with high data values. Higher penalty values can be used to obtain solutions that are strongly averse to selecting places with high data @@ -231,39 +144,36 @@

    Arg penalty values can be used to obtain solutions that prefer places with high data values. Additionally, when adding these penalties to problems with multiple zones, the argument to penalty -must have a value for each zone.

    data

    character, numeric, -Raster, matrix, or Matrix object +must have a value for each zone.

    +
    data
    +

    character, numeric, +Raster, matrix, or Matrix object containing the values used to penalize solutions. Planning units that are associated with higher data values are penalized more strongly -in the solution. See the Data format section for more information.

    - -

    Value

    - -

    Object (i.e. ConservationProblem) with the penalties +in the solution. See the Data format section for more information.

    +
    +
    +

    Value

    +

    Object (i.e., ConservationProblem) with the penalties added to it.

    -

    Details

    - +
    +
    +

    Details

    This function penalizes solutions that have higher values according to the sum of the penalty values associated with each planning unit, weighted by status of each planning unit in the solution.

    -

    Data format

    - +
    +
    +

    Data format

    The argument to data can be specified using the following formats.

    -
    - -
    data as character vector

    containing field (column) name(s) that +

    data as character vector
    +

    containing field (column) name(s) that contain penalty values for planning units. This format is only compatible if the planning units in the argument to x are a -Spatial, sf::sf(), or +Spatial, sf::sf(), or data.frame object. The fields (columns) must have numeric values, and must not contain any missing (NA) values. For problems that contain a single zone, the argument to data must @@ -271,28 +181,34 @@

    data must contain a field name for each zone.

    -
    data as a numeric vector

    containing values for + +

    data as a numeric vector
    +

    containing values for planning units. These values must not contain any missing (NA) values. Note that this format is only available for planning units that contain a single zone.

    -
    data as a matrix/Matrix object

    containing numeric values + +

    data as a matrix/Matrix object
    +

    containing numeric values that specify data for each planning unit. Each row corresponds to a planning unit, each column corresponds to a zone, and each cell indicates the data for penalizing a planning unit when it is allocated to a given zone.

    -
    data as a Raster object

    containing values for planning + +

    data as a Raster object
    +

    containing values for planning units. This format is only compatible if the planning units in the argument to x are -Spatial, sf::sf(), or -Raster objects. -If the planning unit data are a Spatial or -sf::sf() object, then the values are calculated by overlaying the +Spatial, sf::sf(), or +Raster objects. +If the planning unit data are a Spatial or +sf::sf() object, then the values are calculated by overlaying the planning units with the argument to data and calculating the sum of the values associated with each planning unit. If the planning unit data are a -Raster object then the values are calculated by extracting the +Raster object then the values are calculated by extracting the cell values (note that the planning unit data and the argument to data must have exactly the same dimensionality, extent, and missingness). @@ -300,10 +216,10 @@

    -
    - -

    Mathematical formulation

    +
    +
    +

    Mathematical formulation

    The linear penalties are implemented using the following @@ -311,7 +227,7 @@

    See also

    - -

    See penalties for an overview of all functions for adding penalties.

    +
    +
    +

    See also

    +

    See penalties for an overview of all functions for adding penalties.

    Other penalties: -add_boundary_penalties(), -add_connectivity_penalties(), -add_feature_weights()

    - -

    Examples

    -
    # set seed for reproducibility
    -set.seed(600)
    -
    -# load data
    -data(sim_pu_polygons, sim_pu_zones_stack, sim_features, sim_features_zones)
    -
    -# add a column to contain the penalty data for each planning unit
    -# e.g. these values could indicate the level of habitat
    -sim_pu_polygons$penalty_data <- runif(nrow(sim_pu_polygons))
    -
    -# plot the penalty data to visualise its spatial distribution
    -spplot(sim_pu_polygons, zcol = "penalty_data", main = "penalty data",
    -       axes = FALSE, box = FALSE)
    -
    -
    -# create minimal problem with minimum set objective,
    -# this does not use the penalty data
    -p1 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(0.1) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# print problem
    -print(p1)
    -#> Conservation Problem
    -#>   planning units: SpatialPolygonsDataFrame (90 units)
    -#>   cost:           min: 190.13276, max: 215.86384
    -#>   features:       layer.1, layer.2, layer.3, ... (5 features)
    -#>   objective:      Minimum set objective 
    -#>   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -#>   decisions:      Binary decision 
    -#>   constraints:    <none>
    -#>   penalties:      <none>
    -#>   portfolio:      default
    -#>   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (0)]
    -
    -# create an updated version of the previous problem,
    -# with the penalties added to it
    -p2 <- p1 %>% add_linear_penalties(100, data = "penalty_data")
    -
    -# print problem
    -print(p2)
    -#> Conservation Problem
    -#>   planning units: SpatialPolygonsDataFrame (90 units)
    -#>   cost:           min: 190.13276, max: 215.86384
    -#>   features:       layer.1, layer.2, layer.3, ... (5 features)
    -#>   objective:      Minimum set objective 
    -#>   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -#>   decisions:      Binary decision 
    -#>   constraints:    <none>
    -#>   penalties:      <Linear penalties [penalty (100)]>
    -#>   portfolio:      default
    -#>   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (0)]
    -
    -# \dontrun{
    -# solve the two problems
    -s1 <- solve(p1)
    -s2 <- solve(p2)
    -
    -# plot the solutions and compare them,
    -# since we supplied a very high penalty value (i.e. 100), relative
    -# to the range of values in the penalty data and the objective function,
    -# the solution in s2 is very sensitive to values in the penalty data
    -spplot(s1, zcol = "solution_1", main = "solution without penalties",
    -       axes = FALSE, box = FALSE)
    -
    -spplot(s2, zcol = "solution_1", main = "solution with penalties",
    -       axes = FALSE, box = FALSE)
    -
    -
    -# for real conservation planning exercises,
    -# it would be worth exploring a range of penalty values (e.g. ranging
    -# from 1 to 100 increments of 5) to explore the trade-offs
    -# }
    -
    -# now, let's examine a conservation planning exercise involving multiple
    -# management zones
    -
    -# \dontrun{
    -# create targets for each feature within each zone,
    -# these targets indicate that each zone needs to represent 10% of the
    -# spatial distribution of each feature
    -targ <- matrix(0.1, ncol = number_of_zones(sim_features_zones),
    -               nrow = number_of_features(sim_features_zones))
    -
    -# create penalty data for allocating each planning unit to each zone,
    -# these data will be generated by simulating values
    -# (note this requires the RandomFields package to be installed)
    -penalty_stack <- simulate_cost(sim_pu_zones_stack[[1]],
    -                               n = number_of_zones(sim_features_zones))
    -#> ...
    -
    -# plot the penalty data, each layer corresponds to a different zone
    -plot(penalty_stack, main = "penalty data", axes = FALSE, box = FALSE)
    -
    -
    -# create a multi-zone problem with the minimum set objective
    -# and penalties for allocating planning units to each zone,
    -# with a penalty scaling factor of 1 for each zone
    -p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    -      add_min_set_objective() %>%
    -      add_relative_targets(targ) %>%
    -      add_linear_penalties(c(1, 1, 1), penalty_stack) %>%
    -      add_binary_decisions() %>%
    -      add_default_solver(verbose = FALSE)
    -
    -# print problem
    -print(p3)
    -#> Conservation Problem
    -#>   zones:          zone_1, zone_2, zone_3 (3 zones)
    -#>   planning units: RasterStack (90 units)
    -#>   cost:           min: 182.60173, max: 224.84924
    -#>   features:       feature_1, feature_2, feature_3, ... (5 features)
    -#>   objective:      Minimum set objective 
    -#>   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    -#>   decisions:      Binary decision 
    -#>   constraints:    <none>
    -#>   penalties:      <Linear penalties [penalty (min: 1, max: 1)]>
    -#>   portfolio:      default
    -#>   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (0)]
    -
    -# solve problem
    -s3 <- solve(p3)
    -
    -# plot solution
    -plot(category_layer(s3), main = "multi-zone solution",
    -     axes = FALSE, box = FALSE)
    -
    -# }
    +add_boundary_penalties(),
    +add_connectivity_penalties(),
    +add_feature_weights()

    +
    + +
    +

    Examples

    +
    # set seed for reproducibility
    +set.seed(600)
    +
    +# load data
    +data(sim_pu_polygons, sim_pu_zones_stack, sim_features, sim_features_zones)
    +
    +# add a column to contain the penalty data for each planning unit
    +# e.g., these values could indicate the level of habitat
    +sim_pu_polygons$penalty_data <- runif(nrow(sim_pu_polygons))
    +
    +# plot the penalty data to visualise its spatial distribution
    +spplot(sim_pu_polygons, zcol = "penalty_data", main = "penalty data",
    +       axes = FALSE, box = FALSE)
    +
    +
    +# create minimal problem with minimum set objective,
    +# this does not use the penalty data
    +p1 <- problem(sim_pu_polygons, sim_features, cost_column = "cost") %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(0.1) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# print problem
    +print(p1)
    +#> Conservation Problem
    +#>   planning units: SpatialPolygonsDataFrame (90 units)
    +#>   cost:           min: 190.13276, max: 215.86384
    +#>   features:       layer.1, layer.2, layer.3, ... (5 features)
    +#>   objective:      Minimum set objective 
    +#>   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +#>   decisions:      Binary decision 
    +#>   constraints:    <none>
    +#>   penalties:      <none>
    +#>   portfolio:      default
    +#>   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (0)]
    +
    +# create an updated version of the previous problem,
    +# with the penalties added to it
    +p2 <- p1 %>% add_linear_penalties(100, data = "penalty_data")
    +
    +# print problem
    +print(p2)
    +#> Conservation Problem
    +#>   planning units: SpatialPolygonsDataFrame (90 units)
    +#>   cost:           min: 190.13276, max: 215.86384
    +#>   features:       layer.1, layer.2, layer.3, ... (5 features)
    +#>   objective:      Minimum set objective 
    +#>   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +#>   decisions:      Binary decision 
    +#>   constraints:    <none>
    +#>   penalties:      <Linear penalties [penalty (100)]>
    +#>   portfolio:      default
    +#>   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (0)]
    +
    +# \dontrun{
    +# solve the two problems
    +s1 <- solve(p1)
    +s2 <- solve(p2)
    +
    +# plot the solutions and compare them,
    +# since we supplied a very high penalty value (i.e., 100), relative
    +# to the range of values in the penalty data and the objective function,
    +# the solution in s2 is very sensitive to values in the penalty data
    +spplot(s1, zcol = "solution_1", main = "solution without penalties",
    +       axes = FALSE, box = FALSE)
    +
    +spplot(s2, zcol = "solution_1", main = "solution with penalties",
    +       axes = FALSE, box = FALSE)
    +
    +
    +# for real conservation planning exercises,
    +# it would be worth exploring a range of penalty values (e.g., ranging
    +# from 1 to 100 increments of 5) to explore the trade-offs
    +# }
    +
    +# now, let's examine a conservation planning exercise involving multiple
    +# management zones
    +
    +# \dontrun{
    +# create targets for each feature within each zone,
    +# these targets indicate that each zone needs to represent 10% of the
    +# spatial distribution of each feature
    +targ <- matrix(0.1, ncol = number_of_zones(sim_features_zones),
    +               nrow = number_of_features(sim_features_zones))
    +
    +# create penalty data for allocating each planning unit to each zone,
    +# these data will be generated by simulating values
    +# (note this requires the RandomFields package to be installed)
    +penalty_stack <- simulate_cost(sim_pu_zones_stack[[1]],
    +                               n = number_of_zones(sim_features_zones))
    +#> ...
    +
    +# plot the penalty data, each layer corresponds to a different zone
    +plot(penalty_stack, main = "penalty data", axes = FALSE, box = FALSE)
    +
    +
    +# create a multi-zone problem with the minimum set objective
    +# and penalties for allocating planning units to each zone,
    +# with a penalty scaling factor of 1 for each zone
    +p3 <- problem(sim_pu_zones_stack, sim_features_zones) %>%
    +      add_min_set_objective() %>%
    +      add_relative_targets(targ) %>%
    +      add_linear_penalties(c(1, 1, 1), penalty_stack) %>%
    +      add_binary_decisions() %>%
    +      add_default_solver(verbose = FALSE)
    +
    +# print problem
    +print(p3)
    +#> Conservation Problem
    +#>   zones:          zone_1, zone_2, zone_3 (3 zones)
    +#>   planning units: RasterStack (90 units)
    +#>   cost:           min: 182.60173, max: 224.84924
    +#>   features:       feature_1, feature_2, feature_3, ... (5 features)
    +#>   objective:      Minimum set objective 
    +#>   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
    +#>   decisions:      Binary decision 
    +#>   constraints:    <none>
    +#>   penalties:      <Linear penalties [penalty (min: 1, max: 1)]>
    +#>   portfolio:      default
    +#>   solver:         Gurobi [first_feasible (0), gap (0.1), node_file_start (-1), numeric_focus (0), presolve (2), threads (1), time_limit (2147483647), verbose (0)]
    +
    +# solve problem
    +s3 <- solve(p3)
    +
    +# plot solution
    +plot(category_layer(s3), main = "multi-zone solution",
    +     axes = FALSE, box = FALSE)
    +
    +# }
     
    +
    +