% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/add_actions.R
\name{add_actions}
\alias{add_actions}
\title{Add management actions to a planning problem}
\usage{
add_actions(
  x,
  actions,
  include_pairs = NULL,
  exclude_pairs = NULL,
  cost = NULL
)
}
\arguments{
\item{x}{A \code{Problem} object created with \code{\link{create_problem}}.}

\item{actions}{A \code{data.frame} defining the action catalogue. It must
contain a unique \code{id} column. A column named \code{action} is also
accepted and automatically renamed to \code{id}.}

\item{include_pairs}{Optional specification of feasible \code{(pu, action)}
pairs. It can be \code{NULL}, a \code{data.frame} with columns
\code{pu} and \code{action} (optionally also \code{feasible}), or a named
list whose names are action ids and whose elements are vectors of planning
unit ids or \code{sf} objects.}

\item{exclude_pairs}{Optional specification of infeasible \code{(pu, action)}
pairs. It uses the same formats as \code{include_pairs} and removes
matching pairs from the feasible set.}

\item{cost}{Optional cost specification for feasible pairs. It may be
\code{NULL}, a scalar numeric value, a named numeric vector indexed by
action id, or a \code{data.frame} with columns \code{action, cost} or
\code{pu, action, cost}.}
}
\value{
An updated \code{Problem} object with:
\describe{
\item{\code{actions}}{The action catalogue, including a unique integer
\code{internal_id} for each action.}
\item{\code{dist_actions}}{The feasible planning unit--action table with
columns \code{pu}, \code{action}, \code{cost}, \code{status},
\code{internal_pu}, and \code{internal_action}.}
\item{\code{pu index}}{A mapping from user-supplied planning-unit ids to
internal integer ids.}
\item{\code{action index}}{A mapping from action ids to internal integer
ids.}
}
}
\description{
Define the action catalogue, the set of feasible planning unit--action pairs,
and their implementation costs.

This function adds two core components to a \code{Problem} object. First, it
stores the action catalogue. Second, it creates the feasible planning
unit--action table, including implementation costs, status codes, and
internal indices used by the optimization backend.

Conceptually, if \eqn{\mathcal{I}} is the set of planning units and
\eqn{\mathcal{A}} is the set of actions, this function determines which
pairs \eqn{(i,a) \in \mathcal{I} \times \mathcal{A}} are feasible decisions
and assigns a non-negative implementation cost to each feasible pair.
}
\details{
\strong{When to use \code{add_actions()}.}

Use this function when you want to move from a planning problem defined only
by planning units and features to a problem in which decisions are explicitly
represented as actions applied in planning units.

\strong{Action catalogue.}

The \code{actions} argument must be a \code{data.frame} with a unique
\code{id} column identifying each action. If a column named \code{action} is
supplied instead, it is renamed internally to \code{id}. Additional columns
are preserved. If no \code{name} column is provided, action labels are taken
from \code{id}. If an \code{action_set} column is present, it is also
preserved and can later be used to refer to groups of actions.

Actions are stored sorted by \code{id} to ensure reproducible internal
indexing.

\strong{Feasible planning unit--action pairs.}

Feasibility is controlled through \code{include_pairs} and
\code{exclude_pairs}.

If \code{include_pairs = NULL}, all possible \code{(pu, action)} pairs are
initially considered feasible, that is, all pairs
\eqn{(i,a) \in \mathcal{I} \times \mathcal{A}}.

If \code{include_pairs} is supplied, only those pairs are retained. If
\code{exclude_pairs} is also supplied, matching pairs are removed
afterwards.

More precisely, let \eqn{\mathcal{D}^{\mathrm{inc}}} denote the set of
included planning unit--action pairs and let
\eqn{\mathcal{D}^{\mathrm{exc}}} denote the set of excluded pairs.

If \code{include_pairs = NULL}, the feasible decision set is:
\deqn{
\{(i,a) : i \in \mathcal{I},\ a \in \mathcal{A}\} \setminus \mathcal{D}^{\mathrm{exc}}.
}

If \code{include_pairs} is supplied, the feasible decision set is:
\deqn{
\mathcal{D}^{\mathrm{inc}} \setminus \mathcal{D}^{\mathrm{exc}}.
}

Both \code{include_pairs} and \code{exclude_pairs} can be specified as:
\itemize{
\item \code{NULL},
\item a \code{data.frame} with columns \code{pu} and \code{action},
\item or a named list whose names are action ids.
}

When supplied as a \code{data.frame}, the object must contain columns
\code{pu} and \code{action}. An optional logical-like column
\code{feasible} may also be provided; only rows with \code{feasible = TRUE}
are retained. Missing values in \code{feasible} are treated as
\code{FALSE}.

When supplied as a named list, names must match action ids. Each element may
contain either:
\itemize{
\item a vector of planning-unit ids, or
\item an \code{sf} object defining the spatial zone where the action is
feasible.
}

In the spatial case, feasible planning units are identified using
\code{sf::st_intersects()} against the stored planning-unit geometry.

\strong{Feasibility versus decision fixing.}

This function only determines whether a pair \eqn{(i,a)} exists in the model.
It does not force a feasible action to be selected or forbidden beyond
structural infeasibility. Fixed decisions should instead be imposed later
with \code{\link{add_constraint_locked_actions}}.

\strong{Costs.}

Costs can be supplied in several ways:
\itemize{
\item If \code{cost = NULL}, all feasible pairs receive a default cost of
\code{1}.
\item If \code{cost} is a scalar, that value is assigned to all feasible
pairs.
\item If \code{cost} is a named numeric vector, names must match action ids
and costs are assigned by action.
\item If \code{cost} is a \code{data.frame}, it must define either:
\itemize{
\item action-level costs through columns \code{action} and \code{cost}, or
\item pair-specific costs through columns \code{pu}, \code{action}, and
\code{cost}.
}
}

In all cases, costs must be finite and non-negative.

In practice, a scalar cost is useful when all actions cost the same
everywhere, a named vector is useful when cost depends only on action type,
and a \code{(pu, action, cost)} table is useful when cost varies by both
planning unit and action.

\strong{Status values.}

Internally, all feasible pairs are initialized with \code{status = 0},
meaning that the decision is free. If planning units have already been marked
as locked out, then all feasible actions in those planning units are assigned
\code{status = 3}. This preserves consistency with planning-unit exclusions
already stored in the problem.

\strong{Replacement behaviour.}

Calling \code{add_actions()} replaces any previous action catalogue and
feasible action table stored in the problem object.

After defining actions, typical next steps include adding effects, optional
decision-fixing constraints, objectives, and solver settings before calling
\code{solve()}.
}
\examples{
# ------------------------------------------------------
# Minimal planning problem
# ------------------------------------------------------
pu <- data.frame(
  id = 1:4,
  cost = c(2, 3, 1, 4)
)

features <- data.frame(
  id = 1:2,
  name = c("sp1", "sp2")
)

dist_features <- data.frame(
  pu = c(1, 1, 2, 3, 4, 4),
  feature = c(1, 2, 1, 2, 1, 2),
  amount = c(1, 2, 1, 3, 2, 1)
)

p <- create_problem(
  pu = pu,
  features = features,
  dist_features = dist_features
)

actions <- data.frame(
  id = c("conservation", "restoration"),
  name = c("Conservation", "Restoration")
)

# Example 1: all actions feasible in all planning units
p1 <- add_actions(
  x = p,
  actions = actions,
  cost = c(conservation = 5, restoration = 12)
)

print(p1)
utils::head(p1$data$dist_actions)

# Example 2: specify feasible pairs explicitly
include_df <- data.frame(
  pu = c(1, 2, 3, 4),
  action = c("conservation", "conservation", "restoration", "restoration")
)

p2 <- add_actions(
  x = p,
  actions = actions,
  include_pairs = include_df,
  cost = 10
)

p2$data$dist_actions

# Example 3: remove selected pairs after full expansion
exclude_df <- data.frame(
  pu = c(2, 4),
  action = c("restoration", "conservation")
)

p3 <- add_actions(
  x = p,
  actions = actions,
  exclude_pairs = exclude_df,
  cost = c(conservation = 3, restoration = 8)
)

p3$data$dist_actions

}
\seealso{
\code{\link{create_problem}},
\code{\link{add_constraint_locked_actions}}
}
