% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/add_constraint_locked_pu.R
\name{add_constraint_locked_pu}
\alias{add_constraint_locked_pu}
\title{Add locked planning units to a problem}
\usage{
add_constraint_locked_pu(x, locked_in = NULL, locked_out = NULL)
}
\arguments{
\item{x}{A \code{Problem} object created with \code{\link{create_problem}}.}

\item{locked_in}{Optional locked-in specification. It may be \code{NULL}, a
column name in the raw planning-unit data, a logical vector, or a vector of
planning-unit ids.}

\item{locked_out}{Optional locked-out specification. It may be \code{NULL}, a
column name in the raw planning-unit data, a logical vector, or a vector of
planning-unit ids.}
}
\value{
An updated \code{Problem} object in which the planning-unit table
contains logical columns \code{locked_in} and \code{locked_out}.
}
\description{
Define planning units that must be included in, or excluded from, the
optimization problem.

This function updates the planning-unit table stored in the \code{Problem}
object by creating or replacing the logical columns \code{locked_in} and
\code{locked_out}. These columns are later used by the model builder when
translating the problem into optimization constraints.

Lock information may be supplied either directly as logical vectors, as
vectors of planning-unit ids, or by referencing columns in the raw
planning-unit data originally passed to \code{\link{create_problem}}.
}
\details{
Use this function when whole planning units must be forced into or out of
the solution, regardless of which action may later be selected in them.

Let \eqn{\mathcal{I}} denote the set of planning units and let
\eqn{w_i \in \{0,1\}} denote the binary variable indicating whether planning
unit \eqn{i \in \mathcal{I}} is selected by the model.

This function defines two subsets:
\itemize{
\item \eqn{\mathcal{I}^{in} \subseteq \mathcal{I}}, the planning units that
must be included,
\item \eqn{\mathcal{I}^{out} \subseteq \mathcal{I}}, the planning units
that must be excluded.
}

Conceptually, these sets correspond to the following conditions:
\itemize{
\item if \eqn{i \in \mathcal{I}^{in}}, then \eqn{w_i = 1},
\item if \eqn{i \in \mathcal{I}^{out}}, then \eqn{w_i = 0}.
}

These constraints are not imposed immediately by this function; instead, they
are stored in the planning-unit table and enforced later when building the
optimization model.

\strong{Philosophy}

The role of \code{\link{create_problem}} is to construct and normalize the basic
inputs of the planning problem. Locking planning units is treated as a
separate modelling step so that users can define or revise selection
restrictions after the \code{Problem} object has already been created.

In contrast, \code{\link{add_constraint_locked_actions}} is used to fix
specific feasible \code{(pu, action)} decisions rather than whole planning
units.

\strong{Supported input formats}

For both \code{locked_in} and \code{locked_out}, the function accepts:
\itemize{
\item \code{NULL}, meaning that no planning units are locked on that side,
\item a single character string, interpreted as a column name in the raw
planning-unit data,
\item a logical vector of length \code{nrow(pu)},
\item a vector of planning-unit ids.
}

When a column name is supplied, the referenced column is coerced to logical.
Numeric values are interpreted as non-zero = \code{TRUE}; character and
factor values are interpreted using common logical strings such as
\code{"true"}, \code{"t"}, \code{"1"}, \code{"yes"}, and \code{"y"}.
Missing values are treated as \code{FALSE}.

\strong{Replacement behaviour}

Each call to \code{add_constraint_locked_pu()} replaces any existing
\code{locked_in} and \code{locked_out} columns in the planning-unit table. In
other words, the function defines the complete current set of locked planning
units; it does not merge new values with previous ones.

\strong{Consistency checks}

The function checks that no planning unit is simultaneously assigned to both
\code{locked_in} and \code{locked_out}. If such conflicts are found, an error
is raised.
}
\examples{
pu <- data.frame(
  id = 1:5,
  cost = c(2, 3, 1, 4, 2),
  lock_col = c(TRUE, FALSE, FALSE, TRUE, FALSE),
  out_col = c(FALSE, FALSE, FALSE, FALSE, TRUE)
)

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
)

# 1) Lock by planning-unit ids
p1 <- add_constraint_locked_pu(
  x = p,
  locked_in = c(1, 3),
  locked_out = c(5)
)

p1$data$pu[, c("id", "locked_in", "locked_out")]

# 2) Read lock information from raw PU data columns
p2 <- add_constraint_locked_pu(
  x = p,
  locked_in = "lock_col",
  locked_out = "out_col"
)

p2$data$pu[, c("id", "locked_in", "locked_out")]

# 3) Use logical vectors
p3 <- add_constraint_locked_pu(
  x = p,
  locked_in = c(TRUE, FALSE, TRUE, FALSE, FALSE),
  locked_out = c(FALSE, FALSE, FALSE, TRUE, FALSE)
)

p3$data$pu[, c("id", "locked_in", "locked_out")]

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