% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/svem_score_random.R
\name{svem_score_random}
\alias{svem_score_random}
\title{Random-search scoring for SVEM models}
\usage{
svem_score_random(
  objects,
  goals,
  data = NULL,
  n = 50000,
  mixture_groups = NULL,
  level = 0.95,
  combine = c("geom", "mean"),
  numeric_sampler = c("random", "uniform"),
  wmt = NULL,
  verbose = TRUE,
  specs = NULL
)
}
\arguments{
\item{objects}{List of \code{svem_model} objects (from
\code{\link{SVEMnet}}). When unnamed, \code{svem_score_random()} attempts
to infer response names from the left-hand sides of the model formulas.
Names (when present) are treated as response identifiers and should
typically match the model response names. All models must share a common
sampling schema (predictor set, factor levels, numeric ranges)
compatible with \code{\link{svem_random_table_multi}}.}

\item{goals}{List of per-response goal specifications. Either:
\itemize{
\item a named list, where names may be either \code{names(objects)} or
the left-hand-side response names from the fitted models; or
\item an unnamed list with the same length as \code{objects}, in which
case entries are matched to models by position.
}
Each \code{goals[[response]]} must be a list with at least:
\itemize{
\item \code{goal}: one of \code{"max"}, \code{"min"}, \code{"target"};
\item \code{weight}: nonnegative numeric weight.
}
For \code{goal = "target"}, also provide \code{target}. Optional
Derringer–Suich controls:
\itemize{
\item For \code{"max"} or \code{"min"}: \code{lower_acceptable},
\code{upper_acceptable}, \code{shape}.
\item For \code{"target"}: \code{tol} (symmetric), or
\code{tol_left} / \code{tol_right}, and
\code{shape_left} / \code{shape_right}.
}
When anchors/tolerances are not supplied, robust defaults are inferred
from the sampled table using the q0.02–q0.98 span.}

\item{data}{Optional data frame. When supplied (regardless of whether
\code{wmt} is used), it is scored and returned as
\code{original_data_scored}, with predictions (in \code{<resp>_pred}
columns), per-response desirabilities, \code{score} (and
\code{wmt_score} if applicable), and \code{uncertainty_measure}
appended. When \code{specs} is supplied and the spec-limit augmentation
succeeds, the same mean-level spec columns as in \code{score_table}
(per-response \code{<resp>_p_in_spec_mean}, \code{<resp>_in_spec_point},
and joint \code{p_joint_mean}, \code{joint_in_spec_point}) are appended
as well.}

\item{n}{Number of random samples to draw in the predictor space. This is
the number of rows in the sampled table used for scoring.}

\item{mixture_groups}{Optional mixture and simplex constraints passed to
\code{\link{svem_random_table_multi}}. Each group typically specifies
mixture variable names, bounds, and a total.}

\item{level}{Confidence level for percentile intervals used in the CI width
and uncertainty calculations. Default \code{0.95}.}

\item{combine}{How to combine per-response desirabilities into a scalar
score. One of:
\itemize{
\item \code{"geom"}: weighted geometric mean (default);
\item \code{"mean"}: weighted arithmetic mean.
}}

\item{numeric_sampler}{Character string controlling how numeric predictors
are sampled inside \code{\link{svem_random_table_multi}}. One of:
\itemize{
\item \code{"random"}: Latin hypercube sampling when
\pkg{lhs} is available, otherwise independent uniforms;
\item \code{"uniform"}: independent uniforms over stored numeric ranges.
}}

\item{wmt}{Optional object returned by \code{\link{svem_wmt_multi}}.
When non-\code{NULL}, its \code{multipliers} (and \code{p_values}, if
present) are aligned to \code{names(objects)} and used to define WMT
weights, \code{wmt_score}. When \code{NULL}, only
user weights are used and no WMT reweighting is applied.}

\item{verbose}{Logical; if \code{TRUE}, print a compact summary of the run
(and any WMT diagnostics from upstream) to the console.}

\item{specs}{Optional named list of specification objects, one per response
in \code{objects} for which you want to define a mean-level spec
constraint. Each entry should be either \code{NULL} (no specs for that
response) or a list with components:
\itemize{
\item \code{lower}: numeric lower limit (may be \code{-Inf}, \code{NA},
or \code{NULL} for a one-sided upper spec);
\item \code{upper}: numeric upper limit (may be \code{Inf}, \code{NA},
or \code{NULL} for a one-sided lower spec).
}
Names of \code{specs}, when provided, should be a subset of
\code{names(objects)} or of the model response names (left-hand sides).
The specification structure matches that used by
\code{svem_append_design_space_cols}.}
}
\value{
A list with components:
\describe{
\item{\code{score_table}}{Data frame with predictors, predicted responses
(columns \code{<resp>_pred} for each \code{resp} in \code{names(objects)}),
per-response desirabilities, \code{score}, optional \code{wmt_score},
and \code{uncertainty_measure}. For each response \code{r} in
\code{names(objects)}, additional columns \code{r_lwr}, \code{r_upr}
(percentile CI bounds at level \code{level}) and \code{r_ciw_w}
(weighted, normalized CI width contribution to
\code{uncertainty_measure}) are appended. When \code{specs} is supplied
and the spec-limit augmentation succeeds, additional columns
\code{<resp>_p_in_spec_mean}, \code{<resp>_in_spec_point},
\code{p_joint_mean}, and \code{joint_in_spec_point} are appended.}
\item{\code{original_data_scored}}{If \code{data} is supplied, that data
augmented with prediction columns \code{<resp>_pred}, per-response
desirabilities, \code{score}, optional \code{wmt_score}, and
\code{uncertainty_measure}; otherwise \code{NULL}. When \code{specs} is
supplied and the spec-limit augmentation succeeds, the same mean-level
spec columns as in \code{score_table} are appended to
\code{original_data_scored} as well.}
\item{\code{weights_original}}{User-normalized response weights.}
\item{\code{weights_final}}{Final weights after WMT, if \code{wmt} is
supplied; otherwise equal to \code{weights_original}.}
\item{\code{wmt_p_values}}{Named vector of per-response whole-model
p-values when \code{wmt} is supplied and contains \code{p_values};
otherwise \code{NULL}.}
\item{\code{wmt_multipliers}}{Named vector of per-response WMT multipliers
when \code{wmt} is supplied; otherwise \code{NULL}.}
}
}
\description{
Draw random points from the SVEM sampling schema, compute multi-response
desirability scores and (optionally) whole-model-test (WMT) reweighted
scores, and attach a scalar uncertainty measure based on percentile CI
widths. This function does \emph{not} choose candidates; see
\code{\link{svem_select_from_score_table}} for selection and clustering.

Predictions used inside this scorer are always generated with debiasing
disabled (i.e., \code{debias = FALSE}) regardless of whether the underlying
SVEM fits support calibration.

When \code{specs} is supplied, the function also attempts to append
mean-level "in spec" probabilities and related joint indicators using the
SVEM bootstrap ensemble via \code{svem_append_design_space_cols}.
These quantities reflect uncertainty on the \emph{process mean} at each sampled
setting under the fitted SVEM models, not unit-level predictive
probabilities. If any error occurs in this spec-limit augmentation, it is
caught; a message may be issued when \code{verbose = TRUE}, and the
affected table(s) are returned without the spec-related columns.
}
\details{
\subsection{Typical workflow}{
A common pattern is:
\enumerate{
\item Fit one or more \code{SVEMnet()} models for the responses of interest.
\item Call \code{svem_score_random()} to:
\itemize{
\item draw candidate settings in factor space,
\item compute Derringer–Suich (DS) desirabilities and a combined
multi-response score, and
\item attach a scalar uncertainty measure derived from percentile CI
widths.
}
\item Optionally provide \code{specs} to append mean-level "in spec"
probabilities and joint indicators based on the SVEM bootstrap
ensemble (process-mean assurance).
\item Use \code{\link{svem_select_from_score_table}} to:
\itemize{
\item select one "best" row (e.g., maximizing \code{score} or
\code{wmt_score}), and
\item pick a small, diverse set of medoid candidates for optimality or
exploration (e.g. high \code{uncertainty_measure}).
}
The robust bounded summaries \code{score}, per-response desirabilities,
per-response weighted CI-width summaries, and
\code{uncertainty_measure} may contain ties at 0 or 1 because of the
percentile clipping described below. If the intended single "best" row is
the largest raw predicted response, select on the corresponding
\code{<response>_pred} column. If the intended single exploration row is
the widest interval, create and select on a raw width column such as
\code{<response>_ciw = <response>_upr - <response>_lwr}.
\item Run selected candidates, append the new data, refit the SVEM models,
and repeat as needed.
}
}

\subsection{Multi-response desirability scoring}{
Each response is mapped to a Derringer–Suich desirability
\eqn{d_r \in [0,1]} according to its goal:
\itemize{
\item \code{goal = "max"}: larger values are better;
\item \code{goal = "min"}: smaller values are better;
\item \code{goal = "target"}: values near a target are best.
}
Per-response anchors (acceptable lower/upper limits or target-band
tolerances) can be supplied in \code{goals}; when not provided, robust
defaults are inferred from the sampled responses using the q0.02–q0.98
span. As a result, the largest few percent of predictions may share
desirability 1 (and the smallest few percent may share desirability 0) when
default anchors are used. This is intentional for robust desirability
scoring. Use \code{<response>_pred} directly when selecting the single
largest or smallest predicted response.

Per-response desirabilities are combined into a single scalar \code{score}
using either:
\itemize{
\item a weighted arithmetic mean (\code{combine = "mean"}), or
\item a weighted geometric mean (\code{combine = "geom"}), with a small
floor applied inside the log to avoid \code{log(0)}.
}
User-provided weights in \code{goals[[resp]]$weight} are normalized to sum
to one and always define \code{weights_original} and the user-weighted
\code{score}.
}

\subsection{Whole-model reweighting (WMT)}{
When a WMT object from \code{\link{svem_wmt_multi}} is supplied via the
\code{wmt} argument, each response receives a multiplier derived from its
whole-model p-value. Final WMT weights are proportional to the product of
the user weight and the multiplier, then renormalized to sum to one:
\deqn{w_r^{(\mathrm{final})} \propto w_r^{(\mathrm{user})} \times m_r,}
where \eqn{m_r} comes from \code{wmt$multipliers}. The user weights always
define \code{score}; the WMT-adjusted weights define \code{wmt_score}.
The uncertainty measure is always weighted using the user weights, even
when WMT is supplied.

\strong{Binomial responses.} If any responses are fitted with
\code{family = "binomial"}, supplying a non-\code{NULL} \code{wmt} object
is not allowed and the function stops with a clear error. Predictions and
CI bounds for binomial responses are interpreted on the probability
(response) scale and clamped to \code{[0, 1]} before desirability and
uncertainty calculations.
}

\subsection{Uncertainty measure}{
The \code{uncertainty_measure} is a weighted sum of robustly normalized
percentile CI widths across responses. For each response, we compute the
bootstrap percentile CI width
\eqn{\mathrm{CIwidth}_r(x) = u_r(x) - \ell_r(x)} and then map it to the
unit interval using an affine rescaling based on the empirical q0.02 and
q0.98 quantiles of the CI widths for that response. The quantile anchors
are computed once from the sampled \code{score_table} and reused when
scoring \code{data}, so \code{uncertainty_measure} in
\code{original_data_scored} is on the same scale as in \code{score_table}:
\deqn{
  \tilde W_r(x) =
  \frac{
    \min\{\max(\mathrm{CIwidth}_r(x), q_{0.02}(r)), q_{0.98}(r)\}
    - q_{0.02}(r)
  }{
    q_{0.98}(r) - q_{0.02}(r)
  }.
}
The scalar \code{uncertainty_measure} is then
\deqn{
  \text{uncertainty}(x) = \sum_r w_r \, \tilde W_r(x),
}
where \eqn{w_r} are the user-normalized response weights derived from
\code{goals[[resp]]$weight}. Larger values of \code{uncertainty_measure}
indicate settings where the ensemble CI is relatively wide compared to the
response's typical scale and are natural targets for exploration. Because
the normalization clips at empirical q0.02 and q0.98, the widest few percent
of intervals may share \code{uncertainty_measure = 1}. Use a raw interval
width column such as
\code{<response>_ciw = <response>_upr - <response>_lwr} when selecting the
single widest interval.
}

\subsection{Spec-limit mean-level probabilities}{
If \code{specs} is provided, \code{svem_score_random()} attempts to pass
the scored table and models to
\code{svem_append_design_space_cols} to compute, for each response
with an active spec:
\itemize{
\item \code{<resp>_p_in_spec_mean}: estimated probability (under the
SVEM bootstrap ensemble) that the process mean at a setting lies
within the specified interval;
\item \code{<resp>_in_spec_point}: 0/1 indicator that the point
prediction lies within the same interval.
}
and joint quantities:
\itemize{
\item \code{p_joint_mean}: product of per-response mean-level
probabilities over responses with active specs;
\item \code{joint_in_spec_point}: 0/1 indicator that all point
predictions are in spec across responses with active specs.
}
Names in \code{specs} may refer either to \code{names(objects)} or to the
model response names; they are automatically aligned to the fitted models.

These probabilities are defined on the \emph{conditional means} at each sampled
setting, not on individual units or lots, and are best interpreted as
ensemble-based assurance measures under the SVEM + FRW pipeline. If the
augmentation step fails for any reason (for example, missing predictor
columns or incompatible models), the error is caught; a message may be
issued when \code{verbose = TRUE}, and \code{score_table} and/or
\code{original_data_scored} are returned without the spec-related columns.
}
}
\examples{
\donttest{
## ------------------------------------------------------------------------
## Multi-response SVEM scoring with Derringer–Suich desirabilities
## ------------------------------------------------------------------------

data(lipid_screen)

# Build a deterministic expansion once and reuse for all responses
spec <- bigexp_terms(
  Potency ~ PEG + Helper + Ionizable + Cholesterol +
    Ionizable_Lipid_Type + N_P_ratio + flow_rate,
  data             = lipid_screen,
  factorial_order  = 3,
  polynomial_order = 3,
  include_pc_2way  = TRUE,
  include_pc_3way  = FALSE
)

form_pot <- bigexp_formula(spec, "Potency")
form_siz <- bigexp_formula(spec, "Size")
form_pdi <- bigexp_formula(spec, "PDI")

set.seed(1)
fit_pot <- SVEMnet(form_pot, lipid_screen)
fit_siz <- SVEMnet(form_siz, lipid_screen)
fit_pdi <- SVEMnet(form_pdi, lipid_screen)

# Collect SVEM models in a named list by response
objs <- list(Potency = fit_pot, Size = fit_siz, PDI = fit_pdi)

# Targets and user weights for Derringer–Suich desirabilities
goals <- list(
  Potency = list(goal = "max", weight = 0.6),
  Size    = list(goal = "min", weight = 0.3),
  PDI     = list(goal = "min", weight = 0.1)
)

# Optional mixture constraints (composition columns sum to 1)
mix <- list(list(
  vars  = c("PEG", "Helper", "Ionizable", "Cholesterol"),
  lower = c(0.01, 0.10, 0.10, 0.10),
  upper = c(0.05, 0.60, 0.60, 0.60),
  total = 1.0
))

# Basic random-search scoring without WMT or design-space specs
set.seed(3)
scored_basic <- svem_score_random(
  objects         = objs,
  goals           = goals,
  n               = 10000,          # number of random candidates
  mixture_groups  = mix,
  combine         = "geom",
  numeric_sampler = "random",
  verbose         = FALSE
)

# Scored candidate table: predictors, <resp>_pred, <resp>_des, score, uncertainty
names(scored_basic$score_table)
head(scored_basic$score_table)

# Scored original data (if 'data' is supplied)
# scored_basic$original_data_scored contains predictions + desirabilities

## ------------------------------------------------------------------------
## With whole-model tests (WMT) and process-mean specifications
## ------------------------------------------------------------------------

set.seed(123)
wmt_out <- svem_wmt_multi(
  formulas       = list(Potency = form_pot,
                        Size    = form_siz,
                        PDI     = form_pdi),
  data           = lipid_screen,
  mixture_groups = mix,
  wmt_control    = list(seed = 123),
  plot           = FALSE,
  verbose        = FALSE
)

# Simple process-mean specs for a joint design space:
#   Potency >= 78, Size <= 100, PDI <= 0.25
specs_ds <- list(
  Potency = list(lower = 78),
  Size    = list(upper = 100),
  PDI     = list(upper = 0.25)
)

set.seed(4)
scored_full <- svem_score_random(
  objects         = objs,
  goals           = goals,
  data            = lipid_screen,  # score the original runs as well
  n               = 25000,
  mixture_groups  = mix,
  level           = 0.95,
  combine         = "geom",
  numeric_sampler = "random",
  wmt             = wmt_out,       # optional: WMT reweighting
  specs           = specs_ds,      # optional: design-space columns
  verbose         = TRUE
)

# The scored table now includes:
#  * score, wmt_score, uncertainty_measure
#  * per-response CIs: <resp>_lwr, <resp>_upr
#  * design-space columns, e.g. Potency_p_in_spec_mean, p_joint_mean
names(scored_full$score_table)

## ------------------------------------------------------------------------
## Positional (unnamed) goals matched to objects by position
## ------------------------------------------------------------------------

data(lipid_screen)

# Build a deterministic expansion once and reuse for all responses
spec <- bigexp_terms(
  Potency ~ PEG + Helper + Ionizable + Cholesterol +
    Ionizable_Lipid_Type + N_P_ratio + flow_rate,
  data             = lipid_screen,
  factorial_order  = 3,
  polynomial_order = 3,
  include_pc_2way  = TRUE,
  include_pc_3way  = FALSE
)

form_pot <- bigexp_formula(spec, "Potency")
form_siz <- bigexp_formula(spec, "Size")
form_pdi <- bigexp_formula(spec, "PDI")

set.seed(1)
fit_pot <- SVEMnet(form_pot, lipid_screen)
fit_siz <- SVEMnet(form_siz, lipid_screen)
fit_pdi <- SVEMnet(form_pdi, lipid_screen)

# Collect SVEM models in a list.
# Here goals will be matched by position: Potency, Size, PDI.
objs <- list(fit_pot, fit_siz, fit_pdi)

# Positional goals (unnamed list): must have same length as 'objects'
goals_positional <- list(
  list(goal = "max", weight = 0.6),  # for Potency (objs[[1]])
  list(goal = "min", weight = 0.3),  # for Size    (objs[[2]])
  list(goal = "min", weight = 0.1)   # for PDI     (objs[[3]])
)

set.seed(5)
scored_pos <- svem_score_random(
  objects         = objs,
  goals           = goals_positional,
  n               = 5000,
  numeric_sampler = "random",
  verbose         = FALSE
)

names(scored_pos$score_table)

}

}
\seealso{
\code{\link{SVEMnet}},
\code{\link{svem_random_table_multi}},
\code{\link{svem_select_from_score_table}},
\code{svem_append_design_space_cols()},
\code{\link{svem_wmt_multi}}
}
