Package {SVEMnet}


Type: Package
Title: Self-Validated Ensemble Models with Lasso and Relaxed Elastic Net Regression
Version: 4.0.0
Date: 2026-11-07
Description: Implements the self-validated elastic-net and relaxed elastic-net ensemble modeling and multi-response optimization workflow described in Karl (2026) <doi:10.1016/j.chemolab.2026.105660>. Self-validated ensemble models (SVEM; Lemkus et al. (2021) <doi:10.1016/j.chemolab.2021.104439>) are fitted for small-sample design-of-experiments and related workflows using 'glmnet' (Friedman et al. (2010) <doi:10.18637/jss.v033.i01>). Fractional random-weight bootstraps with anti-correlated validation copies are used to tune penalty paths by validation-weighted AIC/BIC. Supports Gaussian and binomial responses, deterministic expansion helpers for shared factor spaces, prediction with bootstrap uncertainty, and a random-search optimizer that respects mixture constraints and combines multiple responses via desirability functions. Gaussian process models from 'GauPro' and extremely randomized forests from 'ranger' can each nominate candidate recipes for follow-up experiments using the same factors and constraints. The default formulation workflow carries forward the best observed preparation, nominates new candidates from elastic-net SVEM, Gaussian process, extremely randomized forest and forward SVEM models, and fills the remaining preparation budget with uncertainty medoids. Users can set a total preparation budget or request separate counts of new model candidates and exploration runs alongside optional reference repeats. A living executable vignette shows the shared factor specification, explicit scientific goals, worklist export, confirmation and refitting. An optional Gaussian self-validated smooth surface can nominate an additional recipe using a saved response-free domain, thin-plate or higher-dimensional Duchon spline bases, categorical study effects, multiple mixture groups and validation-tuned curvature shrinkage. Declared blocks remain additive. Gaussian forward selection uses validation likelihood to select among training-weighted least-squares fits. Also includes a permutation-based whole-model test for Gaussian SVEM fits (Karl (2024) <doi:10.1016/j.chemolab.2024.105122>). Package code was drafted with assistance from generative AI tools.
URL: https://doi.org/10.1016/j.chemolab.2026.105660, https://arxiv.org/abs/2511.20968, https://arxiv.org/abs/2212.11264, https://arxiv.org/abs/2610.36012
Depends: R (≥ 4.0.0)
Imports: glmnet (≥ 4.1-6), stats, cluster, ggplot2, lhs, parallel, utils
Suggests: foreach, testthat (≥ 3.2.0), withr, RhpcBLASctl, GauPro (≥ 0.2.17), ranger (≥ 0.18.0), digest, mgcv, knitr, rmarkdown
VignetteBuilder: knitr
License: GPL-2 | GPL-3
Encoding: UTF-8
Config/testthat/edition: 3
LazyData: true
Config/roxygen2/version: 8.1.0
NeedsCompilation: no
Packaged: 2026-11-07 23:08:09 UTC; andre
Author: Andrew T. Karl ORCID iD [cre, aut]
Maintainer: Andrew T. Karl <akarl@asu.edu>
Repository: CRAN
Date/Publication: 2026-11-08 16:20:50 UTC

SVEMnet: Self-Validated Ensemble Models with Relaxed Lasso and Elastic-Net Regression

Description

The SVEMnet package implements Self-Validated Ensemble Models (SVEM) using Elastic Net (including lasso and ridge) regression via glmnet. SVEM averages predictions from multiple models fitted to fractionally weighted bootstraps of the data, tuned with anti-correlated validation weights. The package supports multi-response optimization with uncertainty-aware candidate generation for iterative formulation and process development.

Details

The primary package reference is Karl (2026), doi:10.1016/j.chemolab.2026.105660, published in Chemometrics and Intelligent Laboratory Systems. It describes the elastic-net and relaxed elastic-net ensemble workflow, shared factor expansions, and multi-response optimization that form the package's core. The analyses in that paper used SVEMnet version 3.2.0; see the package NEWS for subsequent changes. Use citation("SVEMnet") for the package paper and related references. The LNP workflow paper by Karl et al. (2023) is available in JoVE (doi:10.3791/65200) and on arXiv (https://arxiv.org/abs/2212.11264). The SVEMnet package paper is also on arXiv (https://arxiv.org/abs/2511.20968). The journal articles are fixed versions of record. The authors update the arXiv manuscripts occasionally. Each arXiv link opens the latest posted revision.

A Gaussian response fit can also request GauPro and ranger Extra Trees companions. The worked example builds a shared feasible recipe table, compares the models' nominees, and updates their fits after simulated preparations. Run vignette("lipid-screen-follow-up", package = "SVEMnet"). Astra assisted with development of the version 4.0.0 companion fitting and prediction code in SVEMnet() and predict(), the candidate worklist in svem_score_random(), and lipid_screen_simulate(). Astra also assisted with implementation of the default workflow and the optional Gaussian spline. The cited literature provides the statistical foundations of these methods.

Default formulation workflow

Start with vignette("lipid-screen-follow-up", package = "SVEMnet"). The same interface handles process-only factors, mixtures and mixture-process factors, including ingredient categories, through a shared specification and an explicit feasible recipe pool. The default portfolio requires numeric Gaussian responses on the declared analysis scale. Binomial models remain available through the individual fitting and scoring functions.

  1. Declare factors with bigexp_terms, scientific response goals, mixture constraints, a feasible pool and the planned block. The existing cubic default includes pure powers, two-factor partial cubics and three-way interactions. It is a rich candidate basis for model reduction, not a claim that every term is independently estimable.

  2. Call svem_plan_runs with the total preparation budget. The default carries the best observed reference forward, then proposes new optima from elastic-net SVEM, GauPro, Extra Trees and forward SVEM. Shared optima use one preparation. Remaining slots are uncertainty medoids.

  3. Inspect fits, scores, nominations, reference provenance and any shortfall. Plot the plan and explicitly export its worklist with svem_export_candidates_csv.

  4. Measure every selected preparation, append the outcomes, and call the same planner for the next round. Use the same scientific response scale and anchors when comparing rounds.

Whole-model tests and specification-probability diagnostics are optional; neither is required by this streamlined workflow. Users can instead compose the individual functions, including svem_fit_responses and svem_score_random, or customize the named controls.

Planning the initial experiment

Karl's A Sphericity Filter for Space-Filling Designs in Nondeterministic Experiments (https://arxiv.org/abs/2610.36012) is a useful starting point for selecting an initial space-filling experiment with protection for low-order effects. Its working model for design protection is separate from the richer candidate model used for response analysis. SVEMnet currently consumes measured data and feasible recipes; it does not implement that initial-design filter. Declare the physical region, prediction target and allowed categorical combinations before generating an initial experiment.

Core modeling and summaries

SVEMnet

Fit an SVEMnet model using Elastic Net regression (including relaxed elastic net) on fractionally weighted bootstraps.

predict.svem_model

Predict method for SVEM models (ensemble-mean aggregation by default, optional debiasing, and percentile intervals across bootstrap member predictions).

coef.svem_model

Averaged (optionally debiased) coefficients from an SVEM model.

svem_nonzero

Bootstrap nonzero percentages for each coefficient, with an optional quick plot.

plot.svem_model

Quick actual-versus-predicted plot for a fitted model (with ROC, precision-recall, and calibration displays for binomial fits).

Deterministic wide expansions (bigexp helpers)

The bigexp_* helpers build and reuse a locked polynomial/interaction expansion across multiple responses and datasets:

bigexp_terms

Build a deterministic expanded RHS (polynomials, interactions, optional partial-cubic terms) with locked factor levels and numeric ranges.

bigexp_prepare

Coerce new data to match a stored bigexp_spec, including factor levels and numeric types.

bigexp_formula

Reuse a locked expansion for another response to ensure an identical factor space across models.

with_bigexp_contrasts

Temporarily restore the contrast options used when a bigexp_spec was built.

bigexp_train

Convenience wrapper that builds a bigexp_spec and prepares training data in one call.

Random tables, optimization, and candidate generation

svem_random_table_multi

Generate one shared random predictor table (with optional mixture constraints) from cached factor-space information and obtain predictions from multiple SVEM models at those points. Supports both Gaussian and binomial models; binomial predictions are returned on the probability scale. This is the lower-level sampler used by svem_score_random.

svem_score_random

Random-search scoring for multiple responses with Derringer–Suich desirabilities, user weights, optional whole-model-test (WMT) reweighting, percentile CI-based uncertainty, and (optionally) scoring of existing experimental data. Returns a scored random-search table and, when data is supplied, an augmented copy of the original data with <resp>_pred, desirabilities, scores, and an uncertainty_measure.

svem_select_from_score_table

Given a scored table (typically svem_score_random()$score_table), select one "best" row under a chosen objective and a small, diverse set of medoid candidates via PAM clustering on predictors.

svem_export_candidates_csv

Concatenate one or more selection objects from svem_select_from_score_table and export candidate tables (with metadata, predictions, and response columns) to CSV or return them in-memory for inspection.

Whole-model testing and plotting

svem_significance_test_parallel

Whole-model significance test with serial execution by default and an optional private PSOCK cluster for explicit multi-core runs. Supports mixture-constrained sampling and reuse of a locked bigexp_spec; designed for continuous (Gaussian) responses.

svem_wmt_multi

Helper to run svem_significance_test_parallel across multiple responses and construct whole-model p-values and reweighting multipliers for use in svem_score_random.

plot.svem_significance_test

Plot helper for visualizing one or more significance-test outputs (boxplots of observed vs permutation distances).

Auxiliary utilities and data

glmnet_with_cv

Convenience wrapper around repeated cv.glmnet() selection for robust lambda (and optional alpha) choice.

lipid_screen

Example dataset for multi-response modeling, whole-model testing, and mixture-constrained optimization demonstrations.

Extensions beyond the package paper

GauPro and ranger Extra Trees can nominate additional recipes from the same physical candidate pool. svem_score_random can allocate a finite preparation budget to these nominees and to exploration recipes. The package also includes newer methods for further study: svem_forward and its single-model benchmark forward_aicc, the bootstrap-member batch proposer svem_thompson_batch, and the FRW variance diagnostic svem_ij_variance. The optional Gaussian thin-plate smoother, spline_terms and svem_spline, uses the suggested mgcv package. It can nominate an additional recipe on a supported numeric domain after the four default model roles. The opt-in complexity = "edf" selector in SVEMnet is also experimental; the published package workflow uses complexity = "support". These extensions are not established by the results in the package paper. Opt-in polynomial centering in bigexp_terms is a later design-matrix construction option and was not evaluated in that paper.

Bootstrap member-percentile intervals describe variation among member predictions. They do not include new-observation noise and do not by themselves provide calibrated confidence or prediction intervals.

Families

SVEMnet currently supports:

Some higher-level utilities place additional constraints:

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

Author(s)

Maintainer: Andrew T. Karl akarl@asu.edu (ORCID)

Authors:

References

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

See Also

Useful links:

Examples

if (requireNamespace("GauPro", quietly = TRUE) &&
    requireNamespace("ranger", quietly = TRUE)) {
  set.seed(41)
  design <- data.frame(x = seq(-1, 1, length.out = 16))
  design$y <- 2 - (design$x - 0.3)^2 + rnorm(nrow(design), sd = 0.05)
  # A small bootstrap count keeps this example quick.
  fit <- SVEMnet(y ~ x, design, nBoot = 4L, companions = TRUE)
  predict(fit, data.frame(x = c(-0.5, 0, 0.5)), engine = "all")
}

Fit an SVEMnet model (Self-Validated Ensemble Elastic Net)

Description

Fit a Self-Validated Ensemble Model (SVEM) with elastic net or relaxed elastic net base learners using glmnet. Fractional random-weight (FRW) train/validation weights are drawn on each bootstrap replicate, a validation-weighted information criterion (wAIC, wBIC, or wSSE) is minimized to select the penalty, and predictions are ensembled across replicates. Gaussian and binomial responses are supported.

Usage

SVEMnet(
  formula,
  data,
  nBoot = 200,
  glmnet_alpha = c(0.5, 1),
  weight_scheme = c("SVEM", "FRW_plain", "Identity"),
  objective = c("auto", "wAIC", "wBIC", "wSSE"),
  relaxed = "auto",
  response = NULL,
  unseen = c("warn_na", "error"),
  family = c("gaussian", "binomial"),
  complexity = c("support", "edf"),
  ...,
  store_member_weights = FALSE,
  companions = FALSE,
  companion_blocks = c("respect", "interacting"),
  mixture_groups = NULL,
  companion_continuous_blocks = character()
)

Arguments

formula

A formula specifying the model to be fitted, or a bigexp_spec created by bigexp_terms().

data

A data frame containing the variables in the model.

nBoot

Integer. Number of bootstrap replicates (default 200). Each replicate draws FRW weights and fits a glmnet path.

glmnet_alpha

Numeric vector of elastic net mixing parameters alpha in [0, 1]. alpha = 1 is lasso, alpha = 0 is ridge. Defaults to c(0.5, 1). When relaxed = TRUE, alpha = 0 is automatically dropped (ridge + relaxed is not used).

weight_scheme

Character. Weighting scheme for train/validation copies. One of:

  • "SVEM" (default): Self-Validated Ensemble Model weights. For each replicate and row, a shared uniform draw U_i ~ Unif(0, 1) is converted to anti-correlated FRW weights w_train_i = -log(U_i) and w_valid_i = -log(1 - U_i). Each weight vector is then rescaled to have mean 1 (sum n).

  • "FRW_plain": Fractional random-weight regression without a separate validation copy. A single FRW vector w_i = -log(U_i) is used for both training and validation and rescaled to have mean 1 (sum n). This reproduces the FRW bootstrap regression of Xu et al. (2020) and related work.

  • "Identity": Uses unit weights for both training and validation (no resampling). In combination with nBoot = 1 this wraps a single glmnet fit and selects the penalty by the chosen information criterion, while still using SVEMnet's expansion and diagnostics.

objective

Character. One of "auto", "wAIC", "wBIC", or "wSSE". See Details for the automatic choices, the information criteria and the loss-only criterion for Gaussian and binomial responses.

relaxed

Logical or character. Default "auto". If TRUE, use glmnet's relaxed elastic-net path and select both the penalty lambda and the relaxed refit parameter gamma on each bootstrap. If FALSE, fit the standard glmnet path without the relaxed step. If "auto" (default), SVEMnet uses relaxed = TRUE for family = "gaussian" and relaxed = FALSE for family = "binomial".

response

Optional character. When formula is a bigexp_spec, this names the response column to use on the left-hand side. Defaults to the response stored in the spec.

unseen

How to treat factor levels not seen in the original bigexp_spec when formula is a bigexp_spec. One of "warn_na" (default; convert unseen levels to NA with a warning) or "error" (stop with an error).

family

Character. One of "gaussian" (default) or "binomial". For Gaussian models SVEMnet uses the identity link; for binomial it uses the canonical logit link. The binomial response must be numeric 0/1, logical, or a factor with exactly two levels (the second level is treated as 1).

complexity

Character. Complexity measure used in the wAIC/wBIC penalty term. One of:

  • "support" (default): the number of nonzero coefficients (including the intercept), as described in the package references. It is a model-size proxy. For a fixed lasso tuning parameter it estimates degrees of freedom under the usual full-rank assumptions; collinearity and adaptive tuning require additional care.

  • "edf" (experimental, provided for further study): a gamma-blended effective degrees of freedom. For an elastic-net path point with active set A, penalty \lambda, and mixing parameter \alpha < 1, the penalized fit's effective df is the ridge-type trace \sum_j e_j / (e_j + n(\lambda/s_{y,w})(1-\alpha)), where e_j are the eigenvalues of the weighted, standardized Gram matrix of the active columns, and s_{y,w} is the training-weighted response standard deviation. This conditional trace holds the active set and tuning choices fixed; it does not include their adaptation cost. Nonuniform penalty factors enter the corresponding diagonal ridge matrix. Finite coefficient bounds are not supported for EDF selection; support-selected bounded fits report EDF as unavailable. The relaxed refit's df is the rank of the active design. The blended complexity for relaxed mixing parameter \gamma is 1 + \gamma \, df_{enet} + (1-\gamma) \, rank(A). For \alpha = 1 (lasso) this reduces to one plus the rank of the centered active design (Tibshirani and Taylor, 2012), equal to support when those columns are linearly independent. The numerical rank uses Gram eigenvalues exceeding 10^{-10} times the largest eigenvalue; very ill-conditioned columns can therefore count as dependent. Support also overstates the conditional trace for \alpha < 1. Implemented for family = "gaussian" only; binomial fits fall back to "support" with a warning.

The admissibility guardrail (the nonzero non-intercept count must be strictly less than n_{eff,adm}) and the reported model-size diagnostics always use the support count, regardless of this setting. "edf" changes only the complexity value inserted into the wAIC/wBIC penalty and is intended for simulation study of the alpha/gamma comparison; the published SVEM results use "support". This separate conservative constraint can exclude elastic-net models with many nonzeros even when their conditional EDF is small.

...

Additional arguments passed to glmnet(), such as penalty.factor, lower.limits, upper.limits, or standardize.response. An exclude callback receives the original design columns and the current fit's response and weights; an internal compatibility column is never passed to it. Numerically constant columns remain excluded in addition to user exclusions. When applying this mask or handling a one-column design, the package checks that callback returns index the original columns (or form an aligned logical vector); other callbacks retain glmnet's validation. Any user-supplied weights are ignored (SVEM supplies its own fractional random weights), any user-supplied offset is ignored (SVEMnet does not support offsets), and any user-supplied standardize is ignored; SVEMnet always calls glmnet with standardize = TRUE. Glmnet algorithm-control values (for example maxit or thresh) supplied directly or through control = list(...) are routed in a version-compatible way when possible. The arguments nlambda, lambda, and lambda.min.ratio are reserved and managed by SVEMnet (nlambda is fixed at 500); user-supplied values are ignored with a warning. Argument names that the installed glmnet does not recognize are also ignored with a warning (misspelling protection). Relaxed fits additionally accept maxp and path.

store_member_weights

Logical; default FALSE. If TRUE, retain the exact per-member uniforms and the mean-one training and validation weights in member_weights. This can materially increase object size. The stored weights support reproducibility and experimental ensemble-mean diagnostics; they do not make the member-percentile limits calibrated confidence or prediction intervals.

companions

Optional Gaussian-response companion fits. FALSE fits SVEM alone. TRUE requests both GauPro and ranger Extra Trees. A character vector can select "gaupro" or "extra_trees" individually. Companions use the original factors on the SVEM analysis rows. Requested fits and diagnostics are stored in $companions; an unavailable engine leaves the primary fit usable. The companion component retains the unique physical predictor rows used for that endpoint to identify previously prepared recipes at nomination. The Gaussian process uses GauPro's Matern 5/2 kernel on continuous coordinates and latent-factor kernels on nominal inputs. It estimates the nugget with one seeded optimization start. The fit records its seed, package version, kernel settings and attained GauPro deviance on its internally normalized response scale. That deviance is not comparable with earlier unnormalized fits. Printing the companion component reports how many fit notes were retained in its warnings field.

companion_blocks

With "respect" (default), a varying block declared in a BigExp specification makes the companion fits unsupported. "interacting" explicitly permits each companion to learn a different recipe surface per block. Numeric day, plate or batch IDs are nominal labels by default. Supply observed IDs when scoring them. An unreplicated numeric block triggers a diagnostic at fitting time.

mixture_groups

Optional list of fixed-sum mixture groups in svem_random_table_multi format. A BigExp fit inherits its groups from bigexp_terms. Companions use one fewer varying coordinate per group. They drop the last varying component in the declared order and check all components at prediction. The GP and forest can give different fits in a different choice of free coordinates.

companion_continuous_blocks

Names of measured numeric blocking covariates, such as ambient temperature, to treat as continuous inputs in an interacting companion. Such a covariate can be scored between observed values within its fitted range. Other blocks retain their nominal encoding. The SVEM formula and its block penalties are unaffected.

Details

This is the core ensemble fitter described in Karl (2026), doi:10.1016/j.chemolab.2026.105660. The published package workflow uses the default complexity = "support"; the optional complexity = "edf" selector is an experimental extension.

You can pass either:

The Gaussian SVEMnet elastic-net search requests 500 lambda values with glmnet's deviance-based early path stopping disabled (fdev = 0, devmax = 1). Other solver limits can still shorten the requested path. Disabling these rules preserves solver convergence checks, coefficient limits and SVEM's validation-size support guard. Explicit algorithm controls are honored. These call-level defaults override session-level stopping settings. Use control = list(fdev = 1e-5, devmax = 0.999) to recover the former stopping policy. Modern glmnet uses per-call controls; older supported versions use a scoped override restored even on error. This default applies to Gaussian SVEMnet members, including fits inside the whole-model test. Binomial members and the separate glmnet_with_cv() comparison wrapper retain their former stopping defaults. To request the extended binomial path, supply control = list(fdev = 0, devmax = 1). Retained solver warnings, their member count, the glmnet version and the effective call-level controls are available in diagnostics. Changing the path can change fits at any noise level and whole-model p-values or multipliers. Results are not uniformly better on new data: a small stress check saw some small-sample and wSSE fits worsen.

GauPro companions normalize the response internally and return means and latent standard errors in the original response units. An unsupported single-factor input or a constant response is reported before claiming a usable GP fit. Engine messages are retained with companion warnings.

SVEMnet implements Self-Validated Ensemble Models using elastic net and relaxed elastic net base learners from glmnet. Each bootstrap replicate draws fractional random weights, builds a train and validation copy, fits a path over lambda (and optionally over alpha and relaxed gamma), and selects a path point by minimizing a validation-weighted criterion. Final predictions are obtained by averaging replicate predictions on the chosen scale.

By default, relaxed = "auto" resolves to TRUE for Gaussian fits and FALSE for binomial fits.

The function is typically used in small-n design-of-experiments (DOE) workflows where classical train/validation splits and cross-validation can be unstable. A common pattern is: (1) build a deterministic expansion with bigexp_terms(), (2) fit SVEM models via SVEMnet(), (3) perform whole-model significance testing, and (4) call svem_score_random() for constrained multi-response optimization.

Weighting schemes:

Selection criteria (Gaussian): For family = "gaussian", the validation loss is a weighted sum of squared errors on the validation copy. Let \mathrm{SSE}_w = \sum_i w_i^{valid} r_i^2 denote the weighted SSE. The criteria are:

The FRW validation weights are rescaled to have mean one, so that their sum is always \sum_i w_i^{valid} = n. The AIC/BIC analogs therefore use n \log(\mathrm{SSE}_w/n) as the Gaussian loss term, while the "wSSE" selector uses \mathrm{SSE}_w directly.

The effective validation size is computed from the FRW weights using Kish's effective sample size n_eff = (\sum_i w_i^{valid})^2 / \sum_i (w_i^{valid})^2 and then truncated to lie between 2 and n to form n_eff_adm. The AIC-style selector uses a 2 k penalty; the BIC-style selector uses a \log(n_eff_adm) k penalty, so that the loss term is scaled by total validation weight while the complexity penalty uses a weight-concentration adjustment. For "wAIC" and "wBIC", the number of non-intercept coefficients must be strictly less than n_eff_adm; equality is inadmissible.

Because the FRW validation weights are random rather than fixed design weights, these information-criterion scores are used heuristically for relative model comparison within each FRW replicate, rather than as exact AIC/BIC values. The training and validation copies reuse the same responses; they are not independent held-out samples. Kish size measures weight concentration for a weighted mean, not regression rank, leverage or selection complexity. The name wAIC does not refer to Bayesian WAIC. Also, wBIC has a smaller per-coefficient penalty than wAIC when n_eff_adm < \exp(2). Normalizing weights to total n defines the loss-to-penalty scale; substituting Kish size in the loss would be a different selection rule.

For diagnostics, SVEMnet reports the raw Kish effective sizes across bootstraps (see diagnostics$n_eff_summary), while n_eff_adm is used internally in the penalty and model-size guardrail. Near-interpolating path points are screened out via a simple model size guardrail before minimization. When objective = "auto", Gaussian fits use "wAIC" and binomial fits use "wBIC". This guard is a heuristic cap, not a guarantee of positive residual degrees of freedom. With weight_scheme = "Identity", support may reach n; shrunken fits can still have positive SSE, while exact interpolation makes the Gaussian profile likelihood singular. Nonpositive support-based residual denominators are flagged in member_diagnostics.

This structure (pseudo-likelihood using total weight and BIC penalty using a Kish-type effective sample size) parallels survey-weighted information criteria as in Lumley and Scott (2015) and Kish (1965).

Selection criteria (binomial): For family = "binomial", the validation loss is the weighted negative log-likelihood on the FRW validation copy (equivalently, proportional to the binomial deviance up to a constant factor). Let \mathrm{NLL} denote the weighted negative log-likelihood. The same labels are used:

The effective validation size n_eff_adm and the model size guardrail are handled as in the Gaussian case: for "wAIC" and "wBIC" we compute a Kish effective size from the FRW validation weights, truncate it to lie between 2 and n, and require the number of nonzero coefficients (excluding the intercept) to be less than this effective size when evaluating the criterion.

Auto rule: When objective = "auto", SVEMnet selects the criterion by family:

Relaxed elastic net: When relaxed = TRUE, SVEMnet calls glmnet with relax = TRUE and traverses a small grid of relaxed refit values (gamma). For each alpha and gamma, SVEMnet evaluates all lambda path points on the validation copy and records the combination that minimizes the selected criterion. Model size is always defined as the number of nonzero coefficients including the intercept, so standard and relaxed paths are scored on the same scale.

Gaussian debiasing: For Gaussian models, SVEMnet optionally performs a simple linear calibration of ensemble predictions on the training data. When there is sufficient variation in the fitted values and nBoot is at least 10, the function fits lm(y ~ y_pred) and uses the coefficients to construct debiased coefficients and debiased fitted values. Binomial fits do not use debiasing; predictions are ensembled on the probability or link scale directly.

Implementation notes:

Value

An object of class "svem_model" (and "svem_binomial" when family = "binomial") with components:

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

References

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

Zou, H., Hastie, T., & Tibshirani, R. (2007). On the "degrees of freedom" of the lasso. The Annals of Statistics, 35(5), 2173–2192. doi:10.1214/009053607000000127

Examples

set.seed(42)

n  <- 30
X1 <- rnorm(n)
X2 <- rnorm(n)
X3 <- rnorm(n)
eps <- rnorm(n, sd = 0.5)
y <- 1 + 2 * X1 - 1.5 * X2 + 0.5 * X3 + 1.2 * (X1 * X2) +
  0.8 * (X1^2) + eps
dat <- data.frame(y, X1, X2, X3)

# Minimal hand-written expansion
mod_relax <- SVEMnet(
  y ~ (X1 + X2 + X3)^2 + I(X1^2) + I(X2^2),
  data          = dat,
  glmnet_alpha  = 1,
  nBoot         = 10,
  objective     = "auto",
  weight_scheme = "SVEM",
  relaxed       = FALSE
)

pred_in_raw <- predict(mod_relax, dat, debias = FALSE)
pred_in_db  <- predict(mod_relax, dat, debias = TRUE)

# ---------------------------------------------------------------------------
# Big expansion (full factorial + polynomial surface + partial-cubic crosses)
# Build once, reuse for one or more responses
# ---------------------------------------------------------------------------
spec <- bigexp_terms(
  y ~ X1 + X2 + X3,
  data             = dat,
  factorial_order  = 3,
  polynomial_order = 3,
  include_pc_3way  = FALSE
)

# Fit using the spec (auto-prepares data)
fit_y <- SVEMnet(
  spec, dat,
  glmnet_alpha  = 1,
  nBoot         = 8,
  objective     = "auto",
  weight_scheme = "SVEM"
)

# A second, independent response over the same expansion
set.seed(99)
dat$y2 <- 0.5 + 1.4 * X1 - 0.6 * X2 + 0.2 * X3 + rnorm(n, 0, 0.4)
fit_y2 <- SVEMnet(
  spec, dat, response = "y2",
  glmnet_alpha  = 1,
  nBoot         = 8,
  objective     = "auto",
  weight_scheme = "SVEM"
)

svem_nonzero(fit_y2)

p1 <- predict(fit_y,  dat)
p2 <- predict(fit_y2, dat, debias = TRUE)

# Show that a new batch expands identically under the same spec
newdat <- data.frame(
  y  = y,
  X1 = X1 + rnorm(n, 0, 0.05),
  X2 = X2 + rnorm(n, 0, 0.05),
  X3 = X3 + rnorm(n, 0, 0.05)
)
prep_new <- bigexp_prepare(spec, newdat)
stopifnot(identical(
  colnames(model.matrix(spec$formula, bigexp_prepare(spec, dat)$data)),
  colnames(model.matrix(spec$formula, prep_new$data))
))
preds_new <- predict(fit_y, prep_new$data)

## Binomial example
set.seed(2)
n  <- 80
X1 <- rnorm(n); X2 <- rnorm(n); X3 <- rnorm(n)
eta <- -0.3 + 1.1 * X1 - 0.8 * X2 + 0.5 * X1 * X3
p   <- plogis(eta)
yb  <- rbinom(n, 1, p)
db  <- data.frame(yb = yb, X1 = X1, X2 = X2, X3 = X3)

fit_b <- SVEMnet(
  yb ~ (X1 + X2 + X3)^2, db,
  nBoot        = 10,
  glmnet_alpha = 1,
  family       = "binomial"
)

## Probabilities, link, and classes
p_resp <- predict(fit_b, db, type = "response")
p_link <- predict(fit_b, db, type = "link")
y_hat  <- predict(fit_b, db, type = "class")  # 0/1 labels

## Mean aggregation with uncertainty on probability scale
out_b <- predict(
  fit_b, db,
  type     = "response",
  se.fit   = TRUE,
  interval = TRUE,
  level    = 0.9
)
str(out_b)

## Example with blocking (requires SVEMnet to store sampling_schema$blocking)
set.seed(2)
df_block <- data.frame(
  y1         = rnorm(40),
  y2         = rnorm(40),
  X1         = runif(40),
  X2         = runif(40),
  Operator   = factor(sample(paste0("Op", 1:3), 40, TRUE)),
  AmbientTmp = rnorm(40, mean = 22, sd = 2)
)

spec_block <- bigexp_terms(
  y1 ~ X1 + X2,
  data             = df_block,
  factorial_order  = 2,
  polynomial_order = 2,
  blocking         = c("Operator", "AmbientTmp")
)

fit_b1 <- SVEMnet(spec_block, df_block, response = "y1", nBoot = 8,
                  glmnet_alpha = 1, relaxed = FALSE)
fit_b2 <- SVEMnet(spec_block, df_block, response = "y2", nBoot = 8,
                  glmnet_alpha = 1, relaxed = FALSE)

tab_block <- svem_random_table_multi(list(fit_b1, fit_b2), n = 200)

Construct a formula for a new response using a bigexp_spec

Description

bigexp_formula() lets you reuse an existing expansion spec for multiple responses. It keeps the right hand side locked but changes the response variable on the left hand side.

Usage

bigexp_formula(spec, response)

Arguments

spec

A "bigexp_spec" object created by bigexp_terms().

response

Character scalar giving the name of the new response column in your data. If omitted, the original formula is returned unchanged.

Details

This is useful when you want to fit separate models for several responses on the same factor space while guaranteeing that they all use exactly the same design columns and coding.

Value

A formula of the form response ~ rhs, where the right-hand side is taken from the locked expansion stored in spec.

Examples

set.seed(1)
df2 <- data.frame(
  y1 = rnorm(10),
  y2 = rnorm(10),
  X1 = rnorm(10),
  X2 = rnorm(10)
)

spec2 <- bigexp_terms(
  y1 ~ X1 + X2,
  data             = df2,
  factorial_order  = 2,
  polynomial_order = 2
)

f2 <- bigexp_formula(spec2, "y2")
f2


Prepare data to match a bigexp_spec

Description

bigexp_prepare() coerces a new data frame so that it matches a previously built bigexp_terms spec. It:

Usage

bigexp_prepare(spec, data, unseen = c("warn_na", "error"))

Arguments

spec

Object returned by bigexp_terms.

data

New data frame (for example, training, test, or future batches).

unseen

How to handle unseen factor levels in data: "warn_na" (default) maps unseen levels to NA and issues a warning, or "error" stops with an error if any unseen levels are encountered.

Details

Columns that are not listed in spec$vars (for example, the response or extra metadata columns) are left unchanged.

The goal is that model.matrix(spec$formula, data) will produce the same set of columns in the same order across all datasets prepared with the same spec, even if some levels are missing in a particular batch.

Value

A list with two elements:

See Also

bigexp_terms

Examples

set.seed(1)
train <- data.frame(
  y  = rnorm(10),
  X1 = rnorm(10),
  X2 = rnorm(10),
  G  = factor(sample(c("A", "B"), 10, replace = TRUE))
)

spec <- bigexp_terms(
  y ~ X1 + X2 + G,
  data             = train,
  factorial_order  = 2,
  polynomial_order = 2
)

newdata <- data.frame(
  y  = rnorm(5),
  X1 = rnorm(5),
  X2 = rnorm(5),
  G  = factor(sample(c("A", "B"), 5, replace = TRUE))
)

prep <- bigexp_prepare(spec, newdata)
str(prep$data)


Create a deterministic expansion spec for wide polynomial and interaction models

Description

bigexp_terms() builds a specification object that:

Usage

bigexp_terms(
  formula,
  data,
  factorial_order = 3L,
  polynomial_order = 3L,
  include_pc_2way = TRUE,
  include_pc_3way = FALSE,
  intercept = TRUE,
  blocking = NULL,
  discrete_numeric = NULL,
  audit = c("warn", "error", "none"),
  audit_numeric_rate = 0.9,
  audit_unique_ratio = 0.8,
  audit_min_n = 12L,
  report = TRUE,
  center_polynomials = FALSE,
  mixture_vars = NULL,
  mixture_groups = NULL
)

Arguments

formula

Main-effects formula of the form y ~ X1 + X2 + G or y ~ .. The right-hand side should contain main effects only; do not include : (interactions), ^ (factorial shortcuts), I() powers, transformed terms such as log(X1), or inline polynomial expansions. The helper will generate interactions and polynomial terms automatically. Dot exclusions such as y ~ . - X1 are honored. Note that with y ~ . every non-response column of data becomes a predictor, including any other response columns present in the data frame; list the main effects explicitly (or use exclusions) when the data holds several responses.

data

Data frame used to decide types and lock factor levels.

factorial_order

Integer >= 1. Maximum order of factorial interactions among the non-blocking main effects. For example, 1 gives main effects only, 2 gives up to two-way interactions, 3 gives up to three-way interactions, and so on.

polynomial_order

Integer >= 1. Maximum polynomial degree for continuous non-blocking predictors. A value of 1 means only linear terms; 2 adds squares I(X^2); 3 adds cubes I(X^3); in general, all powers I(X^k) for k from 2 up to polynomial_order are added.

include_pc_2way

Logical. If TRUE (default), polynomial_order >= 2, and factorial_order >= 2, include partial-cubic two-way terms of the form Z:I(X^2) where X is continuous and Z is another non-blocking predictor. (These are cross terms, so factorial_order = 1 — "main effects only" — suppresses them.)

include_pc_3way

Logical. If TRUE, polynomial_order >= 2, and factorial_order >= 3, include partial-cubic three-way terms I(X^2):Z:W among non-blocking predictors.

intercept

Logical. If TRUE (default), include an intercept in the expansion; if FALSE, the generated RHS drops the intercept. The no-intercept form is available for standalone design-matrix workflows; SVEMnet() and glmnet_with_cv() require the intercept convention used by the published SVEM methods and reject such a specification.

blocking

Optional character vector of column names in data to treat as blocking factors. These variables are included in the spec and typed like other predictors (categorical vs continuous), but they enter the model only as additive main effects and never appear in interactions, polynomials, or partial-cubic terms. Important: when using y ~ ., blocking variables are automatically excluded from the "non-blocking" predictor set so they do not trigger a conflict error. When using an explicit RHS (for example y ~ X1 + X2), blocking variables must not also be explicitly listed on the right-hand side.

discrete_numeric

Optional specification of "discrete numeric" predictors for downstream sampling (for example in svem_random_table_multi()). These predictors are still treated as numeric for modeling and expansion (that is, they remain continuous in the design matrix and may participate in polynomial and interaction terms). This option only records a finite set of preferred numeric levels to be used when randomly generating recipes. Supply either:

  • a character vector of predictor names, in which case the allowed levels are inferred as the sorted unique finite values observed in data; or

  • a named list mapping predictor names to numeric vectors of allowed levels. If an entry is NULL or length zero, levels are inferred from data for that predictor.

audit

How to handle suspicious typing / high-cardinality issues when building the spec. One of "warn" (default), "error", or "none". Audits cover numeric-like character/factor columns (including percent strings like "25%"), and very high-cardinality categorical predictors that are likely IDs or mis-typed numerics.

audit_numeric_rate

Numeric in (0,1). If at least this fraction of non-missing values parse as numeric (after stripping commas and an optional trailing %), the column is flagged as numeric-like when stored as character/factor.

audit_unique_ratio

Numeric in (0, 1). For categorical predictors, warn/error if unique(non-missing) / n_nonmissing >= audit_unique_ratio.

audit_min_n

Integer >= 1. Minimum number of non-missing values required before audits are applied.

report

Logical. If TRUE (default), print a compact summary of the inferred predictor types and settings (via print.bigexp_spec) when bigexp_terms() returns.

center_polynomials

Logical; default FALSE. If TRUE, subtract each eligible predictor's fixed training-data mean before raising it to a power. Applies to numeric, non-blocking predictors except those in mixture_vars. See Centered polynomial powers.

mixture_vars

Optional character vector naming numeric, non-blocking predictors whose polynomial powers must remain uncentered. This only excludes them from polynomial centering; it does not impose mixture sum constraints. When mixture_groups is supplied, its component names also define mixture_vars.

mixture_groups

Optional fixed-sum mixture groups, each with vars, lower, upper, and total as in the candidate sampler. Store components at sufficient precision to sum to their total (within 1e-8 * max(1, abs(total))). For a rounded recipe, reconstruct a balancing component at preparation precision and verify all individual bounds. See svem_random_table_multi. The fit and sampler inherit these groups. Components must be continuous, non-blocking predictors.

Details

The expansion for non-blocking predictors can include:

Predictor types are inferred from data:

Variables listed in blocking are included in the spec and are typed using the same rules as other predictors (for example, a numeric blocking variable with many distinct values is treated as continuous). However, blocking variables enter the model only as additive main effects, without interactions or polynomial terms, regardless of factorial_order or polynomial_order.

Once built, a "bigexp_spec" can be reused to create consistent expansions for new datasets via bigexp_prepare and bigexp_formula. The RHS and contrast settings are locked, so the same spec applied to different data produces design matrices with the same columns in the same order (up to missing levels for specific batches).

Value

An object of class "bigexp_spec" with components:

Centered polynomial powers

With center_polynomials = TRUE, a power I(X^k) is replaced by I((X - c)^k), where c is the arithmetic mean of the finite values of X in the data supplied to bigexp_terms(). The same replacement is used wherever that power occurs in a partial-cubic term. Main effects, ordinary factorial interactions, and the non-powered factors in partial-cubic terms remain on their original scales.

Each center is computed separately from its predictor, without excluding rows because another predictor or response is missing. This keeps the expansion independent of the response used to build the spec. The constants are recorded in spec$settings$polynomial_centers and embedded in the formula; bigexp_prepare(), bigexp_formula(), and prediction reuse them without recentering new data. Predictor ranges, discrete sampling levels, and supplied data values are unchanged. Only design-matrix construction changes; fitting algorithms are unchanged, although selecting or penalizing terms in the new basis can change the fitted model.

Predictors named in mixture_vars retain raw powers. Mixture membership is explicit and is not inferred from names, ranges, or observed sums.

Typical workflow

In a typical multi-response workflow you:

  1. Call bigexp_terms() once on your training data to build and lock the expansion (types, levels, contrasts, RHS).

  2. Fit models using spec$formula and the original data (for example, SVEMnet(spec$formula, data, ...) or lm(spec$formula, data)).

  3. For new batches, call bigexp_prepare with the same spec so that design matrices have exactly the same columns and coding.

  4. For additional responses on the same factor space, use bigexp_formula to swap the left-hand side while reusing the locked expansion.

See Also

bigexp_prepare, bigexp_formula, bigexp_train

Examples

## Example 1: small design with one factor
set.seed(1)
df <- data.frame(
  y  = rnorm(20),
  X1 = rnorm(20),
  X2 = rnorm(20),
  G  = factor(sample(c("A", "B"), 20, replace = TRUE))
)

## Two-way interactions and up to cubic terms in X1 and X2
spec <- bigexp_terms(
  y ~ X1 + X2 + G,
  data             = df,
  factorial_order  = 2,
  polynomial_order = 3
)

print(spec)

## Optional centering of powers using fixed training-data means
spec_centered <- bigexp_terms(
  y ~ X1 + X2 + G, data = df,
  factorial_order = 2, polynomial_order = 2,
  center_polynomials = TRUE
)
spec_centered$settings$polynomial_centers
## For mixture-process data, also supply mixture_vars = c("A", "B", "C")
## to keep the named mixture components' powers on their raw scales.

## Example 2: pure main effects (no interactions, no polynomial terms)
spec_main <- bigexp_terms(
  y ~ X1 + X2 + G,
  data             = df,
  factorial_order  = 1,  # main effects only
  polynomial_order = 1   # no I(X^2) or higher
)

## Example 3: blocking factors (categorical and continuous)
set.seed(2)
df_block <- data.frame(
  y           = rnorm(30),
  X1          = rnorm(30),
  X2          = rnorm(30),
  G           = factor(sample(c("A", "B"), 30, replace = TRUE)),
  Operator    = factor(sample(paste0("Op", 1:3), 30, replace = TRUE)),
  AmbientTemp = rnorm(30, mean = 22, sd = 2)  # continuous blocking covariate
)

## Here Operator (categorical) and AmbientTemp (continuous) are blocking factors:
## they enter additively, but do not appear in interactions or polynomials.
spec_block <- bigexp_terms(
  y ~ X1 + X2 + G,
  data             = df_block,
  factorial_order  = 2,
  polynomial_order = 3,
  blocking         = c("Operator", "AmbientTemp")
)

print(spec_block)
spec_block$rhs

## Example 4: discrete numeric predictors (finite numeric support)
## A common case is a numeric process setting that only takes a small set
## of allowed values (e.g., 0.5, 1, 2, 4). Use `discrete_numeric` in
## bigexp_terms() so downstream sampling respects those levels automatically.
set.seed(3)
D_allowed <- c(0.5, 1, 2, 4)
df_disc <- data.frame(
  y  = rnorm(40),
  D  = sample(D_allowed, 40, replace = TRUE),   # numeric with discrete support
  X1 = rnorm(40),
  G  = factor(sample(c("A", "B"), 40, replace = TRUE))
)

# Record that D should be treated as "discrete numeric" for downstream sampling.
# Levels are inferred automatically from the training data.
spec_disc <- bigexp_terms(
  y ~ D + X1 + G,
  data             = df_disc,
  factorial_order  = 2,
  polynomial_order = 2,
  discrete_numeric = "D"
)


# Fit. The discrete support is retained in fit$sampling_schema.
fit_disc <- SVEMnet(spec_disc, df_disc, nBoot = 8,
                    glmnet_alpha = 1, relaxed = FALSE)

# Score random candidates; sampled D values stay in D_allowed
scored <- svem_score_random(
  objects         = list(y = fit_disc),
  goals           = list(y = list(goal = "max", weight = 1)),
  n               = 200,
  numeric_sampler = "random",
  verbose         = FALSE
)

table(scored$score_table$D)
stopifnot(all(scored$score_table$D %in% D_allowed))

Build a spec and prepare training data in one call

Description

bigexp_train() is a convenience wrapper around bigexp_terms and bigexp_prepare. It:

Usage

bigexp_train(formula, data, ...)

Arguments

formula

Main-effects formula such as y ~ X1 + X2 + G or y ~ .. Only main effects should appear on the right hand side.

data

Training data frame used to lock types and levels.

...

Additional arguments forwarded to bigexp_terms(), such as factorial_order, polynomial_order, include_pc_2way, include_pc_3way, and intercept.

Details

This is handy when you want a single object that contains both the spec and the training data in a form that is ready to pass into a modeling function. For more control, you can call bigexp_terms() and bigexp_prepare() explicitly instead.

Value

An object of class "bigexp_train" which is a list with components:

Examples

set.seed(1)
df5 <- data.frame(
  y  = rnorm(20),
  X1 = rnorm(20),
  X2 = rnorm(20)
)

tr <- bigexp_train(
  y ~ X1 + X2,
  data             = df5,
  factorial_order  = 2,
  polynomial_order = 3
)

## Prepared training data and expanded formula:
str(tr$data)
tr$formula

## Example: fit a model using the expanded formula
fit_lm <- lm(tr$formula, data = tr$data)
summary(fit_lm)


Coefficients for SVEM Models

Description

Extracts averaged coefficients from an svem_model fitted by SVEMnet.

Usage

## S3 method for class 'svem_model'
coef(object, debiased = FALSE, ...)

Arguments

object

An object of class svem_model, typically returned by SVEMnet.

debiased

Logical; if TRUE and debiased coefficients are available (Gaussian fits with parms_debiased), return those instead of parms. Default is FALSE.

...

Unused; present for S3 method compatibility.

Details

The default returns object$parms, the ensemble-averaged coefficients. For a Gaussian fit, debiased = TRUE returns the coefficients after calibration when fitted. Otherwise the stored coefficients are unchanged.

For Binomial models, debiased is ignored and the averaged coefficients in object$parms are returned.

This is a lightweight accessor around the stored components of an svem_model:

For older objects that lack parms_debiased, the accessor returns parms.

Value

A named numeric vector of coefficients (including the intercept).

See Also

svem_nonzero for bootstrap nonzero percentages and a quick stability plot.

Examples

  set.seed(1)
  n  <- 50
  x1 <- rnorm(n)
  x2 <- rnorm(n)
  eps <- rnorm(n, sd = 0.3)
  y_g <- 1 + 2*x1 - 0.5*x2 + eps
  dat_g <- data.frame(y_g, x1, x2)

  fit_g <- SVEMnet(y_g ~ x1 + x2, data = dat_g,
                   nBoot = 10, glmnet_alpha = 1, relaxed = FALSE)

  # Ensemble-averaged coefficients
  cc <- coef(fit_g)
  head(cc)

  # Debiased (only if available for Gaussian fits)
  ccd <- coef(fit_g, debiased = TRUE)
  head(ccd)


Forward Selection by AICc (or AIC / BIC)

Description

Deterministic greedy forward selection over whole model terms for Gaussian responses, minimizing a least-squares information criterion. This is a single-model benchmark companion to SVEMnet (in the same spirit as glmnet_with_cv): it shares SVEMnet's formula and bigexp_terms() front end and returns an object compatible with predict(), coef(), and the downstream scoring tools, but fits one deterministic model instead of an SVEM ensemble.

Usage

forward_aicc(
  formula,
  data,
  criterion = c("AICc", "AIC", "BIC"),
  response = NULL,
  unseen = c("warn_na", "error")
)

Arguments

formula

A model formula or a bigexp_spec created by bigexp_terms(), exactly as in SVEMnet.

data

A data frame containing the variables in the model.

criterion

Character. One of "AICc" (default), "AIC", or "BIC".

response

Optional character. When formula is a bigexp_spec, names the response column to use on the left-hand side. Defaults to the response stored in the spec.

unseen

How to treat factor levels not seen in the original bigexp_spec: "warn_na" (default) or "error".

Details

This benchmark accompanies the newer svem_forward extension. The core elastic-net ensemble workflow in Karl (2026), doi:10.1016/j.chemolab.2026.105660, is implemented by SVEMnet.

Starting from the intercept-only model, each step evaluates every remaining model term (all columns of a multi-column term, such as a factor's contrast block, enter together), refits by least squares, and adds the term whose augmented model minimizes the criterion. Selection stops when no candidate improves the criterion, when all remaining candidates are collinear with the current model, or when the criterion is undefined for every candidate (for example, AICc requires n - K - 1 > 0). Ties are broken by model-matrix term order.

The criteria are computed up to the additive Gaussian constant as

where K = p + 1 counts the fitted regression coefficients (including the intercept) plus the residual variance. Candidate models that are rank deficient given the current path, or whose criterion is undefined, are skipped.

Only Gaussian responses are supported. The fit is deterministic: no random weights are drawn and repeated calls return identical results. Because coef_matrix has one row, predict(..., se.fit = TRUE) and interval = TRUE are unavailable; use svem_forward for ensemble uncertainty with forward-selection base learners.

Value

An object of classes "svem_forward_ic" and "svem_model", structured like an SVEMnet fit so that predict(), coef(), and plot() work unchanged (coef_matrix has a single row, so bootstrap uncertainty options are unavailable). Additional components:

See Also

svem_forward, SVEMnet, glmnet_with_cv

Examples

set.seed(1)
n  <- 40
X1 <- rnorm(n); X2 <- rnorm(n); X3 <- rnorm(n)
y  <- 1 + 2 * X1 - 1.5 * X2 + rnorm(n, 0, 0.3)
dat <- data.frame(y, X1, X2, X3)

fit <- forward_aicc(y ~ (X1 + X2 + X3)^2, dat)
fit$selected_terms
fit$selection_path
preds <- predict(fit, dat)

## bigexp_spec front end, as in SVEMnet()
spec <- bigexp_terms(y ~ X1 + X2 + X3, dat, factorial_order = 2)
fit2 <- forward_aicc(spec, dat)


Fit a glmnet Model with Repeated Cross-Validation

Description

Repeated K-fold cross-validation over per-alpha lambda paths, with a combined 1-SE rule across repeats. Preserves fields expected by predict.svem_model() and internal prediction helpers. When relaxed = TRUE, the complete relaxed cross-validation surfaces are aggregated across repeats and alpha, lambda, and gamma are selected jointly. This mirrors cv.glmnet(relax = TRUE) rather than choosing lambda from the ordinary (gamma = 1) curve before choosing gamma.

Usage

glmnet_with_cv(
  formula,
  data,
  glmnet_alpha = c(0.5, 1),
  standardize = TRUE,
  nfolds = 10,
  repeats = 5,
  choose_rule = c("min", "1se"),
  seed = NULL,
  exclude = NULL,
  relaxed = FALSE,
  relax_gamma = NULL,
  family = c("gaussian", "binomial"),
  ...
)

Arguments

formula

Model formula.

data

Data frame containing the variables in the model.

glmnet_alpha

Numeric vector of Elastic Net mixing parameters (alphas) in [0,1]; default c(0.5, 1). When relaxed = TRUE, any alpha = 0 (ridge) is dropped with a warning.

standardize

Logical passed to glmnet() and cv.glmnet() (default TRUE).

nfolds

Requested number of CV folds (default 10). Internally constrained so that there are at least about 3 observations per fold and at least 5 folds when possible.

repeats

Number of independent CV repeats (default 5). Each repeat reuses the same folds across all alphas for paired comparisons.

choose_rule

Character; how to choose the tuning point within each alpha:

  • "min": tuning point minimizing the cross-validated criterion.

  • "1se": largest lambda (then largest gamma for relaxed fits) within 1 combined SE of the minimum, where the SE includes both within- and between-repeat variability.

Default is "min". In small-mixture simulations, the 1-SE rule tended to increase RMSE on held-out data, so "min" is used as the default here.

seed

Optional integer seed for reproducible fold IDs (and the ridge fallback, if used).

exclude

Optional vector or function for glmnet's exclude= argument. If a function, cv.glmnet() applies it inside each training fold (requires glmnet >= 4.1-2). The callback receives the original design columns, without any internal compatibility column, and the current fit's response and weights. Numerically constant columns remain excluded. When applying this mask or handling a one-column design, callback returns are checked against the original column indices (or an aligned logical vector); other callbacks retain glmnet's validation.

relaxed

Logical; if TRUE, call glmnet() and cv.glmnet() with relax = TRUE and optionally a gamma path (default FALSE). The repeated CV criterion is aggregated over the full lambda-by-gamma surface and both parameters are selected jointly. If cv.glmnet(relax = TRUE) fails for a particular repeat/alpha, the function retries that fit without relaxation; incomplete non-relaxed fallback surfaces are not mixed into a successful relaxed search. The number of fallbacks is recorded in meta$relax_cv_fallbacks.

relax_gamma

Optional numeric vector passed as gamma= to glmnet() and cv.glmnet() when relaxed = TRUE. If NULL, glmnet's internal default gamma grid is used.

family

Model family: either "gaussian" or "binomial", or the corresponding stats::gaussian() or stats::binomial() family objects with canonical links. For Gaussian, y must be numeric. For binomial, y must be 0/1 numeric, logical, or a factor with exactly 2 levels (the second level is treated as 1). Non-canonical links are not supported.

...

Additional arguments forwarded to both cv.glmnet() and glmnet(), for example: parallel, type.measure, intercept, lower.limits, upper.limits, penalty.factor, standardize.response, keep, and so on. Any user-supplied weights or offset are ignored with a warning: glmnet_with_cv() returns objects meant for side-by-side comparison with SVEMnet() fits, whose weighting is controlled by the SVEM scheme, and downstream prediction helpers do not carry observation weights or offsets. Glmnet algorithm-control values such as maxit, thresh, dfmax, and pmax may be supplied directly for compatibility or via control = list(...) with glmnet versions that support it; they are routed in a version-compatible way when possible. Argument names that the installed glmnet does not recognize are ignored with a warning (misspelling protection).

Details

This function is a convenience wrapper around glmnet() and cv.glmnet() that returns an object inheriting from "svem_model" for prediction and model-matrix schema tools. Candidate scoring and planning require the additional ensemble and sampling information stored by SVEMnet(). This wrapper is intended for:

It is not called internally by the SVEM bootstrap routines.

The basic workflow is:

  1. Generate one set of CV fold IDs per repeat. Reuse each repeat's folds across all values in glmnet_alpha.

  2. For each alpha, run repeats cv.glmnet() fits, align the lambda paths and, for relaxed fits, the gamma paths, and aggregate the CV surfaces.

  3. At each lambda-gamma point, compute a combined SE that accounts for both within-repeat and between-repeat variability.

  4. Apply choose_rule ("min" or "1se") to select lambda and gamma for that alpha, then choose the best alpha by comparing these per-alpha scores.

Special cases and fallbacks:

Family-specific behavior:

Design-matrix schema and contrasts:

The returned object inherits classes "svem_cv" and "svem_model" and is designed to be compatible with SVEMnet prediction and schema utilities. It is a standalone, standard glmnet CV workflow that does not use SVEM-style bootstrap weighting or ensembling.

Value

A list of class c("svem_cv","svem_model") with elements:

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

References

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

Examples

set.seed(123)
n <- 60; p <- 6
X <- matrix(rnorm(n * p), n, p)
beta <- c(1, -1, rep(0, p - 2))
y <- as.numeric(X %*% beta + rnorm(n))
df_ex <- data.frame(y = y, X)
colnames(df_ex) <- c("y", paste0("x", 1:p))

# Gaussian example, v1-like behavior: choose_rule = "min"
fit_min <- glmnet_with_cv(
  y ~ ., df_ex,
  glmnet_alpha = 1,
  nfolds = 5,
  repeats = 1,
  choose_rule = "min",
  seed = 42,
  family = "gaussian"
)

# Gaussian example, relaxed path with gamma search
fit_relax <- glmnet_with_cv(
  y ~ ., df_ex,
  glmnet_alpha = 1,
  nfolds = 5,
  repeats = 1,
  relaxed = TRUE,
  seed = 42,
  family = "gaussian"
)

# Binomial example (numeric 0/1 response)
set.seed(456)
n2 <- 80; p2 <- 5
X2 <- matrix(rnorm(n2 * p2), n2, p2)
beta2 <- c(1.0, -1.5, rep(0, p2 - 2))
linpred <- as.numeric(X2 %*% beta2)
prob <- plogis(linpred)
y_bin <- rbinom(n2, size = 1, prob = prob)
df_bin <- data.frame(y = y_bin, X2)
colnames(df_bin) <- c("y", paste0("x", 1:p2))

fit_bin <- glmnet_with_cv(
  y ~ ., df_bin,
  glmnet_alpha = 1,
  nfolds = 5,
  repeats = 1,
  seed = 99,
  family = "binomial"
)


Lipid formulation screening data

Description

An example dataset for modeling Potency, Size, and PDI as functions of formulation and process settings. Percent composition columns are stored as proportions in [0, 1] (for example, 4.19 percent is 0.0419). This table is intended for demonstration of SVEMnet multi-response modeling, desirability-based random-search optimization, and estimated probabilities that mean responses meet specification limits.

Usage

data(lipid_screen)

Format

A data frame with one row per experimental run and the following columns:

RunID

character. Optional identifier for each run.

PEG

numeric. Proportion (0-1).

Helper

numeric. Proportion (0-1).

Ionizable

numeric. Proportion (0-1).

Cholesterol

numeric. Proportion (0-1).

Ionizable_Lipid_Type

factor. Categorical identity of the ionizable lipid.

N_P_ratio

numeric. Molar or mass N:P ratio (unitless).

flow_rate

numeric. Process flow rate (arbitrary units).

Operator

factor. Categorical blocking factor.

Potency

numeric. Response (for example, normalized activity).

Size

numeric. Response (for example, particle size in nm).

PDI

numeric. Response (polydispersity index).

Notes

character. Optional free-text notes.

Details

The four composition columns PEG, Helper, Ionizable, and Cholesterol are stored as proportions in [0,1] and sum to exactly 1 in every row (so the mixture columns are exactly collinear), making them natural candidates for mixture constraints in optimization and design-space examples.

The vignette vignette("lipid-screen-follow-up", package = "SVEMnet") uses this dataset to nominate and confirm new recipes. See lipid_screen_simulate for the teaching simulator.

This dataset accompanies examples showing:

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

Source

Simulated screening table supplied by the package author.

References

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

Examples

data(lipid_screen)
str(lipid_screen)

## Not run: 
# Full production-scale modeling workflow. It is not run automatically
# because it fits wide ensembles, runs permutation WMTs, and scores a large
# constrained candidate table.
#2) Build a deterministic expansion using bigexp_terms()
#  Provide main effects only on the right-hand side; expansion width
#  is controlled via arguments. Here Operator is treated as a blocking
#  factor: additive only, no interactions or polynomial terms.
spec <- bigexp_terms(
  Potency ~ PEG + Helper + Ionizable + Cholesterol +
    Ionizable_Lipid_Type + N_P_ratio + flow_rate,
  data             = lipid_screen,
  factorial_order  = 3,   # up to 3-way interactions
  polynomial_order = 3,   # include up to cubic terms I(X^2), I(X^3)
  include_pc_2way  = TRUE,  # partial-cubic two-way terms Z:I(X^2)
  include_pc_3way  = FALSE,  # no partial-cubic three-way terms I(X^2):Z:W
  blocking         = "Operator",
  discrete_numeric = c("N_P_ratio","flow_rate")
)

# 3) Reuse the same locked expansion for other responses
form_pot <- bigexp_formula(spec, "Potency")
form_siz <- bigexp_formula(spec, "Size")
form_pdi <- bigexp_formula(spec, "PDI")

# 4) Fit SVEM models with the shared factor space and expansion
set.seed(1)
fit_pot <- SVEMnet(form_pot, lipid_screen)
fit_siz <- SVEMnet(form_siz, lipid_screen)
fit_pdi <- SVEMnet(form_pdi, lipid_screen)

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

# 6) Define multi-response goals and weights (DS desirabilities under the hood)
#    Maximize Potency (0.6), minimize Size (0.3), minimize PDI (0.1)
goals <- list(
  Potency = list(goal = "max", weight = 0.6),
  Size    = list(goal = "min", weight = 0.3),
  PDI     = list(goal = "min", weight = 0.1)
)

# Mixture constraints: components sum to total, with bounds
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
))

# 7) Optional: whole-model tests and WMT multipliers via svem_wmt_multi()
#    This wrapper runs svem_significance_test_parallel() for each response,
#    plots the distance distributions, and prints p-values and multipliers.

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, nCore = 1L),
  plot           = TRUE
)

# Inspect WMT p-values and multipliers (also printed by the wrapper)
wmt_out$p_values
wmt_out$multipliers

# 8) Optional: define process-mean specifications for a joint design space.
#    Potency at least 78, Size no more than 100, PDI less than 0.25.
#    Here we only specify the bounded side; the unbounded side defaults to
#    lower = -Inf or upper = Inf inside svem_score_random().
specs_ds <- list(
  Potency = list(lower = 78),
  Size    = list(upper = 100),
  PDI     = list(upper = 0.25)
)

# 9) Random-search scoring in one step via svem_score_random()
#    This draws a random candidate table, computes DS desirabilities,
#    a combined multi-response score, WMT-adjusted wmt_score (if `wmt`
#    is supplied), CI-based uncertainty, and (when `specs` is supplied)
#    appends mean-level design-space columns.
#
#    The `wmt` and `specs` arguments are optional:
#      - Omit `wmt` for no whole-model reweighting.
#      - Omit `specs` if you do not need design-space probabilities.

set.seed(3)
scored <- svem_score_random(
  objects         = objs,
  goals           = goals,
  data            = lipid_screen,  # scored and returned as original_data_scored
  n               = 25000,
  mixture_groups  = mix,
  level           = 0.95,
  combine         = "geom",
  numeric_sampler = "random",
  wmt             = wmt_out,   # optional: NULL for no WMT
  specs           = specs_ds,  # optional: NULL for no design-space columns
  verbose         = TRUE
)

# 10) Select optimal and exploration sets from the same scored table

# Optimal medoid candidates (maximizing DS score)
opt_sel <- svem_select_from_score_table(
  score_table = scored$score_table,
  target      = "score",      # score column is maximized
  direction   = "max",
  k           = 5,
  top_type    = "frac",
  top         = 0.1,
  label       = "round1_score_optimal"
)

# Optimal medoid candidates (maximizing WMT-adjusted wmt_score)
opt_sel_wmt <- svem_select_from_score_table(
  score_table = scored$score_table,
  target      = "wmt_score",  # wmt_score column is maximized
  direction   = "max",
  k           = 5,
  top_type    = "frac",
  top         = 0.1,
  label       = "round1_wmt_optimal"
)

# Exploration medoid candidates (highest uncertainty_measure)
explore_sel <- svem_select_from_score_table(
  score_table = scored$score_table,
  target      = "uncertainty_measure",  # uncertainty_measure column is maximized
  direction   = "max",
  k           = 5,
  top_type    = "frac",
  top         = 0.1,
  label       = "round1_explore"
)

# In-spec medoid candidates (highest joint mean-level assurance)
inspec_sel <- svem_select_from_score_table(
  score_table = scored$score_table,
  target      = "p_joint_mean",         # p_joint_mean column is maximized
  direction   = "max",
  k           = 5,
  top_type    = "frac",
  top         = 0.10,
  label       = "round1_inspec"
)

# Single best by score, including per-response CIs
opt_sel$best

# Single best by WMT-adjusted score, including per-response CIs
opt_sel_wmt$best

# Diverse high-score candidates (medoids)
head(opt_sel_wmt$candidates)

# Highest-uncertainty setting and its medoid candidates
explore_sel$best
head(explore_sel$candidates)

# Highest probability mean-in-spec setting and its medoid candidates
inspec_sel$best
head(inspec_sel$candidates)

# 11) Scored original data (predictions, desirabilities, score, wmt_score, uncertainty)
head(scored$original_data_scored)

# 12) Example: combine new candidate settings with the best existing run
#     and (optionally) export a CSV for the next experimental round.

# Best existing run from the original scored data (no new medoids; k = 0)
best_existing <- svem_select_from_score_table(
  score_table = scored$original_data_scored,
  target      = "score",
  direction   = "max",
  k           = 0,               # k <= 0 => only the best row, no medoids
  top_type    = "frac",
  top         = 1.0,
  label       = "round1_existing_best"
)

# 13) Prepare candidate export tables for the next experimental round.
#     svem_export_candidates_csv() accepts individual selection objects.
#     Calls are commented out so examples/tests do not write files.

# Export a design-style candidate table (factors + responses + predictions)
# out.df <- svem_export_candidates_csv(
#   opt_sel,
#   opt_sel_wmt,
#   explore_sel,
#   inspec_sel,
#   best_existing,
#   file      = "lipid_screen_round1_candidates.csv",
#   overwrite = TRUE
# )
# head(out.df)

# Export all columns including desirabilities, CI widths, and design-space columns
# out.df2 <- svem_export_candidates_csv(
#   opt_sel,
#   opt_sel_wmt,
#   explore_sel,
#   inspec_sel,
#   best_existing,
#   file      = "lipid_screen_round1_candidates_all.csv",
#   overwrite = TRUE
# )
# head(out.df2)

## End(Not run)

Simulate follow-up preparations for the lipid screening example

Description

Returns the underlying responses for recipes from the simulated 23-run lipid screen. Supply preparation IDs to draw new observations as well. Call this function after fixing the follow-up worklist. Only recorded observations belong in a refitted model.

Usage

lipid_screen_simulate(recipes, run_id = NULL, seed = 1L)

Arguments

recipes

Data frame containing PEG, Helper, Ionizable, Cholesterol as proportions summing to one, Ionizable_Lipid_Type as an H101, H102 or H103 label, and numeric N_P_ratio and flow_rate. Additional columns are ignored. Use the experiment's declared feasible region when choosing recipes.

run_id

Optional character vector of unique preparation IDs, one per row. A fresh preparation of the same recipe needs a distinct ID.

seed

Nonnegative integer identifying a set of simulated observations. For each endpoint the draw is keyed to the seed and preparation ID. Thus reordering or subsetting a worklist retains its measurements. Generating observations requires the suggested package digest and preserves the caller's RNG state.

Details

Potency and Size use the generating surfaces for the simulated JoVE example. Fresh observations have independent Normal errors with standard deviations 2 and 3 respectively and are rounded to whole units, with half units rounded away from zero. The constant correction inside the Size generating mean belongs to its archived surface. The archived observation formula adds Normal noise on the original Size scale. Operator does not change these generating surfaces. For PDI the original noise family is unknown. This demonstration uses an independent Beta(4, 16) draw at every recipe and operator (mean 0.20, SD 0.0873), recorded to the nearest hundredth. These choices describe a teaching dataset.

Value

A data frame in input row order with Potency_true, Size_true and PDI_true, the latent pre-recording means. With run_id supplied, it also contains Potency, Size and PDI for refitting, plus PDI_draw, the continuous draw before recording.

References

Karl AT, Essex S, Wisnowski J, Rushing H (2023). A workflow for lipid nanoparticle (LNP) formulation optimization using designed mixture-process experiments and Self-Validated Ensemble Models (SVEM). doi:10.3791/65200.

See Also

lipid_screen

Examples

data(lipid_screen)
lipid_screen_simulate(head(lipid_screen, 2))
if (requireNamespace("digest", quietly = TRUE)) {
  lipid_screen_simulate(head(lipid_screen, 2), c("prep-24", "prep-25"), seed = 72)
}

Plot Method for SVEM Binomial Models

Description

Diagnostics for svem_binomial fits from SVEMnet(..., family = "binomial"). Produces one of:

Usage

## S3 method for class 'svem_binomial'
plot(
  x,
  type = c("calibration", "roc", "pr"),
  bins = 10,
  jitter_width = 0.05,
  ...
)

Arguments

x

An object of class svem_binomial.

type

One of "calibration", "roc", or "pr" (default "calibration").

bins

Integer number of equal-frequency bins for calibration (default 10).

jitter_width

Vertical jitter amplitude for raw points in calibration (default 0.05). A positive width draws random offsets and advances the caller's random-number stream. Set it to zero to omit jitter.

...

Additional aesthetics passed to ggplot2::geom_line() or ggplot2::geom_point().

Details

For ROC/PR, simple one-class guards are used (returns a diagonal ROC and trivial PR). The function assumes binomial models store x$y_pred on the probability scale.

Value

A ggplot2 object.

Examples

  ## --- Binomial example: simulate, fit, and plot --------------------------
  set.seed(2025)
  n  <- 100
  x1 <- rnorm(n); x2 <- rnorm(n); x3 <- rnorm(n)
  eta    <- -0.4 + 1.1*x1 - 0.8*x2 + 0.5*x3
  p_true <- plogis(eta)
  y      <- rbinom(n, 1, p_true)
  dat_b  <- data.frame(y, x1, x2, x3)

  fit_b <- SVEMnet(
    y ~ x1 + x2 + x3 + I(x1^2) + (x1 + x2 + x3)^2,
    data          = dat_b,
    family        = "binomial",
    glmnet_alpha  = 1,
    nBoot         = 10,
    objective     = "auto",
    weight_scheme = "SVEM",
    relaxed       = FALSE
  )

  # Calibration / ROC / PR
  plot(fit_b, type = "calibration", bins = 12)
  plot(fit_b, type = "roc")
  plot(fit_b, type = "pr")


Plot Method for SVEM Models (Gaussian / Generic)

Description

Plots actual versus predicted values for an svem_model. This is the default plot for models fit with SVEMnet(..., family = "gaussian") and any other non-binomial models that share the svem_model class.

Usage

## S3 method for class 'svem_model'
plot(x, plot_debiased = FALSE, ...)

Arguments

x

An object of class svem_model.

plot_debiased

Logical; if TRUE, include debiased predictions (when available) as an additional series. Default FALSE.

...

Additional aesthetics passed to ggplot2::geom_point().

Details

Points show fitted values against observed responses; the dashed line is the 45-degree identity. If available and requested, debiased predictions are included as a second series.

This method assumes the fitted object stores the training response in $actual_y and in-sample predictions in $y_pred, as produced by SVEMnet() and glmnet_with_cv().

Value

A ggplot2 object.

Examples

  ## --- Gaussian example: simulate, fit, and plot --------------------------
  set.seed(2026)
  n  <- 60
  X1 <- rnorm(n); X2 <- rnorm(n); X3 <- rnorm(n)
  eps <- rnorm(n, sd = 0.4)
  y_g <- 1.2 + 2*X1 - 0.7*X2 + 0.3*X3 + 1.1*(X1*X2) + 0.8*(X1^2) + eps
  dat_g <- data.frame(y_g, X1, X2, X3)

  fit_g <- SVEMnet(
    y_g ~ (X1 + X2 + X3)^2 + I(X1^2) + I(X2^2),
    data          = dat_g,
    family        = "gaussian",
    glmnet_alpha  = 1,
    nBoot         = 10,
    objective     = "auto",
    weight_scheme = "SVEM",
    relaxed       = FALSE
  )

  # Actual vs predicted (with and without debias overlay)
  plot(fit_g, plot_debiased = FALSE)
  plot(fit_g, plot_debiased = TRUE)


Plot SVEM significance test results for one or more responses

Description

Plots the Mahalanobis-like distances for original and permuted data from one or more SVEM significance test results returned by svem_significance_test_parallel().

Usage

## S3 method for class 'svem_significance_test'
plot(x, ..., labels = NULL)

Arguments

x

An object of class svem_significance_test.

...

Optional additional svem_significance_test objects to include in the same plot.

labels

Optional character vector of labels for the responses. If not provided, the function uses inferred response names (from data_d$Response or x$response) and ensures uniqueness.

Details

If additional svem_significance_test objects are provided via ..., their distance tables ($data_d) are stacked and plotted together using a shared x-axis grouping of "Response / Source" and a fill aesthetic indicating "Original" vs "Permutation".

Value

A ggplot2 object showing the distributions of distances for original vs. permuted data, grouped by response.


Predict Method for SVEM Models (Gaussian and Binomial)

Description

Generate predictions from a fitted SVEM model (Gaussian or binomial), with optional bootstrap uncertainty and family-appropriate output scales.

Usage

## S3 method for class 'svem_model'
predict(
  object,
  newdata,
  type = c("response", "link", "class"),
  debias = FALSE,
  se.fit = FALSE,
  interval = FALSE,
  level = 0.95,
  ...,
  engine = c("svemnet", "gaupro", "extra_trees", "all")
)

Arguments

object

A fitted SVEM model (class svem_model; binomial models typically also inherit class svem_binomial). Created by SVEMnet().

newdata

A data frame of new predictor values.

type

(Binomial only) One of:

  • "response" (default): predicted probabilities in [0,1].

  • "link": linear predictor (log-odds).

  • "class": 0/1 class labels (threshold 0.5). Uncertainty summaries are not available for this type.

Ignored for Gaussian models.

debias

(Gaussian only) Logical; default FALSE. If TRUE, apply the linear calibration fit lm(y ~ y_pred) learned at training when available. Ignored (and internally set to FALSE) for binomial models.

se.fit

Logical; if TRUE, return bootstrap standard errors computed from member predictions (requires coef_matrix). Not available for type = "class". For Gaussian models, this forces use of bootstrap member predictions instead of aggregate coefficients.

interval

Logical; if TRUE, return percentile limits from member predictions (requires coef_matrix). Not available for type = "class". For Gaussian models, this forces use of bootstrap member predictions instead of aggregate coefficients.

level

Central probability mass for member-percentile intervals. Default 0.95.

...

Currently unused.

engine

Default "svemnet" uses the original SVEM prediction. "gaupro" or "extra_trees" returns one companion mean. For GauPro, se.fit = TRUE returns the conditional latent-mean standard deviation in $se.fit. "all" returns a data frame of means with one column per requested engine plus SVEMnet. A failed companion has missing means and its diagnostic in the engine_status attribute. Calibration, intervals, and binomial companions are unsupported.

Details

This method dispatches on object$family:

Uncertainty summaries (se.fit, interval) and all binomial predictions are based on per-bootstrap member predictions obtained from the coefficient matrix stored in object$coef_matrix. If coef_matrix is NULL, these options are not available (and binomial prediction will fail). For Gaussian models with se.fit = FALSE and interval = FALSE, predictions are computed directly from the aggregated coefficients.

Value

If se.fit = FALSE and interval = FALSE:

If se.fit and/or interval are TRUE (and type != "class"), a list with components:

Rows containing unseen or missing factor levels produce NA predictions (and NA SEs/intervals), with a warning.

Design-matrix reconstruction

The function rebuilds the design matrix for newdata to match the training design:

Rows containing unseen factor levels yield NA predictions (with a warning).

Aggregation and debiasing

For Gaussian SVEM models:

Point predictions

When se.fit = FALSE and interval = FALSE, predictions are computed from the aggregated coefficients saved at fit time (object$parms; or object$parms_debiased when debias = TRUE). This is algebraically equivalent to averaging member predictions when the coefficients were formed as bootstrap means.

Bootstrap-based summaries

When se.fit = TRUE and/or interval = TRUE, predictions are computed from per-bootstrap member predictions using object$coef_matrix. For debias = TRUE, the linear calibration is applied to member predictions before summarizing.

For binomial SVEM models, predictions are always aggregated from member predictions on the requested scale (probability or link) using coef_matrix; the stored coefficient averages (parms, parms_debiased) are retained for diagnostics but are not used in prediction. The debias argument is ignored and treated as FALSE for binomial models.

For Gaussian fits, if debias = TRUE and a calibration model lm(y ~ y_pred) was learned at training, predictions (and, when applicable, member predictions) are transformed by that calibration. This debiasing is never applied for binomial fits.

Uncertainty

When se.fit = TRUE, standard errors are computed as the row-wise standard deviations of member predictions on the requested scale. When interval = TRUE, percentile intervals are computed from member predictions on the requested scale, using the requested level. Both require a non-null coef_matrix. For type = "class" (binomial), uncertainty summaries are not available.

These summaries describe variation among bootstrap member predictions. They do not include new-observation noise, and the percentile limits alone do not establish calibrated confidence or prediction coverage.

See Also

SVEMnet

Examples

## ---- Gaussian example -------------------------------------------------
set.seed(1)
n  <- 60
X1 <- rnorm(n); X2 <- rnorm(n); X3 <- rnorm(n)
y  <- 1 + 0.8 * X1 - 0.6 * X2 + 0.2 * X3 + rnorm(n, 0, 0.4)
dat <- data.frame(y, X1, X2, X3)

fit_g <- SVEMnet(
  y ~ (X1 + X2 + X3)^2, dat,
  nBoot = 10, glmnet_alpha = 1,
  relaxed = FALSE, family = "gaussian"
)

## Aggregate-coefficient predictions (with and without debiasing)
p_g  <- predict(fit_g, dat)                # debias = FALSE (default)
p_gd <- predict(fit_g, dat, debias = TRUE) # apply calibration, if available

## Bootstrap-based uncertainty (requires coef_matrix)
out_g <- predict(
  fit_g, dat,
  debias   = TRUE,
  se.fit   = TRUE,
  interval = TRUE,
  level    = 0.90
)
str(out_g)

## ---- Binomial example ------------------------------------------------
set.seed(2)
n  <- 120
X1 <- rnorm(n); X2 <- rnorm(n); X3 <- rnorm(n)
eta <- -0.3 + 1.1 * X1 - 0.8 * X2 + 0.5 * X1 * X3
p   <- plogis(eta)
yb  <- rbinom(n, 1, p)
db  <- data.frame(yb = yb, X1 = X1, X2 = X2, X3 = X3)

fit_b <- SVEMnet(
  yb ~ (X1 + X2 + X3)^2, db,
  nBoot = 10, glmnet_alpha = 1,
  relaxed = FALSE, family = "binomial"
)

## Probabilities, link, and classes
p_resp <- predict(fit_b, db, type = "response")
p_link <- predict(fit_b, db, type = "link")
y_hat  <- predict(fit_b, db, type = "class")  # 0/1 labels (no SE or interval)

## Bootstrap-based uncertainty on the probability scale
out_b <- predict(
  fit_b, db,
  type     = "response",
  se.fit   = TRUE,
  interval = TRUE,
  level    = 0.90
)
str(out_b)


Predict a self-validated Gaussian spline

Description

Predict a self-validated Gaussian spline

Usage

## S3 method for class 'svem_spline'
predict(
  object,
  newdata,
  type = c("response", "members"),
  se.fit = FALSE,
  interval = FALSE,
  level = 0.95,
  debias = FALSE,
  ...
)

Arguments

object

A fitted svem_spline() object.

newdata

Physical recipes with the named surface variables and all declared blocking factors at known measured levels. Nominal study factors must also be at levels measured for this response. Keep mgcv installed to predict at new sites after reloading a serialized fit.

type

Use "response" for the ensemble mean in original units and "members" for the matrix of member predictions (rows are recipes).

se.fit

Return the mean and the sample standard deviation across fitted members. This spread is a diagnostic, not a calibrated standard error or prediction interval.

interval

Return empirical percentile bounds across fitted members. These describe member variation and have no predictive coverage guarantee.

level

Central fraction of member predictions for percentile bounds.

debias

Must be FALSE. Predictions use the original fitted means.

...

Reserved for extensions.

Value

A numeric vector, a recipe-by-member matrix, or a list with fit, optionally se.fit, and optionally lwr and upr.


Predict from glmnet_with_cv Fits (svem_cv Objects)

Description

Generate predictions from a fitted object returned by glmnet_with_cv() (class "svem_cv").

Usage

predict_cv(object, newdata, debias = FALSE, strict = FALSE, ...)

## S3 method for class 'svem_cv'
predict(object, newdata, debias = FALSE, strict = FALSE, ...)

Arguments

object

A fitted object returned by glmnet_with_cv() (class "svem_cv").

newdata

A data frame of new predictor values.

debias

Logical; if TRUE and a debiasing fit is available, apply it. Has an effect only for Gaussian models where debias_fit is present.

strict

Logical; if TRUE, enforce a strict column-name match between the aligned design for newdata and the training design (including the intercept position). Default FALSE.

...

Additional arguments. An engine selector applies only to models fitted with SVEMnet(); a CV fit has one prediction surface.

Details

The design matrix for newdata is rebuilt using the stored training terms (with a compact formula-evaluation environment), together with the saved factor xlevels and contrasts (cached in object$schema). Columns are then aligned back to the training design in a robust way:

For Gaussian fits (family = "gaussian"), the returned values are on the original response (identity-link) scale. For binomial fits (family = "binomial"), the returned values are probabilities in [0,1] (logit-link inverted via plogis()).

If debias = TRUE and a calibration model lm(y ~ y_pred) is present with a finite slope, predictions are adjusted via a + b * pred. Debiasing is only fitted and used for Gaussian models; for binomial models the debias argument has no effect.

predict_cv() is a small convenience wrapper that simply calls the underlying S3 method predict.svem_cv(), keeping a single code path for prediction from glmnet_with_cv() objects.

Value

A numeric vector of predictions on the response scale: numeric fitted values for Gaussian models; probabilities in [0,1] for binomial models. Rows with unseen factor/character levels return NA.

Examples

set.seed(1)
n <- 50; p <- 5
X <- matrix(rnorm(n * p), n, p)
y <- X[, 1] - 0.5 * X[, 2] + rnorm(n)
df_ex <- data.frame(y = as.numeric(y), X)
colnames(df_ex) <- c("y", paste0("x", 1:p))

fit <- glmnet_with_cv(
  y ~ ., df_ex,
  glmnet_alpha = 1,
  nfolds = 5,
  repeats = 1,
  seed = 9,
  family = "gaussian"
)

preds_raw <- predict_cv(fit, df_ex)
preds_db  <- predict_cv(fit, df_ex, debias = TRUE)
cor(preds_raw, df_ex$y)

# Binomial example (probability predictions on [0,1] scale)
set.seed(2)
n2 <- 80; p2 <- 4
X2 <- matrix(rnorm(n2 * p2), n2, p2)
eta2 <- X2[, 1] - 0.8 * X2[, 2]
pr2 <- plogis(eta2)
y2 <- rbinom(n2, size = 1, prob = pr2)
df_bin <- data.frame(y = y2, X2)
colnames(df_bin) <- c("y", paste0("x", 1:p2))

fit_bin <- glmnet_with_cv(
  y ~ ., df_bin,
  glmnet_alpha = 1,
  nfolds = 5,
  repeats = 1,
  seed = 11,
  family = "binomial"
)

prob_hat <- predict_cv(fit_bin, df_bin)
summary(prob_hat)


Print a formula that carries a bigexp_spec attribute

Description

Prints the formula itself and a one-line note, instead of the default formula print, which would dump the entire attached spec.

Usage

## S3 method for class 'bigexp_formula'
print(x, ...)

Arguments

x

A formula with an attached bigexp_spec (from bigexp_terms or bigexp_formula).

...

Passed to the default formula print method.

Value

x, invisibly.


Print method for bigexp_spec objects

Description

This print method shows a compact summary of the expansion settings and the predictors that are treated as continuous or categorical. It also reports any variables that were designated as blocking factors and therefore enter the model only additively (no interactions, no polynomials).

Usage

## S3 method for class 'bigexp_spec'
print(x, ...)

Arguments

x

A "bigexp_spec" object.

...

Unused.

Examples

set.seed(1)
df4 <- data.frame(
  y  = rnorm(10),
  X1 = rnorm(10),
  G  = factor(sample(c("A", "B"), 10, replace = TRUE))
)

spec4 <- bigexp_terms(
  y ~ X1 + G,
  data             = df4,
  factorial_order  = 2,
  polynomial_order = 2
)

print(spec4)

## Example with a blocking factor:
set.seed(2)
df_block2 <- data.frame(
  y           = rnorm(12),
  X1          = rnorm(12),
  G           = factor(sample(c("A", "B"), 12, replace = TRUE)),
  Operator    = factor(sample(letters[1:3], 12, replace = TRUE)),
  AmbientTemp = rnorm(12, mean = 22, sd = 1.5)
)

spec_block2 <- bigexp_terms(
  y ~ X1 + G,
  data             = df_block2,
  factorial_order  = 2,
  polynomial_order = 3,
  blocking         = c("Operator", "AmbientTemp")
)

print(spec_block2)


Print Method for SVEM Significance Test

Description

Prints the median p-value from an object of class svem_significance_test.

Usage

## S3 method for class 'svem_significance_test'
print(x, ...)

Arguments

x

An object of class svem_significance_test.

...

Additional arguments (unused).


Print method for svem_thompson_batch objects

Description

Print method for svem_thompson_batch objects

Usage

## S3 method for class 'svem_thompson_batch'
print(x, ...)

Arguments

x

An object of class "svem_thompson_batch".

...

Unused.

Value

x, invisibly.


Specify a Gaussian smooth surface on a declared recipe domain

Description

Construct the surface basis once, before fitting any responses. Each distinct numeric site in the reference grid receives equal weight in the shared numeric basis centering and scaling. Nominal coverage does not reweight it. The grid need not contain a response or block column.

Usage

spline_terms(
  formula,
  domain_grid,
  blocking = NULL,
  mixture_groups = NULL,
  bounds = NULL,
  rank = NULL,
  null_weight = 0.05,
  level_modes = 3L,
  level_penalty_ratio = 1,
  max_columns = 200L,
  interaction_order = 3L,
  marginal_modes = 4L,
  marginal_weight = 0.25
)

Arguments

formula

A two-sided formula with one response name and raw study predictor names joined by +, for example y ~ lipid + ratio + type. With y ~ ., include only intended predictors, an optional response and declared blocks in domain_grid. Numerical coordinates enter a joint surface; factor, character and logical predictors are nominal. Ordered factors are also treated as nominal; use explicit numeric values to model an ordered physical scale.

domain_grid

Feasible reference recipes defined independently of response values. Exact duplicate recipes count once. Set this grid before observing outcomes and retain the saved specification for later fits. The numerical surface uses distinct numerical sites; adding another nominal level does not change its numeric basis at a fixed surface rank. A smaller reference grid can be used to build a basis for a much larger prediction pool. The constructor caps distinct numeric reference sites at 65,536; use a smaller response-free reference grid when the scoring pool is larger.

blocking

Optional nuisance-factor names. Known measured levels contribute ordinary penalized additive shifts, without interactions with study factors. Numeric block IDs are matched by their exact values. Unreplicated numeric IDs warn. Continuous blocking trends are unsupported.

mixture_groups

Fixed-sum mixture groups declared as in bigexp_terms(). Include every component. Multiple disjoint groups, legal zeros and fixed components are supported. Each varying group is mapped to its independent closure coordinates. The reference grid must span these coordinates.

bounds

Optional two-row numeric matrix of lower and upper legal limits, with columns named for numeric study predictors. Defaults to the reference ranges. A coordinate fixed on the reference grid remains fixed in this saved model. Different legal limits do not change coordinate scaling or the curvature penalty. Prediction beyond reference coverage is extrapolation. Eligibility uses the package's absolute physical tolerance of 1e-8, scaled by max(1, abs(total)) for each mixture total. Additional preparation constraints belong in the candidate pool.

rank

Advanced shared numeric-surface rank including its intercept. Default NULL retains rank 26 for the original one-to-three-coordinate surface. General surfaces use a quadratic-sized rank target, bounded by distinct reference sites and max_columns, while reserving nominal main effects. Marginal curvature columns are additional and share the overall column budget. This target does not imply an exact quadratic basis. With too few sites or a limited budget, the numeric surface can reduce to linear directions.

null_weight

Positive penalty on linear directions and nominal main effects relative to the smallest positive curvature penalty; default 0.05.

level_modes

Advanced maximum number of smooth curvature modes for each nominal level's deviation from the common surface; default 3.

level_penalty_ratio

Positive multiplier for nominal-level deviation penalties, including level-specific numeric slopes; default 1.

max_columns

Advanced budget for non-block model columns; default 200. Main effects are retained. Complete interaction families and curvature ranks are reduced when necessary and reported in basis_notes. Increase this explicit resource budget when more columns are needed. Memory and fitting cost increase with the column count. There is no fixed three-coordinate or twelve-coordinate restriction.

interaction_order

Advanced maximum order, 1 to 3, of nominal cell effects; default 3. Third-order structure can also include two nominal factors times a numeric linear direction when the budget permits.

marginal_modes

Advanced maximum curvature modes per physical numeric factor in general surfaces with at least four independent coordinates; default 4. These one-factor smooths complement the joint surface. Zero disables them. Whole ranks are reduced uniformly by max_columns.

marginal_weight

Positive multiplier for marginal curvature penalties; default 0.25 relative to the first shared joint-curvature penalty.

Details

The original numeric surface is retained exactly for one to three independent coordinates with at most one mixture and no fixed component. General specifications store version 2. Their shared numeric surface is thin-plate for one to three coordinates and a low-rank Duchon spline in higher dimensions, with a linear penalty null space. Higher-dimensional surfaces also include modest one-factor curvature to avoid spending every curvature direction on joint geometry. Its positive penalty allows overlap with joint modes. This is a practical basis choice, not a universal optimum. Nominal factors add penalized level shifts, low-rank deviations from the common surface and cell effects. These deviations shrink toward the common surface rather than fitting an independent high-dimensional surface at every level. Fixed coordinates use explicit point bounds or a scale-relative floating-point tolerance of eight machine epsilons. This removes rounding noise without using an absolute width threshold on a narrow varying factor.

With no varying numeric coordinate, the general model reduces to penalized categorical effects, or an intercept if every factor is fixed; no numeric spline is claimed in that limit. Such limits need no mgcv. Interaction effects specific to unmeasured combinations of otherwise observed levels are not identified; predictions there use shared and lower-order shrinkage assumptions. New or unmeasured nominal study levels have no prediction. Study factors can interact; declared blocks remain additive. The selected member means and their variation are not calibrated prediction intervals. Factor support does not imply that a sparse design identifies all interactions or narrow response peaks.

The optional mgcv package supplies the numeric spline construction. The stored basis, penalties and charts are reused in prediction and after serialization. General specifications retain their basis controls, legality tolerance and construction-package version. Numerical prediction at new sites requires mgcv. Non-default general basis controls can select version 2 even on a low-dimensional numeric problem; saved version 1 fits remain unchanged. The implementation was developed with assistance from Astra.

Value

A serializable svem_spline_spec containing physical predictors, bounds, reference grid, coordinate transforms, basis and diagonal penalty. General specifications additionally record nominal levels, included interactions, the actual column count and any automatic budget reductions. Pass the specification to svem_spline().

References

Wood, S. N. (2003). Thin plate regression splines. Journal of the Royal Statistical Society B, 65, 95–114. doi:10.1111/1467-9868.00374. Duchon, J. (1977). Splines minimizing rotation-invariant semi-norms in Sobolev spaces. In Construction Theory of Functions of Several Variables, 85–100. See also the mgcv documentation for https://stat.ethz.ch/R-manual/R-devel/library/mgcv/html/smooth.construct.ds.smooth.spec.html.

Examples

if (requireNamespace("mgcv", quietly = TRUE)) {
  recipes <- expand.grid(u = seq(0, 1, length.out = 7),
                         v = seq(0, 1, length.out = 7))
  spec <- spline_terms(y ~ u + v, recipes)
  measured <- recipes[seq(1, 49, by = 2), ]
  measured$y <- with(measured, u * (1 - v))
  fit <- svem_spline(spec, measured, nBoot = 3)
  predict(fit, recipes[1:2, ])
}

Export SVEM candidate sets to CSV

Description

A plan from svem_plan_runs, or a scored result from svem_score_random with a budgeted $worklist, or that worklist data frame itself, exports the selected preparations in slot order. This path preserves recipe_id, slot, role, nominated_by, and all other columns as supplied. It does not relabel exploration rows as best candidates. A direct worklist must have all four identity columns, unique nonmissing recipe IDs, positive unique integer-valued slots, and nonempty roles and endorsements. A short worklist exports only the available preparations; an empty worklist exports column headers. No rows are invented to fill a budget shortfall.

Alternatively, given one or more selection objects returned by svem_select_from_score_table, concatenate their $best rows and $candidates and export a CSV suitable for planning new experimental runs.

On the legacy selection-object path, each row is tagged with:

The function does not modify any response or prediction columns (for example, Potency, Potency_pred); it simply harmonizes columns across inputs (adding NA-filled columns where necessary), concatenates rows, and reorders a few metadata columns for readability.

On that legacy path, columns named candidate_type, selection_label, or Notes_from_SVEMnet that are present in the final data frame are moved to the leftmost positions in that order.

Usage

svem_export_candidates_csv(
  ...,
  file = NULL,
  overwrite = FALSE,
  write_file = TRUE,
  planned_date = NULL,
  preparation_prefix = NULL
)

Arguments

...

One or more objects returned by svem_select_from_score_table. You may also pass a single list of such objects. Alternatively, pass one run plan, budgeted scored result or its recognizable worklist data frame. These worklist forms cannot be combined with other arguments in .... A score result without a worklist is not exported: request budget when scoring or select rows with svem_select_from_score_table() first.

file

Character scalar; path to the CSV file to be written. Required only when write_file = TRUE.

overwrite

Logical; if FALSE (default) and file already exists, an error is thrown. If TRUE, any existing file at file is overwritten. Only used when write_file = TRUE.

write_file

Logical; if TRUE (default), write the combined table to file as CSV and print the full path. If FALSE, no file is written and file may be NULL; the concatenated data.frame is still returned (invisibly).

planned_date

Optional planned preparation date: one Date or an ISO date string such as "2026-11-02". Adds a planned_date column. This is scheduling metadata, not the actual execution date, a predictor, or an automatically added blocking factor.

preparation_prefix

Optional nonempty string identifying this batch, for example "studyA-20261102-round1". Adds preparation_id values formed from the prefix and -001, -002, and so on, in exported row order. Use a distinct prefix for every physical batch, including batches on the same date. IDs are unique within this export; the function does not maintain a global laboratory ID registry. Re-exporting the same plan with the same prefix reproduces its labels. Exporting an unlabeled subset renumbers its rows; to preserve previously assigned IDs, filter the returned labeled table and omit these labeling options on export. For a laboratory's own IDs, add a preparation_id column to the worklist instead. Existing columns are preserved; supplying either option when its output column already exists errors rather than overwriting it. With both options omitted, export columns and labels are unchanged, including legacy exports.

Value

Invisibly, the data.frame that was written to CSV (or would be written, when write_file = FALSE).

Examples

# Fabricated scored candidates keep this example independent of model fitting.
scored <- data.frame(
  x1 = c(0.1, 0.4, 0.7, 0.9),
  x2 = factor(c("A", "B", "A", "B")),
  score = c(0.55, 0.82, 0.91, 0.73),
  uncertainty_measure = c(0.15, 0.31, 0.20, 0.44)
)

optimal <- svem_select_from_score_table(
  scored, target = "score", direction = "max", k = 0,
  label = "round1_optimal"
)
exploratory <- svem_select_from_score_table(
  scored, target = "uncertainty_measure", direction = "max", k = 0,
  label = "round1_exploratory"
)

# Export to disk when desired:
# svem_export_candidates_csv(
#   optimal, exploratory, file = "round1_candidates.csv",
#   overwrite  = FALSE,
#   write_file = TRUE
# )

# Inspect the combined table without writing a file.
cand_tbl <- svem_export_candidates_csv(optimal, exploratory, write_file = FALSE)
head(cand_tbl)
# Optional scheduling labels, separate from the formulation's recipe_id:
labelled <- svem_export_candidates_csv(optimal, exploratory, write_file = FALSE,
  planned_date = as.Date("2026-11-02"),
  preparation_prefix = "studyA-20261102-round1")
labelled[c("planned_date", "preparation_id", "selection_label")]


Fit several responses over one shared factor specification

Description

Fit one independent SVEM model per response using the same specification from bigexp_terms. The returned ordinary named list works directly with svem_random_table_multi and svem_score_random when using the default SVEM method. Optional spline lists are passed through the scorer's separate spline argument.

Usage

svem_fit_responses(
  spec,
  data,
  responses,
  ...,
  method = c("svemnet", "spline"),
  domain_grid = NULL
)

Arguments

spec

A bigexp_spec from bigexp_terms, or a saved spline_terms() specification when method = "spline".

data

A data frame containing the shared predictors and responses.

responses

Character vector of unique response column names, in the order to fit. Names on this vector are ignored; the returned list is named by the response column names themselves.

...

Arguments for the selected fitting method. The default passes them unchanged to SVEMnet, for example nBoot, objective, or companions. The spline method accepts nBoot, lambda and store_member_weights; with a bigexp_spec it also accepts the advanced basis controls documented in spline_terms(). Use responses, rather than a separate response argument.

method

"svemnet" (default) keeps the original sequential fitting path. "spline" fits optional Gaussian smooth surfaces, including nominal study factors and multiple mixture groups, with the same raw-factor specification. Spline fits can be passed as the separate spline list to svem_plan_runs() or svem_score_random().

domain_grid

Explicit immutable response-free feasible recipe grid required for method = "spline" with a bigexp specification. A saved spline_terms() specification already contains its domain.

Details

Fits run sequentially in the requested response order. With the default method, this helper neither sets nor restores the random seed: it uses the same random draws as equivalent sequential calls to SVEMnet(spec, data, response = ...). Fitting choices in ... are passed to the selected fitting method. For a single response already named in the specification, SVEMnet(spec, data) is sufficient.

Value

An ordinary named list in response order: svem_model entries for the default method, or svem_spline entries for the optional spline method. No joint-response fit or additional list wrapper class is introduced.

Examples

dat <- data.frame(x = seq(-1, 1, length.out = 20), z = rep(c(0, 1), 10))
dat$y1 <- 2 + dat$x + 0.1 * sin(seq_len(nrow(dat)))
dat$y2 <- 3 - dat$z + 0.1 * cos(seq_len(nrow(dat)))
spec <- bigexp_terms(y1 ~ x + z, dat, factorial_order = 1,
                    polynomial_order = 1, report = FALSE)
set.seed(12)
fits <- svem_fit_responses(spec, dat, c("y1", "y2"), nBoot = 3,
                           glmnet_alpha = 1, relaxed = FALSE)
predict(fits$y1, dat[1:2, ])
# One response needs no list helper:
one_fit <- SVEMnet(spec, dat, nBoot = 3, glmnet_alpha = 1, relaxed = FALSE)


Fit an SVEM Model with Forward-Selection Base Learners

Description

Fit a Self-Validated Ensemble Model in which each bootstrap replicate's base learner is greedy forward selection on training-weighted least squares. By default, each path point's training-fitted Gaussian mean and variance are evaluated on the validation-weighted copy. The first path point attaining the lowest admissible score wins that replicate. Winning coefficient vectors are averaged across bootstrap replicates exactly as in SVEMnet().

Usage

svem_forward(
  formula,
  data,
  nBoot = 200,
  objective = c("auto", "gaussian_likelihood", "wAIC", "wBIC", "wSSE"),
  weight_scheme = c("SVEM", "FRW_plain", "Identity"),
  response = NULL,
  unseen = c("warn_na", "error"),
  store_member_weights = FALSE
)

Arguments

formula

A model formula or a bigexp_spec created by bigexp_terms(), exactly as in SVEMnet.

data

A data frame containing the variables in the model.

nBoot

Integer. Number of bootstrap replicates (default 200).

objective

Character. The default and "auto" select "gaussian_likelihood". Use objective = "wAIC" to reproduce the former forward default. Explicit "wBIC" and "wSSE" are also available. The information criteria count fitted coefficients including the intercept. This likelihood option is specific to Gaussian forward selection.

weight_scheme

Character. "SVEM" (default), "FRW_plain", or "Identity", as in SVEMnet. "Identity" with nBoot = 1 yields a single deterministic forward-selection fit tuned by the chosen objective on the training data. The support guard does not guarantee positive residual degrees of freedom. In particular, "Identity" gives n_{eff,adm} = n, so the Kish cap admits saturated path points with k = n. A forward path can then interpolate the training data and be selected by the log-scale criterion. Such members are flagged in member_diagnostics. By comparison, forward_aicc() skips models with nonpositive residual degrees of freedom for AIC/BIC; its AICc criterion requires at least three residual degrees of freedom.

response

Optional character. When formula is a bigexp_spec, names the response column to use on the left-hand side.

unseen

How to treat factor levels not seen in the original bigexp_spec: "warn_na" (default) or "error".

store_member_weights

Logical; default FALSE. If TRUE, retain the exact per-member uniforms and mean-one training and validation weights in member_weights. This can materially increase object size.

Details

This is an experimental alternative to the elastic-net base learners in the package workflow described by Karl (2026), doi:10.1016/j.chemolab.2026.105660. The package paper's results do not establish the performance of this implementation.

Per replicate and row, a shared uniform draw is converted to anti-correlated train/validation weights (weight_scheme = "SVEM"), the forward path is grown on rows scaled by sqrt(w_train), greedy steps add the whole model term (multi-column factor blocks move as a unit) that most reduces training RSS, and each path point is refit and scored by validation-weighted residuals on the original (unweighted) rows. Gaussian validation likelihood, wAIC and wBIC admit only models whose number of slope coefficients is strictly below the bounded validation Kish effective size. All columns of a term count toward the cap. Growth stops once the current support reaches the ceiling. The wSSE criterion uses the uncapped path. Replicates with no admissible finite path point fall back to a weighted intercept-only model, as in SVEMnet().

For Gaussian validation likelihood let t and v be the training- and validation-weighted mean squared residuals of the same training fit. Each uses its own sum of weights. Minimize \log(t) + v/t over all admissible points, including the intercept. A fit that understates residual variation can receive a poor validation log score. The score omits the common Gaussian constant and factor of one half.

For this objective the response is centered at its midrange and divided by the largest absolute centered value. The training variance is floored at .Machine$double.eps^2 in these units in both parts of the score. A constant response has zero centered residuals and selects its exact intercept. In member_diagnostics, objective_value uses conditioned units. Away from the floor, adding twice the log of the response scale gives the score in original units. Coefficients, fitted means, predictions and validation SSE are returned on the original scale.

Only Gaussian responses are supported. There is no debias argument at fit time: as in SVEMnet(), a linear calibration lm(y ~ y_pred) is stored when nBoot >= 10 and the fitted values vary, and predict(..., debias = TRUE) applies it.

Unlike the elastic net, forward selection contains no shrinkage, so exactly collinear candidates (for example the third mixture component after the first two have entered) are skipped as rank deficient rather than entering with shrunk coefficients.

Value

An object of classes "svem_forward" and "svem_model", structured like an SVEMnet fit: parms, parms_debiased, debias_fit, coef_matrix (rows = bootstrap members), diagnostics, schema, sampling_schema, and so on, so that predict() (including se.fit/interval), coef(), plot(), svem_nonzero(), svem_random_table_multi(), and svem_score_random() work unchanged. Forward-selection-specific components:

See Also

forward_aicc for the deterministic single-model version, SVEMnet for the elastic-net ensemble.

Examples

set.seed(42)
n  <- 30
X1 <- rnorm(n); X2 <- rnorm(n); X3 <- rnorm(n)
y  <- 1 + 2 * X1 - 1.5 * X2 + 0.8 * X1 * X2 + rnorm(n, 0, 0.5)
dat <- data.frame(y, X1, X2, X3)

## Validate each training-fitted Gaussian mean and variance on the weighted copy.
fit <- svem_forward(y ~ (X1 + X2 + X3)^2, dat, nBoot = 8)
fit$member_diagnostics[1:3, c("objective", "objective_value")]
preds <- predict(fit, dat)
## To reproduce the former forward default, supply objective = "wAIC".

## bigexp_spec front end, as in SVEMnet()
spec <- bigexp_terms(y ~ X1 + X2 + X3, dat, factorial_order = 2)
fit2 <- svem_forward(spec, dat, nBoot = 8)


Experimental FRW Infinitesimal-Jackknife Variance Diagnostic

Description

Estimate the sampling-variance component of the raw SVEM ensemble-mean prediction from covariances between stored fractional-random training weights and member predictions. This adapts the infinitesimal-jackknife calculations for bootstrap-smoothed estimators in Efron (2014) and Wager, Hastie, and Efron (2014) by replacing ordinary-bootstrap counts with the mean-one FRW training weights used by SVEM.

Usage

svem_ij_variance(object, newdata, finite_B = TRUE)

Arguments

object

A Gaussian svem_model fitted with store_member_weights = TRUE.

newdata

A data frame of predictor values.

finite_B

Logical; default TRUE. Subtract the ordinary- bootstrap-inspired finite-ensemble Monte Carlo correction n B^{-2}\sum_b\{T_b(x)-\bar T(x)\}^2. The untruncated raw estimate and the correction are always returned.

Details

For member prediction T_b(x) and stored mean-one training weight W_{bi}, the raw diagnostic is

V_{IJ}(x)=\sum_i \operatorname{cov}_b\{W_{bi},T_b(x)\}^2.

When finite_B = TRUE, the finite-ensemble correction is subtracted and negative corrected values are truncated at zero for the reported variance and standard error.

This is an experimental diagnostic, not a calibrated confidence interval. The cited IJ results are for ordinary bootstrap counts; the FRW substitution used here is an adaptation. In particular, this function does not correct prediction bias, selection nonregularity, or observation noise, and it must not be used as a prediction interval for a new observation.

Value

A data frame with one row per row of newdata and columns fit, variance, se, variance_raw, finite_B_correction, truncated_at_zero, and calibrated (always FALSE). Attributes record the number of observations, ensemble members, weighting scheme, method, and caution.

References

Efron, B. (2014). Estimation and Accuracy After Model Selection. Journal of the American Statistical Association, 109, 991–1007.

Wager, S., Hastie, T., and Efron, B. (2014). Confidence Intervals for Random Forests: The Jackknife and the Infinitesimal Jackknife. Journal of Machine Learning Research, 15, 1625–1651.

See Also

SVEMnet, svem_forward


Coefficient Nonzero Percentages (SVEM)

Description

Summarizes variable-selection stability across SVEM bootstrap refits by computing the percentage of bootstrap iterations in which each coefficient (excluding the intercept) is nonzero, using a small tolerance. Optionally produces a quick ggplot2 summary and/or prints a compact table.

Usage

svem_nonzero(object, tol = 1e-07, plot = TRUE, print_table = TRUE, ...)

Arguments

object

An object of class svem_model with a non-empty $coef_matrix component. svem_nonzero() is not defined for svem_cv objects.

tol

Numeric tolerance for "nonzero". Coefficients with |beta| > tol are counted as nonzero. Default is 1e-7. This is an absolute threshold in the raw coefficient units, so reports using a positive tolerance depend on predictor and response units. Use tol = 0 for exact selection frequencies, matching the internal support counts used for model selection.

plot

Logical; if TRUE, draws a quick ggplot2 summary plot of the nonzero percentages (default TRUE).

print_table

Logical; if TRUE, prints a compact table of nonzero percentages to the console (default TRUE).

...

Unused; included for future extension.

Details

This function expects object$coef_matrix to contain the per-bootstrap coefficients (including an intercept column), which SVEMnet always stores in its fitted objects. Rows correspond to bootstrap fits; columns correspond to coefficients.

Internally, svem_nonzero():

The plot is a simple line + point chart with labels, ordered by decreasing nonzero percentage. It is intended as a quick diagnostic; for publication graphics, you may want to customize the output data frame with your own plotting code.

Value

Invisibly returns a data frame with columns:

If no non-intercept coefficients are found (for example, if only the intercept is present), an empty data frame is returned and a message is issued.

See Also

coef.svem_model for ensemble-averaged (optionally debiased) coefficients.

Examples

  set.seed(10)
  n  <- 60
  x1 <- rnorm(n)
  x2 <- rnorm(n)
  x3 <- rnorm(n)
  eps <- rnorm(n, sd = 0.4)
  y   <- 0.7 + 1.5*x1 - 0.8*x2 + 0.05*x3 + eps
  dat <- data.frame(y, x1, x2, x3)

  fit <- SVEMnet(y ~ x1 + x2 + x3, data = dat,
                 nBoot = 10, glmnet_alpha = 1, relaxed = FALSE)

  nz <- svem_nonzero(fit, tol = 1e-7, plot = FALSE, print_table = TRUE)
  head(nz)


Plan the default formulation follow-up workflow

Description

Fit complementary surfaces, carry forward the best observed preparation, and allocate a fixed preparation budget. This is the package's default formulation workflow, composed from svem_fit_responses(), svem_forward() and svem_score_random(). Existing individual fitting/scoring calls retain their behavior. Files are written only by an explicit call to svem_export_candidates_csv().

Usage

svem_plan_runs(
  spec,
  data,
  goals,
  candidates,
  budget = 5L,
  fits = NULL,
  forward = NULL,
  fit_control = list(),
  forward_control = list(),
  score_control = list(),
  incumbent = TRUE,
  reference = NULL,
  priority = c("incumbent", "svemnet", "gaupro", "extra_trees", "forward"),
  exploration_fraction = 0.1,
  incumbent_summary = c("best", "mean"),
  benchmark = NULL,
  spline = NULL,
  spline_control = list(),
  n_optima = NULL,
  n_explore = NULL
)

## S3 method for class 'svem_run_plan'
print(x, ...)

## S3 method for class 'svem_run_plan'
plot(x, type = c("tradeoff", "responses"), ...)

Arguments

spec

Shared bigexp_terms() specification.

data

Measured data, one row per independent preparation. Summarize technical readings within preparation before calling. Use response columns on the scale on which the scientific goals are defined.

goals

Named response goals as in svem_score_random(). Their names are response columns in data. Fixed scientific desirability anchors are recommended so decisions across rounds have the same interpretation.

candidates

Explicit feasible recipe pool, including the planned operator/day values and any recipe_id labels. Feasible historical recipes may be appended for the incumbent repeat, without rounding their settings.

budget

Total physical preparation budget, including required repeats. With both n_optima and n_explore supplied, omit budget to use their sum plus the distinct required reference repeats. With only one count, supply a budget to allocate the remaining capacity. With neither count, the default budget is five preparations.

fits

Optional ordinary named list of already fitted primary SVEM models with any requested companions. Otherwise fitted sequentially.

forward

Optional named list of forward fits. Otherwise fitted for each response, using the Gaussian validation-likelihood default.

fit_control

Named arguments passed to svem_fit_responses(). Defaults request companions = c("gaupro", "extra_trees") with companion_blocks = "interacting". Optional engines need the suggested packages; an unavailable fit remains visible in the scoring diagnostics. Supply companions = FALSE for a core-only workflow. All primary SVEM algorithm defaults, including relaxation and penalized blocks, are retained.

forward_control

Named additional arguments passed to each svem_forward() fit. Explicit choices such as objective = "wAIC" are preserved. nBoot controls its members independently of fit_control.

score_control

Named additional arguments to svem_score_random(), such as combine, increments and mixture_groups. WMT and specification probabilities are not computed by default.

incumbent

TRUE carries the best observed preparation forward; FALSE omits the repeat. An integer chooses a historical row explicitly.

reference

Historical block context for automatic incumbent selection; see svem_score_random(). Defaults to the planned block levels. Raw observations from different days/operators are not compared automatically.

priority

Ordered roles, default incumbent, elastic net, GauPro, Extra Trees, then forward. Omit a role or reorder explicitly to customize. Listing "spline" requests that fit and conflicts with spline = FALSE. With spline = TRUE, include "spline" in any custom priority.

exploration_fraction

Top fraction of eligible SVEM uncertainty scores supplied to medoid selection. Default 0.1; 0.25 is a wider sensitivity.

incumbent_summary

"best" (default) retains the best observed individual preparation. "mean" first averages repeated independent preparations of the same recipe within the selected reference context. Technical readings must already be summarized within preparation.

benchmark

Optional fixed benchmark to carry forward in addition to the incumbent: one historical row number, or a named list with one unique preparation identifier, for example list(RunID = "02SEP22-2"). The identifier form stays meaningful when data rows are reordered or added. A requested benchmark is mandatory and consumes a preparation slot. If it is the same physical recipe as the incumbent, they share a single repeat.

spline

Optional named response list from svem_spline(), TRUE to fit the optional smoother, or FALSE to disable it. Default NULL preserves the ordinary five-role workflow. For a newly fitted plan with explicit counts and default priority, spline follows forward. The planner reports cases outside the declared surface domain or its resource budget. Explicit spline = TRUE, a spline priority, or n_optima = 5 enables the general surface for numeric, nominal and mixture/process factors. Unmeasured block levels have no spline prediction. A pool fixing a numeric coordinate that varied in the data can make the spline unavailable. For pools above 65,536 distinct numeric sites, supply saved spline fits built on a smaller response-free reference grid. Smaller count-only calls retain the former automatic numeric-spline eligibility. When the planner fits the spline, candidates supplies its response-free reference grid. This grid determines the basis and can limit its available rank. The declared physical bounds separately determine recipe eligibility. Choose the reference grid before observing outcomes and keep it fixed across refits. To score a different pool with the same basis, pass saved fits from svem_fit_responses(..., method = "spline", domain_grid = reference_grid). Declare mixture groups in the shared bigexp_terms() specification when asking the planner to fit the spline. Numeric block values denote categorical levels. A declared continuous companion block makes the spline unavailable.

spline_control

Named spline fitting controls (nBoot, lambda, store_member_weights) and the advanced basis controls documented in spline_terms(), including rank, null_weight, level_modes, level_penalty_ratio, max_columns, interaction_order, marginal_modes and marginal_weight. A nonempty control list requires spline fitting to be enabled.

n_optima

Optional count of distinct new model-nominated preparations, excluding incumbent and benchmark repeats. If five models nominate five distinct new recipes, they and one incumbent make six preparations. Duplicate nominations share one preparation and the next model is considered. With n_optima = NULL, available models fill capacity left after repeats and reserved exploration. An unfilled automatic model place is capacity rather than a requested-model shortfall.

n_explore

Optional exact exploration-medoid count, including zero. Default NULL fills remaining capacity. This count is distinct from exploration_fraction, which controls the uncertainty shortlist. Requested counts and required repeats must fit the total budget; unused capacity and genuine model/exploration shortages remain inspectable.

x

A svem_run_plan.

...

Additional arguments (reserved).

type

"tradeoff" plots SVEM desirability and ensemble spread for the pool and selected runs; "responses" shows predicted endpoint means at the selected preparations. These are fitted predictions, not observations.

Details

Five slots normally provide an incumbent repeat and up to four model optima. Each optimum is the best eligible unprepared recipe from that model. Shared nominees consume one slot and retain all endorsements. A requested fixed benchmark is reserved before optional model nominations; the required incumbent and benchmark must both fit within the budget. Definite budget shortfalls are rejected before fitting. When automatic incumbent selection needs fitted desirability anchors, the exact required number is checked after scoring. Their shared recipe has role incumbent_benchmark_repeat; a distinct fixed benchmark has role benchmark_repeat. Remaining slots are diverse medoids of high ensemble spread. That spread is a useful exploration diagnostic, not automatically calibrated uncertainty. If a model also endorses the incumbent, its unrestricted nomination records that fact. No maximum-GP-uncertainty slot is reserved in this allocation.

After required repeats, the budget truncates the optional model priority. Budget 1 with the incumbent enabled and no distinct fixed benchmark repeats the incumbent and produces zero new recipes. This nested order is a practical default, not a universally optimal ranking. A scientist needing one new recipe can set incumbent = FALSE. If nominations coincide, unavailable engines leave gaps, or the budget is larger, exploration fills the remaining slots when informative spread and eligible recipes are available. A shortfall is reported without inventing settings. Preparation IDs should be assigned by the laboratory on execution; recipe_id identifies settings within the pool. Preserve supplied stable IDs and historical recipes in subsequent pools to carry these labels across rounds; otherwise match physical settings, not automatically generated IDs.

The incumbent is the best observed preparation, not necessarily the best true recipe. Its selection uses measured outcomes with the same goal maps and weights as candidate scoring. Missing required outcomes are ineligible. Ties use a stable recipe key. For repeated independent preparations this intentionally retains the best observed row, even after a poorer repeat. Set incumbent_summary = "mean" to average independent preparations of each recipe within the reference context before scoring, or explicitly choose a historical row. Saturated multi-response goals remain exact desirability ties, resolved by a stable recipe key; no extra utility beyond the declared goal limits is assumed. Inspect best_ties and new_ties in the nominations: shared nominations resolved by the same tie key are not evidence of independent model agreement.

Refit by calling this function on the augmented measured data with the next explicit pool. The planner checks the measured predictor levels against spec before fitting. If a new measured day, operator or ingredient level is absent from the stored specification, rebuild it with bigexp_terms(..., data = augmented_data), keeping the intended formula, expansion settings and constraints, before calling the planner again. This prevents an old specification from mapping new measured levels to missing values and dropping those preparations. Missing response values remain subject to the individual endpoint fits' usual missing-data rules. To rescore different scientific weights or allocations without refitting, reuse fits and forward and pass spline = plan$spline if the earlier plan fitted a spline. With both primary and forward fits supplied, changing counts alone does not request a fresh spline. Request it explicitly with spline = TRUE or a custom priority containing "spline". Reused fits draw no random numbers. Supplied models must reproduce the complete training response/design rows, allowing row permutations and each endpoint's missing-response pattern. The predictors, nominal factors and block roles must agree with spec. A declared discrete support must match the fitted support. A declared mixture geometry must match any fitted geometry and is required at fit time for companions. For fits without companions and without fitted mixture geometry, the declared geometry constrains scoring. Changed measurements require refitting. Older objects without verifiable stored training rows cannot be reused here. Check plausible weight alternatives when they change scientific priorities; the model portfolio does not remove that sensitivity. The planner does not automatically add a round or batch factor. Only explicitly declared blocking factors enter the model. New measured levels require a rebuilt specification and refit; primary SVEM represents them as additive penalized effects without recipe-by-block interactions. Companion blocks may interact with recipe factors. A repeated fixed benchmark can connect contexts, and exploration extends recipe coverage, but neither guarantees separation of block and recipe effects. Compare new recipes with repeats prepared in the same batch; under an additive shift, their contrast removes that shift without relying on a fitted block term. Declare blocks from physical conditions before seeing outcomes. A fixed benchmark chosen in advance avoids selecting that reference for an unusually high observed response, but its repeat measurements are still noisy. Deliberate challenge preparations to revisit poor responses remain a manual choice.

GauPro contributes fitted means or joint desirability within this custom confirmation workflow. This is not a full Bayesian optimizer. For Gaussian process optimization and acquisition policies see Jones, Schonlau and Welch (1998), doi:10.1023/A:1008306431147, and Frazier (2018), https://arxiv.org/abs/1807.02811. The living vignette extends the workflow and SVEMnet papers at https://arxiv.org/abs/2212.11264 and https://arxiv.org/abs/2511.20968. Those author-maintained versions may be updated; published journal versions remain static.

Value

A svem_run_plan with ordinary fits and forward model lists, full scores, worklist, nominations, observed incumbent and optional benchmark provenance, shortfall and policy. A fitted or supplied spline adds spline. Explicit counts add allocation_counts and merged_nominations. Printing shows the achieved budget and roles. Plotting returns a score/spread view or endpoint prediction panels.

Examples

d <- data.frame(x = seq(-1, 1, length.out = 16))
d$y <- 2 + d$x - d$x^2 + 0.05 * sin(seq_len(nrow(d)))
spec <- bigexp_terms(y ~ x, d, factorial_order = 1,
                    polynomial_order = 2, report = FALSE)
goals <- list(y = list(goal = "max", weight = 1, lower_acceptable = 0, upper_acceptable = 3))
pool <- data.frame(x = seq(-1, 1, length.out = 41))
set.seed(7)
plan <- svem_plan_runs(spec, d, goals, pool, budget = 5,
  fit_control = list(nBoot = 3, companions = FALSE),
  forward_control = list(nBoot = 3), score_control = list(verbose = FALSE))
plan
plan$worklist[, c("recipe_id", "slot", "role", "nominated_by", "new_recipe")]
# Explicit file writing: svem_export_candidates_csv(plan, file = path)

Generate a Random Prediction Table from Multiple SVEMnet Models (no refit)

Description

Samples the original predictor factor space cached in fitted svem_model objects and computes predictions from each model at the same random points. This is intended for multiple responses built over the same factor space and a deterministic factor expansion (for example via a shared bigexp_terms), so that a shared sampling schema is available.

Usage

svem_random_table_multi(
  objects,
  n = 1000,
  mixture_groups = NULL,
  debias = FALSE,
  range_tol = 1e-08,
  numeric_sampler = c("random", "uniform"),
  candidates = NULL
)

Arguments

objects

A list of fitted svem_model objects returned by SVEMnet(). Each object must contain a valid $sampling_schema produced by SVEMnet(). A single model is also accepted and treated as a length-one list.

n

Number of random points to generate (rows in the output tables). Default is 1000.

mixture_groups

Optional list of mixture constraint groups. Each group is a list with a character vector vars, numeric vectors lower and upper in the same order as vars, and a numeric scalar total. Bounds default to 0 and 1, and the total defaults to 1. Mixture variables must be numeric-like and must also appear in the models' predictor_vars (that is, they must be used as predictors in all models).

debias

Logical; if TRUE, apply each model's calibration during prediction when available (for Gaussian fits). This is passed to predict.svem_model(). Default is FALSE.

range_tol

Numeric tolerance for comparing numeric ranges across models (used when checking that all $sampling_schema$num_ranges agree). Default is 1e-8.

numeric_sampler

Sampler for non-mixture numeric predictors: "random" (default), or "uniform".

  • "random": random Latin hypercube when the lhs package is available; otherwise independent uniforms via runif().

  • "uniform": independent uniform draws within numeric ranges (fastest; no lhs dependency).

candidates

Optional data frame of physical recipes to score, in the supplied order, instead of drawing new settings. Include all model predictors. Extra columns are ignored. Numeric limits, factor labels, discrete supports and declared mixture constraints are checked.

Details

No refitting is performed. Predictions are obtained by averaging per-bootstrap member predictions on the requested scale. Intercept-only models return n rows with no predictor columns and constant predictions.

All models must share an identical predictor schema. Specifically, their $sampling_schema entries must agree on:

The function stops with an informative error message if any of these checks fail.

Discrete numeric predictors (automatic). If any supplied model stores discrete-numeric sampling information in its $sampling_schema, this function will automatically respect it (no separate user argument).

This information is stored as:

(Older objects may use $sampling_schema$discrete_values instead of discrete_levels; this function accepts both for backward compatibility.)

Discrete numeric variables are sampled independently (uniform over allowed values) and are excluded from Latin hypercube sampling; LHS (when used) is applied only to the remaining continuous numeric predictors. Discrete numeric variables are not allowed to be mixture variables.

Models may be Gaussian or binomial. For binomial fits, predictions are returned on the probability scale (that is, on the response scale) by default, consistent with the default behavior of predict.svem_model().

Value

A list with three data frames:

Each prediction column is named by the model's response (left-hand side) with a "_pred" suffix (for example, "y1_pred"). If that name would collide with a predictor name or with another prediction column, the function stops with an error and asks the user to rename the response or predictor.

Typical workflow

  1. Build a deterministic expansion (for example with bigexp_terms) and fit several SVEMnet() models for different responses on the same factor space, using the same expansion / sampling settings.

  2. Ensure that the fitted models were created with a version of SVEMnet() that populates $sampling_schema.

  3. Collect the fitted models in a list and pass them to svem_random_table_multi().

  4. Use $data (predictors), $pred (response columns), or $all (cbind(data, pred)) for downstream plotting, summarization, or cross-response comparison.

Blocking variables

If the models were fit using a bigexp_spec that included blocking variables (for example blocking = c("Operator", "Plate_ID")) and SVEMnet() stored these in $sampling_schema$blocking, then svem_random_table_multi() will:

Specifically:

Blocking variables are not allowed to appear in mixture_groups. If any mixture group tries to use a blocking variable, the function stops with an error.

When no blocking information is present in $sampling_schema (for example for models fit without a bigexp_spec or without blocking), all predictors are sampled using their stored numeric ranges, discrete supports, or categorical levels and the chosen numeric_sampler.


Random-search scoring for SVEM models

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. Companion fits or an explicit preparation budget also return a worklist of recipes to test. For other selections from the scored table, see svem_select_from_score_table.

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

When specs is supplied, the function also attempts to append mean-level "in spec" probabilities and related joint indicators using the SVEM bootstrap ensemble via svem_append_design_space_cols. These quantities reflect uncertainty on the 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 with a warning regardless of verbose, and the affected table(s) are returned without the spec-related columns.

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,
  candidates = NULL,
  budget = NULL,
  gp_endpoint = NULL,
  increments = NULL,
  forward = NULL,
  allocation = c("legacy", "portfolio"),
  incumbent = FALSE,
  reference = NULL,
  priority = c("incumbent", "svemnet", "gaupro", "extra_trees", "forward"),
  exploration_fraction = 0.1,
  incumbent_summary = c("best", "mean"),
  benchmark = NULL,
  spline = NULL,
  n_optima = NULL,
  n_explore = NULL
)

Arguments

objects

List of svem_model objects (from SVEMnet). When unnamed, 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 svem_random_table_multi.

goals

List of per-response goal specifications. Either:

  • a named list, where names may be either names(objects) or the left-hand-side response names from the fitted models; or

  • an unnamed list with the same length as objects, in which case entries are matched to models by position.

Each goals[[response]] must be a list with at least:

  • goal: one of "max", "min", "target";

  • weight: nonnegative numeric weight.

For goal = "target", also provide target. Optional Derringer–Suich controls:

  • For "max" or "min": lower_acceptable, upper_acceptable, shape.

  • For "target": tol (symmetric), or tol_left / tol_right, and shape_left / shape_right.

When anchors/tolerances are not supplied, robust defaults are inferred from the sampled table using the q0.02–q0.98 span.

data

Optional data frame. When supplied (regardless of whether wmt is used), it is scored and returned as original_data_scored, with predictions (in <resp>_pred columns), per-response desirabilities, score (and wmt_score if applicable), and uncertainty_measure appended. When specs is supplied and the spec-limit augmentation succeeds, the same mean-level spec columns as in score_table (per-response <resp>_p_in_spec_mean, <resp>_in_spec_point, and joint p_joint_mean, joint_in_spec_point) are appended as well. Recognized columns from an earlier scoring round are replaced with a warning. A generated output name that collides with a model predictor or an observed response variable is rejected before sampling.

n

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

mixture_groups

Optional mixture and simplex constraints passed to svem_random_table_multi. Each group typically specifies mixture variable names, bounds, and a total.

level

Confidence level for percentile intervals used in the CI width and uncertainty calculations. Default 0.95.

combine

How to combine per-response desirabilities into a scalar score. One of:

  • "geom": weighted geometric mean (default);

  • "mean": weighted arithmetic mean.

numeric_sampler

Character string controlling how numeric predictors are sampled inside svem_random_table_multi. One of:

  • "random": Latin hypercube sampling when lhs is available, otherwise independent uniforms;

  • "uniform": independent uniforms over stored numeric ranges.

wmt

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

verbose

Logical; if TRUE, print a compact summary of the run (and any WMT diagnostics from upstream) to the console.

specs

Optional named list of specification objects, one per response in objects for which you want to define a mean-level spec constraint. Each entry should be either NULL (no specs for that response) or a list with components:

  • lower: numeric lower limit (may be -Inf, NA, or NULL for a one-sided upper spec);

  • upper: numeric upper limit (may be Inf, NA, or NULL for a one-sided lower spec).

Names of specs, when provided, should be a subset of names(objects) or of the model response names (left-hand sides). The specification structure matches that used by svem_append_design_space_cols.

candidates

Optional feasible recipe data frame to score in place of random sampling. Include all predictors. A unique recipe_id is optional. Repeated physical recipes collapse to the smallest supplied ID (numeric order or bytewise order for character IDs). Without IDs the scorer assigns ranks of sorted unique recipe keys, independent of the input row order. For a worklist, hold each block at its planned operating level. Physical recipe keys exclude those nuisance blocks.

budget

Optional positive physical preparation count for a worklist. Under allocation = "legacy" it counts distinct new preparations. The order is elastic-net SVEM, GauPro, Extra Trees, forward and spline optima when supplied, then SVEM spread, GP latent SD and high-spread medoids. Under allocation = "portfolio" it counts all physical preparations, including incumbent and benchmark repeats. Shared optima use one place. With unspecified exploration count, exploration can fill remaining slots. Supply data to exclude previously prepared recipes across all blocks, or use the recorded preparation settings in companion fits. A worklist's history_source reports the coverage available for this exclusion. For the legacy allocation, budget required controls separately.

gp_endpoint

Optional response name for GP exploration by latent SD. A single Gaussian response is inferred automatically when GauPro was fitted.

increments

Optional named numeric vector of positive preparation increments for an already discretized supplied pool, measured from zero. For example, use 0.0001 for component fractions in basis points.

forward

Optional named list of Gaussian forward fits for the same responses. They use the same desirability anchors and scientific weights.

allocation

"legacy" preserves the existing new-recipe allocation. "portfolio" uses the declared priority, then uncertainty medoids. The default formulation workflow in svem_plan_runs() selects this policy.

incumbent

For portfolio allocation, TRUE repeats the best observed preparation in the reference block context; FALSE omits it. A positive integer specifies a row of data explicitly. The repeat consumes one slot. Each historical row must represent an independent preparation; summarize technical readings first. Selection uses observed outcomes, not fitted historical means. Missing positively weighted outcomes are ineligible.

reference

Named list with one historical level of every blocking factor for automatic incumbent selection. By default uses the planned levels in candidates; raw observations from different blocks are not pooled. An explicit incumbent row instead supplies its own source context.

priority

Portfolio nomination order. The usual order is incumbent, elastic net, GauPro, Extra Trees and forward. The default order appends spline after forward only when a spline fit list is supplied to this scorer. A custom order can omit or reorder models. Shared optima consume one place.

exploration_fraction

Fraction of remaining eligible recipes offered to uncertainty medoid selection in portfolio allocation, default 0.1. The shortlist expands only enough to fill available slots when necessary; allocation_details records that expansion. Use 0.25 for a wider sensitivity.

incumbent_summary

How repeated independent preparations within a reference context are ranked. The default "best" retains the highest individual observation. A poor repeat cannot demote that stored maximum. Use "mean" to score each physical recipe's mean observed responses over complete preparations; technical readings must be summarized beforehand. An explicit incumbent row always uses that row alone.

benchmark

Optional fixed historical benchmark, carried in addition to the selected incumbent. Supply one row index of data, or a named one-element list identifying a unique row, such as list(RunID = "original-control"). The identifier form is convenient across refits. The recipe must satisfy the current preparation constraints; it need not have complete response observations. A requested benchmark is mandatory and consumes a slot. If it is the same physical recipe as the incumbent, one preparation carries both roles. Too small a budget errors.

spline

Optional named list of svem_spline() fits, one per response. Scoring uses the same physical factors, block declarations, mixture constraints, scientific weights and desirability anchors as the primary fits. Supplying a spline adds its fitted means, joint score and nomination. A spline treats known block levels as categories and is incompatible with a primary fit that declares a continuous companion block. This scorer uses previously fitted objects. Use svem_plan_runs() to fit models and prepare a worklist together.

n_optima

Optional number of distinct new model nominations in portfolio allocation. Duplicate model optima share one preparation and the next distinct prioritized model can fill the requested count. With n_optima = NULL, available models fill places left after required repeats and reserved exploration. An unfilled automatic model place is recorded as capacity.

n_explore

Optional exact number of uncertainty-medoid preparations. If both counts are supplied, an omitted budget is their sum plus the distinct required reference repeats. Supply a total budget when only one count is specified. Reference repeats consume budget first. With n_explore = NULL, remaining eligible slots are filled by exploration. Explicit counts can leave capacity unused. Inspect allocation_counts and merged_nominations for requested and obtained roles, shortages and shared physical recipes.

Details

Typical workflow

A common pattern is:

  1. Fit one or more SVEMnet() models for the responses of interest.

  2. Call svem_score_random() to:

    • draw candidate settings in factor space,

    • compute Derringer–Suich (DS) desirabilities and a combined multi-response score, and

    • attach a scalar uncertainty measure derived from percentile CI widths.

  3. Optionally provide specs to append mean-level "in spec" probabilities and joint indicators based on the SVEM bootstrap ensemble (process-mean assurance).

  4. Use svem_select_from_score_table to:

    • select one "best" row (e.g., maximizing score or wmt_score), and

    • pick a small, diverse set of medoid candidates for optimality or exploration (e.g. high uncertainty_measure).

    The robust bounded summaries score, per-response desirabilities, per-response weighted CI-width summaries, and 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 <response>_pred column. If the intended single exploration row is the widest interval, create and select on a raw width column such as <response>_ciw = <response>_upr - <response>_lwr.

  5. Run selected candidates, append the new data, refit the SVEM models, and repeat as needed.

Multi-response desirability scoring

Each response is mapped to a Derringer–Suich desirability d_r \in [0,1] according to its goal:

Per-response anchors (acceptable lower/upper limits or target-band tolerances) can be supplied in 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 <response>_pred directly when selecting the single largest or smallest predicted response.

Per-response desirabilities are combined into a single scalar score using either:

User-provided weights in goals[[resp]]$weight are normalized to sum to one and always define weights_original and the user-weighted score.

Whole-model reweighting (WMT)

When a WMT object from svem_wmt_multi is supplied via the 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:

w_r^{(\mathrm{final})} \propto w_r^{(\mathrm{user})} \times m_r,

where m_r comes from wmt$multipliers. The user weights always define score; the WMT-adjusted weights define wmt_score. The uncertainty measure is always weighted using the user weights, even when WMT is supplied.

Binomial responses. If any responses are fitted with family = "binomial", supplying a non-NULL 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 [0, 1] before desirability and uncertainty calculations.

Uncertainty measure

The uncertainty_measure is a weighted sum of robustly normalized percentile CI widths across responses. For each response, we compute the bootstrap percentile CI width \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 score_table and reused when scoring data, so uncertainty_measure in original_data_scored is on the same scale as in score_table:

\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 uncertainty_measure is then

\text{uncertainty}(x) = \sum_r w_r \, \tilde W_r(x),

where w_r are the user-normalized response weights derived from goals[[resp]]$weight. Larger values of 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 uncertainty_measure = 1. Use a raw interval width column such as <response>_ciw = <response>_upr - <response>_lwr when selecting the single widest interval. If a model has fewer than two bootstrap members or its interval prediction fails, the CI columns are NA. Its uncertainty contribution is the neutral value 0.5 times its user weight, and a warning identifies the affected response. These fallback values do not measure fitted uncertainty.

Spec-limit mean-level probabilities

If specs is provided, svem_score_random() attempts to pass the scored table and models to svem_append_design_space_cols to compute, for each response with an active spec:

and joint quantities:

Names in specs may refer either to names(objects) or to the model response names; they are automatically aligned to the fitted models.

These probabilities are defined on the 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 with a warning regardless of verbose, and score_table and/or original_data_scored are returned without the spec-related columns.

When companions or a budget are requested, score_table also contains one set of endpoint means and one comparable desirability per available engine. The returned worklist has one row per distinct nomination after excluding known preparations. Its selections can be passed to svem_export_candidates_csv(). nominations reports each engine's unrestricted and best unprepared candidate. exploration contrasts best predicted desirability among tied maximum SVEM spread scores with an unclipped standardized-width tie-break. All engines use the original scientific weights and the same SVEM-resolved desirability anchors. A sole max/min endpoint ranks by the raw fitted mean. For several endpoints, equally desirable candidates remain tied even when their predictions differ outside the specified desirability limits; a stable recipe key resolves those ties. The scorer does not invent extra utility beyond the declared goals. Mean-interval, WMT and specification diagnostics remain specific to SVEM.

Portfolio results also return uncertainty_anchors and a uncertainty_eligibility table with recipe IDs, informative-width flags and unclipped standardized widths. Constant or unavailable interval widths cannot support exploration. The worklist records its prediction context and the incumbent's observed source context and rows. The incumbent's by_context table reports observed winners separately, without pooling or adjusting outcomes across blocks. The incumbent object (not its by_context table) also records recipe_n_preparations (all attempted preparations of the selected physical recipe in its source context), recipe_n_complete, and recipe_mean_score, the desirability of its complete-preparation mean responses. These diagnostics do not change the default best-row selection. n_preparations counts the source rows used by the selected statistic, so it remains one for a best individual observation. The worklist exposes the recipe counts and mean score with the prefix incumbent_recipe_.

The companion worklist is a custom experimental confirmation workflow. GauPro nominates an optimum by the fitted response mean or joint desirability. A separate optional slot uses the largest latent standard deviation for a chosen endpoint. A full Bayesian optimization workflow specifies an acquisition rule and updates the choice as data arrive. Expected improvement, for example, combines improvement and uncertainty in one acquisition value (Jones, Schonlau and Welch, 1998, doi:10.1023/A:1008306431147; Frazier, 2018, https://arxiv.org/abs/1807.02811). Here the scientist allocates preparations, measures the responses and requests the refit.

Value

A list with components:

score_table

Data frame with predictors, predicted responses (columns <resp>_pred for each resp in names(objects)), per-response desirabilities, score, optional wmt_score, and uncertainty_measure. For each response r in names(objects), additional columns r_lwr, r_upr (percentile CI bounds at level level) and r_ciw_w (weighted, normalized CI width contribution to uncertainty_measure) are appended. When specs is supplied and the spec-limit augmentation succeeds, additional columns <resp>_p_in_spec_mean, <resp>_in_spec_point, p_joint_mean, and joint_in_spec_point are appended. A supplied candidate pool or a worklist adds recipe_id. A worklist also includes <resp>_<engine>_pred for each requested companion, <engine>_score for its joint goal and, when requested, <resp>_gp_latent_sd for GP exploration.

original_data_scored

If data is supplied, that data augmented with prediction columns <resp>_pred, per-response desirabilities, score, optional wmt_score, and uncertainty_measure; otherwise NULL. When specs is supplied and the spec-limit augmentation succeeds, the same mean-level spec columns as in score_table are appended to original_data_scored as well.

weights_original

User-normalized response weights.

weights_final

Final weights after WMT, if wmt is supplied; otherwise equal to weights_original.

wmt_p_values

Named vector of per-response whole-model p-values when wmt is supplied and contains p_values; otherwise NULL.

wmt_multipliers

Named vector of per-response WMT multipliers when wmt is supplied; otherwise NULL.

With companions, budget or gp_endpoint, the result also includes:

worklist

Scored recipes in preparation order, with slot, role, nominated_by (all endorsing engines) and nearest_distance to a previously prepared or earlier selected recipe. SVEM spread exploration uses maximum_spread for a unique maximum and best_predicted_tie when predicted desirability breaks a spread tie.

nominations

One row per engine with its unrestricted and best unprepared recipe IDs and tie counts. Status is selected, dropped_by_budget, dropped_by_model_count, not_prioritized, not_supplied, no_finite_score or no_unprepared_recipe. The slot column identifies the selected preparation and is missing for omitted optima. Engines endorsing the same recipe share a slot.

engine_status

Fit or prediction status and a diagnostic message for each engine and response. A companion needs finite predictions for every positively weighted response on the same recipe to nominate it.

exploration

The best predicted and widest-spread recipes among the maximum SVEM spread ties, with tie counts and desirability ranges.

tie_endpoint_ranges

Ranges of endpoint predictions, raw interval widths and weighted spread contributions among those ties.

selections

Worklist rows formatted for svem_export_candidates_csv().

shortfall

Without explicit counts, the number of budgeted preparations left unfilled. With n_optima or n_explore, the total model and exploration shortfall. Only an explicitly requested model count can contribute a model shortfall. Unused physical capacity is reported separately in allocation_counts$unused_capacity.

historical_unmatched

Number of history rows with missing or off-increment settings that could not be matched.

history_source

Whether previous preparations came from supplied data, all fitted response histories, some histories, or no history.

With explicit counts, the result also includes:

allocation_counts

Requested, targeted and selected model and exploration counts, distinct required repeats, role shortfalls, unfilled model capacity and unused physical capacity.

merged_nominations

Endorsements sharing a physical preparation with an earlier role.

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

References

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

See Also

SVEMnet, svem_random_table_multi, svem_select_from_score_table, svem_append_design_space_cols(), svem_wmt_multi

Examples

## Small runnable two-response example
set.seed(11)
n_toy <- 36
toy <- data.frame(x1 = runif(n_toy), x2 = runif(n_toy))
toy$yield <- 1 + 2 * toy$x1 - toy$x2 + rnorm(n_toy, sd = 0.15)
toy$impurity <- 1.5 - toy$x1 + 0.5 * toy$x2 + rnorm(n_toy, sd = 0.10)

fit_yield <- SVEMnet(yield ~ x1 + x2, toy, nBoot = 5,
                     glmnet_alpha = 1, relaxed = FALSE)
fit_impurity <- SVEMnet(impurity ~ x1 + x2, toy, nBoot = 5,
                        glmnet_alpha = 1, relaxed = FALSE)

toy_scored <- svem_score_random(
  objects = list(yield = fit_yield, impurity = fit_impurity),
  goals = list(
    yield = list(goal = "max", weight = 0.7),
    impurity = list(goal = "min", weight = 0.3)
  ),
  n = 100,
  numeric_sampler = "uniform",
  verbose = FALSE
)
head(toy_scored$score_table)

## Not run: 
## Production-scale lipid workflow. This is not run automatically because
## it fits three wide ensembles and optionally performs permutation WMTs.
## ------------------------------------------------------------------------
## 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, nCore = 1L),
  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)


## End(Not run)


Select diverse candidate rows via PAM medoids

Description

Select a small set of diverse candidate rows from a scored table. The function:

  1. Orders rows according to a score column by, with larger or smaller values treated as more desirable depending on direction.

  2. Retains the top fraction top_frac of rows.

  3. Computes a dissimilarity matrix on predictor_cols using cluster::daisy() with the requested metric.

  4. Runs cluster::pam() on the dissimilarities and returns the row indices of the selected medoids, in the original table.

This helper is used inside optimization routines to pick a small, diverse subset of high scoring or high uncertainty points while respecting the original sampled candidate set.

Usage

svem_select_candidates(
  table,
  by,
  top_frac,
  k,
  predictor_cols,
  direction = c("max", "min"),
  metric = "gower"
)

Arguments

table

A data.frame containing candidate rows, including the score column referenced by by and the predictor columns listed in predictor_cols.

by

Character scalar. Name of the column in table used for ranking.

top_frac

Numeric scalar in the open interval (0, 1] giving the fraction of top ranked rows to retain before clustering.

k

Integer scalar giving the desired number of medoids (candidates) to select. If k <= 0 or nrow(table) == 0, the function returns integer(0). Internally, k is truncated to the number of available rows in the top fraction.

predictor_cols

Character vector of column names in table that define the space on which diversity is measured. These columns are passed to cluster::daisy().

direction

Character scalar, either "max" or "min", indicating whether larger or smaller values of by are treated as more desirable when ranking rows and defining the top fraction.

metric

Character scalar giving the dissimilarity metric passed to cluster::daisy(). Defaults to "gower".

Details

Rows are ranked according to by, with larger or smaller values treated as better depending on direction ("max" or "min"). Rows whose by value is NA, NaN, Inf, or -Inf are excluded before ranking and cannot enter the top fraction. The function errors when no finite ranking values remain. Exact score ties retain input order. Supply a stable row order for reproducible ties. This function selects medoids only: it does not add a best row, check feasibility, exclude historical preparations, or deduplicate recipes. Supply an already filtered table with physical design factors only in predictor_cols; omit blocking factors. To request at least k shortlist rows, enlarge top_frac to at least k / nrow(table). Logical design factors are treated as symmetric nominal factors, including FALSE-FALSE matches. Clustering shortlists above 65,536 rows are rejected before allocating a dissimilarity matrix. The default workflow performs these steps through svem_plan_runs().

Value

An integer vector of row indices (referring to table) corresponding to the selected PAM medoids drawn from the top top_frac fraction of rows by by. If no candidates can be selected (for example because k <= 0 or nrow(table) == 0), returns integer(0).

Examples

set.seed(1)
n <- 100
tab <- data.frame(
  score = rnorm(n),
  x1    = runif(n),
  x2    = runif(n)
)

# Select 3 medoids from the top 20% by score (maximize score)
idx_max <- svem_select_candidates(
  table          = tab,
  by             = "score",
  top_frac       = 0.2,
  k              = 3,
  predictor_cols = c("x1", "x2"),
  direction      = "max"
)

# Select 3 medoids from the bottom 20% by score (minimize score)
idx_min <- svem_select_candidates(
  table          = tab,
  by             = "score",
  top_frac       = 0.2,
  k              = 3,
  predictor_cols = c("x1", "x2"),
  direction      = "min"
)


Select best row and diverse candidates from an SVEM score table

Description

Given a scored random-search table (e.g. from svem_score_random()), pick a single "best" row under a chosen objective column and sample a small, diverse set of medoid candidates from the top of that ranking. Any per-response CI columns (e.g. *_lwr / *_upr) present in score_table are carried through unchanged.

Optionally, a string label can be supplied to annotate the returned best row and candidates by appending that label to a "Notes_from_SVEMnet" column. If "Notes_from_SVEMnet" is missing, it is created. If it exists and is nonempty, the label is appended with "; " as a separator.

Usage

svem_select_from_score_table(
  score_table,
  target = "score",
  direction = c("max", "min"),
  k = 5,
  top_type = c("frac", "n"),
  top = 0.1,
  predictor_cols = NULL,
  label = NULL
)

Arguments

score_table

Data frame with predictors, responses, scores, and uncertainty_measure, typically scored$score_table from svem_score_random. When medoids are requested (k > 0), the predictor columns used for clustering are taken from the "svem_predictor_cols" attribute by default. If that attribute is missing, a heuristic is used. If you accidentally pass the full scored list, a helpful error is thrown reminding you to use scored$score_table.

target

Character scalar naming the column in score_table to optimize (e.g. "score", "wmt_score", "uncertainty_measure", or a response-specific column such as "Y_pred"). The robust bounded columns created by svem_score_random may contain ties at 0 or 1 because of percentile clipping. For a single largest predicted response, use <response>_pred; for a single widest interval, create a raw width column such as <response>_ciw = <response>_upr - <response>_lwr and use that as target.

direction

Either "max" or "min" indicating whether larger or smaller values of target are preferred.

k

Integer; desired number of medoid candidates to return. If k <= 0, only the best row is returned and no clustering is performed.

top_type

Either "frac" or "n" specifying whether top is a fraction of rows or an integer count.

top

Value for the top set: a fraction in (0,1] if top_type = "frac", or an integer >= 1 if top_type = "n".

predictor_cols

Optional character vector of predictor column names used to measure diversity in the PAM step when k > 0. When NULL (default), the function first tries attr(score_table, "svem_predictor_cols"). If that is unavailable, it falls back to a heuristic that prefers non-derived predictor columns (excluding e.g. *_pred, *_des, *_lwr, *_upr, *_ciw_w, *_p_in_spec_mean, *_in_spec_point, score, wmt_score, uncertainty_measure, p_joint_mean, joint_in_spec_point, candidate_type, selection_label, Notes_from_SVEMnet). If no usable predictor columns can be inferred, a warning is issued and only best is returned.

label

Optional character scalar. When non-NULL, this label is appended into a "Notes_from_SVEMnet" column for the returned best row and candidates. If "Notes_from_SVEMnet" is missing, it is created; if present and nonempty, the label is appended using "; " as separator.

Details

This row-based helper does not deduplicate physical recipes: the best row may repeat a medoid, and discrete candidate rows may repeat one another. Use svem_plan_runs() for a distinct, budgeted preparation worklist. svem_score_random() returns both response prediction columns (for example, Y_pred) and robust bounded summaries such as score, per-response desirabilities, per-response weighted CI-width summaries, and uncertainty_measure. The bounded summaries are useful for defining high-desirability or high-uncertainty regions for medoid selection, but they may not identify a unique single best row when several rows are clipped to 1. In that case, select on a raw column such as Y_pred, or add a raw CI-width column such as Y_ciw = Y_upr - Y_lwr, before calling this function.

Value

A list with components:

best

One-row data frame at the optimum of target under the specified direction, including any columns present in score_table (e.g. *_lwr / *_upr).

candidates

Data frame of medoid candidates (possibly empty or NULL) drawn from the top top of the ranking on target, with all columns carried through from score_table.

label

The label value supplied to this call (or NULL); used by svem_export_candidates_csv for the selection_label column.

call

The matched call, including all arguments used to create this selection object.

See Also

svem_score_random, svem_select_candidates()


SVEM whole-model significance test with mixture support (parallel)

Description

Perform a permutation-based whole-model significance test for a continuous (Gaussian) SVEM fit, with optional mixture-factor groups and parallel SVEM refits.

Usage

svem_significance_test_parallel(
  formula,
  data,
  mixture_groups = NULL,
  nPoint = 2000,
  nSVEM = 10,
  nPerm = 150,
  percent = 90,
  nBoot = 100,
  glmnet_alpha = c(1),
  weight_scheme = c("SVEM"),
  objective = c("wAIC", "wBIC", "wSSE", "auto"),
  relaxed = FALSE,
  verbose = TRUE,
  nCore = 1L,
  seed = NULL,
  rng_sample_kind = c("Rejection", "Rounding"),
  spec = NULL,
  response = NULL,
  use_spec_contrasts = TRUE,
  ...
)

Arguments

formula

A model formula. If spec is provided, the right-hand side is ignored and replaced by the locked expansion in spec. A bigexp_spec attached by bigexp_formula() or stored on spec$formula is also honored. Response transformations are retained unless response explicitly overrides the response column.

data

A data frame containing the variables in the model.

mixture_groups

Optional list describing one or more mixture-factor groups. Each element should be a list with components:

  • vars: character vector of column names;

  • lower: numeric vector of lower bounds (same length as vars);

  • upper: numeric vector of upper bounds (same length as vars);

  • total: scalar specifying the sum of the mixture variables.

All mixture variables must appear in exactly one group. When a bigexp_terms() specification declares groups, the evaluation grid inherits them and an explicitly supplied group list must agree.

nPoint

Number of random evaluation points in the factor space (default 2000); must be a positive integer.

nSVEM

Number of SVEM fits on the original (unpermuted) data used to summarize the observed surface (default 10); must be at least one.

nPerm

Number of SVEM fits on permuted responses used to build the null reference distribution (default 150); must be at least five. Values below 20 are permitted for tests but trigger a warning because they are too small for production inference.

percent

Percentage of variance to capture in the SVD of the permutation surfaces (default 90); must lie strictly between zero and 100.

nBoot

Number of bootstrap iterations within each inner SVEM fit (default 100); must be at least two because member standard deviations are required.

glmnet_alpha

Numeric vector of glmnet alpha values (default c(1)).

weight_scheme

Weighting scheme for the inner SVEM fits. Only "SVEM" is accepted: the whole-model test in Karl (2024) is defined for the anti-correlated fractional-random-weight scheme, so the other SVEMnet() weighting schemes are intentionally not offered here.

objective

Objective used inside SVEMnet() to pick the bootstrap path solution. One of "wAIC", "wBIC", or "wSSE" (default "wAIC").

relaxed

Logical; default FALSE. When TRUE, inner SVEMnet() fits use glmnet's relaxed elastic-net path and select both lambda and relaxed gamma on each bootstrap. When FALSE, the standard glmnet path is used. If relaxed = TRUE and glmnet_alpha includes 0, ridge (alpha = 0) is dropped by SVEMnet() for relaxed fits.

verbose

Logical; if TRUE, display progress messages (default TRUE).

nCore

Number of CPU cores for processing. Default is 1, which runs in-process and is suitable for CRAN and other shared systems. Values greater than one use a private cluster local to this call.

seed

Optional integer seed for reproducible RNG (default NULL). Deterministic per-fit seeds yield reproducibility regardless of parallel scheduling and core count.

rng_sample_kind

Sampling algorithm used with the "L'Ecuyer-CMRG" RNG. The default "Rejection" uses R's modern unbiased discrete sampler. Use "Rounding" to reproduce legacy WMT streams from SVEMnet 3.3.1 and earlier.

spec

Optional bigexp_spec created by bigexp_terms(). If provided, the test reuses its locked expansion while preserving the left-hand side of formula, unless response is supplied. If omitted, a valid spec attached to formula is used automatically. Categorical sampling uses spec$levels, and numeric sampling prefers spec$num_range when available. Discrete numeric predictors recorded by bigexp_terms() (spec$settings$discrete_numeric + spec$settings$discrete_levels) are sampled only from their recorded allowed levels when building the evaluation grid. Blocking variables recorded in spec$settings$blocking are treated as nuisance factors and held fixed at a single reference value across the evaluation grid (numeric blocks at the range midpoint, snapped to the discrete support when recorded; categorical blocks at the most frequent training level), matching svem_random_table_multi(). They are never swept, so between-block variation does not contribute to the whole-model distances.

response

Optional character name for the response variable to use when spec is supplied. If omitted, the response is taken from the left-hand side of formula.

use_spec_contrasts

Logical; default TRUE. When spec is supplied and use_spec_contrasts = TRUE, the function replays spec$settings$contrasts_options on the parallel workers for deterministic factor coding.

...

Additional arguments passed to SVEMnet() and then to glmnet() (for example: penalty.factor, lower.limits, upper.limits, standardize.response, etc.). The relaxed setting is controlled by the relaxed argument of this function. User weights and offset are not supported (SVEM controls its own weighting). Argument names that neither SVEMnet() nor the installed glmnet recognizes are ignored with a warning (misspelling protection). The documented maxp and path controls are accepted only when relaxed = TRUE.

Details

The procedure follows Karl (2024): it generates a space-filling evaluation grid in the factor space, fits multiple SVEM models on the original data and on permuted responses, standardizes grid predictions, reduces them via an SVD-based low-rank representation, and summarizes each fit by a Mahalanobis-type distance in the reduced space. A flexible SHASHo distribution is then fit to the permutation distances and used to obtain a whole-model p-value for the observed surface.

Because the test is based on a finite number of permutations and a fitted null distribution, the reported p-values are approximate and are intended as a diagnostic measure of global factor signal, not as exact hypothesis tests.

With nCore = 1, SVEM refits run in-process. With more than one core, the function uses a private PSOCK cluster and parallel::parLapplyLB(). It does not register or change a foreach backend.

Reproducible parallel RNG (Windows/macOS/Linux): when seed is supplied, the function sets its private computation stream to RNGkind("L'Ecuyer-CMRG", sample.kind = rng_sample_kind) and generates a deterministic, per-iteration seed schedule on the master. Each fit slot calls set.seed() with its assigned seed before performing random draws (including permutations and the bootstrap randomness inside SVEMnet()). This makes results reproducible regardless of worker scheduling and independent of the number of cores. The caller's RNG kind and .Random.seed are restored on exit, including error exits. With seed = NULL, the private stream derives from the caller's current state. Two calls starting from the same saved state can therefore repeat the same draws. Supply distinct seeds for independent repetitions. A failed fit slot is retried twice with deterministic sub-seeds while its response permutation remains fixed. If all three attempts fail, the test aborts with a failure summary rather than silently reducing nSVEM or nPerm. Likewise, a non-converged SHASHo null-distribution fit is reported as an error rather than being used to calculate a whole-model p-value. The SHASHo null distribution is fitted by independently written maximum-likelihood code based on the sinh-arcsinh-normal transformation of Jones and Pewsey (2009), doi:10.1093/biomet/asp053. It uses stats::nlminb(), deterministic multiple starts, and numerical derivatives. No source code from the former GAMLSS dependencies is used. The distribution and distance statistic are unchanged, but fitted parameters and p-values can differ from versions before 3.6.1, which used a different fitting algorithm. A fit must satisfy both the optimizer's convergence criterion and a small numerical likelihood gradient in the internally normalized coordinates (location, log(scale/tail), skew, log(tail)). If these checks fail, no p-value is returned; increasing nPerm can help resolve a small reference sample.

The function can optionally reuse a deterministic, locked expansion built with bigexp_terms(). Supply spec (and optionally response) to ensure that categorical levels, contrasts, and the polynomial/interaction structure are identical across repeated calls and across multiple responses sharing the same factor space.

The significance test accepts continuous numeric responses and uses Gaussian SVEM refits. A binomial response family is unsupported.

Value

An object of class "svem_significance_test", a list with components:

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

References

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

See Also

SVEMnet, bigexp_terms, bigexp_formula

Examples

## Not run: 
  ## Production WMT inference is intentionally not run in examples because
  ## it requires repeated ensemble fits for many response permutations.
  set.seed(1)

  # Small toy data with a 3-component mixture A, B, C
  n <- 40
  sample_trunc_dirichlet <- function(n, lower, upper, total) {
    k <- length(lower)
    stopifnot(length(upper) == k, total >= sum(lower), total <= sum(upper))
    avail <- total - sum(lower)
    if (avail <= 0) return(matrix(rep(lower, each = n), nrow = n))
    out <- matrix(NA_real_, n, k)
    i <- 1L
    while (i <= n) {
      g <- rgamma(k, 1, 1)
      w <- g / sum(g)
      x <- lower + avail * w
      if (all(x <= upper + 1e-12)) { out[i, ] <- x; i <- i + 1L }
    }
    out
  }

  lower <- c(0.10, 0.20, 0.05)
  upper <- c(0.60, 0.70, 0.50)
  total <- 1.0
  ABC   <- sample_trunc_dirichlet(n, lower, upper, total)
  A <- ABC[, 1]; B <- ABC[, 2]; C <- ABC[, 3]
  X <- runif(n)
  F <- factor(sample(c("red", "blue"), n, replace = TRUE))
  y <- 2 + 3*A + 1.5*B + 1.2*C + 0.5*X + 1*(F == "red") + rnorm(n, sd = 0.3)
  dat <- data.frame(y = y, A = A, B = B, C = C, X = X, F = F)

  mix_spec <- list(list(
    vars  = c("A", "B", "C"),
    lower = lower,
    upper = upper,
    total = total
  ))

  ## Example 1: direct formula interface (no locked expansion spec)
  res1 <- svem_significance_test_parallel(
    y ~ A + B + C + X + F,
    data           = dat,
    mixture_groups = mix_spec,
    glmnet_alpha   = 1,
    weight_scheme  = "SVEM",
    objective      = "auto",
    relaxed        = FALSE,   # default, shown for clarity
    nCore          = 1,
    seed           = 123,
    verbose        = FALSE
  )
  res1$p_value

  ## Example 2: using a deterministic bigexp expansion spec
  ## Build a wide expansion once and reuse it via `spec`
  spec <- bigexp_terms(
    y ~ A + B + C + X + F,
    data             = dat,
    factorial_order  = 2,  # up to 2-way interactions
    polynomial_order = 2   # up to quadratic terms in continuous vars
  )

  ## Run the same significance test, but with the locked expansion:
  ## - `formula` is still required, but its RHS is ignored when `spec` is given
  ## - `response` tells the helper which LHS to use with `spec`
  res2 <- svem_significance_test_parallel(
    y ~ A + B + C + X + F,
    data               = dat,
    mixture_groups     = mix_spec,
    glmnet_alpha       = 1,
    weight_scheme      = "SVEM",
    objective          = "auto",
    relaxed            = FALSE,
    nCore              = 1,
    seed               = 123,
    spec               = spec,
    response           = "y",
    use_spec_contrasts = TRUE,
    verbose            = FALSE
  )
  res2$p_value

## End(Not run)

Fit a Gaussian self-validated smooth surface

Description

Each member fits a training-weighted penalized mean and residual variance and scores these on its validation-weighted copy. For training and validation mean squared residuals t and v, the selection score is log(t) + v/t. A training fit that understates residual variation can receive a poor validation score. Predictions average the selected means.

Usage

svem_spline(
  spec,
  data,
  response = NULL,
  nBoot = 200L,
  store_member_weights = FALSE,
  lambda = c(Inf, 10^seq(1, -4, length.out = 9L))
)

Arguments

spec

A saved spline_terms() specification.

data

Measured rows containing the physical predictors, optional block IDs and a numeric response. Rows missing one of these fields are omitted. Study factors can be numeric or nominal as declared in the saved specification.

response

A single response column name. Defaults to the specification response. Reuse the same physical specification for multiple endpoints.

nBoot

Number of fractional random-weight members (default 200).

store_member_weights

Store the exact member uniforms and mean-one training and validation weights. Use set.seed() for reproducibility.

lambda

Advanced smoothing path starting with Inf for the intercept-only model, followed by strictly decreasing positive values.

Details

One uniform draw per preparation gives antithetic exponential training and validation weights. Minimize weighted training mean squared error plus lambda times the quadratic penalty. Each member considers an intercept-only fit, by default followed by nine smoothing strengths decreasing from 10 to 0.0001 on a log scale. The selected nonintercept effective degrees of freedom must be strictly below the validation Kish size bounded between 2 and n. Both linear directions and measured additive block contrasts contribute to this bound. Each block contrast has penalty one.

A constant response returns that constant exactly. General fits also treat variation within eight scale-relative machine epsilons as rounding-only, returning a constant up to that rounding. Otherwise the score uses responses centered at their midrange and divided by their largest absolute centered value, with training variance floored at squared machine epsilon in those units. For positive variance above the floor this conditioning changes all candidate scores by one additive constant. The first minimum on the ordered admissible path wins exact ties.

General fits record per-response measured nominal levels and an equal-weight path-capacity diagnostic beside the actual member Kish caps. Their member diagnostics also report effective degrees of freedom by basis family. These add to slope EDF but are a descriptive allocation among overlapping penalized bases, not a scientific variance decomposition. A constant general surface has no directional optimum. When the joint fit is flat, portfolio scoring omits the spline nominee and reports no_signal for fully covered endpoints. Endpoints with missing predictions retain their coverage status. Ordinary predictions remain available on covered recipes. A constant endpoint can still contribute to a joint goal when other weighted endpoints vary.

Value

A svem_spline fit. Its member_diagnostics record smoothing strengths, conditional effective degrees of freedom, Kish caps, selected residual variances and Gaussian scores in conditioned response units. path_scores contains all scores with excluded points set to infinity. coef_matrix contains conditioned-response coefficients in the saved basis, one row per member. Ordinary predictions use original response units. Optional member spread describes variation among fitted members.

See Also

spline_terms()


Propose a batch of runs by Thompson sampling from SVEM bootstrap ensembles

Description

Experimental sequential-design proposer for multi-response SVEM workflows. This extends the core workflow described in Karl (2026), doi:10.1016/j.chemolab.2026.105660; its performance is not established by the results in that package paper. Given fitted SVEMnet models and per-response desirability goals, svem_thompson_batch() proposes batch_size new candidate runs by parallel Thompson sampling (Thompson, 1933; Kandasamy et al., 2018) from the SVEM bootstrap ensembles: for each batch slot, one bootstrap member is drawn at random for each response, the drawn members' predictions are mapped to Derringer–Suich desirabilities and combined into a single score, and the candidate maximizing that score is added to the batch (previously selected candidates are excluded).

Each slot re-draws the bootstrap members, allowing candidates preferred by different members to enter the batch without an explicit exploration parameter. Bootstrap members are used as heuristic function draws; they are not samples from a fitted Bayesian posterior. This complements the score/uncertainty workflow of svem_score_random and svem_select_from_score_table: CI-width exploration targets regions of large global uncertainty, while Thompson sampling refines the current contenders for the optimum.

Usage

svem_thompson_batch(
  objects,
  goals,
  batch_size = 4,
  candidates = NULL,
  n = 5000,
  mixture_groups = NULL,
  numeric_sampler = c("random", "uniform"),
  combine = c("geom", "mean"),
  verbose = TRUE
)

Arguments

objects

List of svem_model objects (from SVEMnet), one per response, as in svem_score_random. When unnamed, response names are inferred from the model formulas. All models must have bootstrap coefficients (coef_matrix; fit with nBoot >= 2) and, when candidates = NULL, a shared sampling schema.

goals

List of per-response goal specifications in the same format as svem_score_random (see Details).

batch_size

Integer number of runs to propose. Default 4.

candidates

Optional data frame of candidate settings containing the predictor columns required by the models. When NULL (default), n candidates are drawn with svem_random_table_multi. Supplied columns must not use generated output names (slot, ts_score, or response _draw, _des, _pred columns); collisions are rejected to preserve candidate settings.

n

Number of random candidates to draw when candidates is NULL. Default 5000.

mixture_groups

Optional mixture and simplex constraints passed to svem_random_table_multi when generating candidates. For supplied candidates, groups declared in the fitted factor specification are enforced and an explicit declaration must agree. For older fits without declared groups, the caller supplies an already feasible table.

numeric_sampler

Character; how numeric predictors are sampled when candidates = NULL: "random" (Latin hypercube when lhs is available) or "uniform". As in svem_score_random.

combine

How to combine per-response desirabilities into the slot score: "geom" (weighted geometric mean; default) or "mean" (weighted arithmetic mean).

verbose

Logical; if TRUE (default), print a compact summary of the proposed batch.

Details

Candidate table

Candidates may be supplied directly via candidates (a data frame containing at least the predictor columns used by the models). When candidates = NULL, n random candidates are drawn from the models' shared sampling schema with svem_random_table_multi, honoring mixture_groups and numeric_sampler exactly as in svem_score_random.

Goals, desirabilities, and combination

goals uses the same specification as svem_score_random: each response gets goal ("max", "min", or "target"), a nonnegative weight, and optional Derringer–Suich anchors (lower_acceptable/upper_acceptable/shape, or target/tol/tol_left/tol_right/ shape_left/shape_right). When anchors are not supplied, robust defaults are inferred from the ensemble-mean predictions over the candidate table using the q0.02–q0.98 span, mirroring svem_score_random. Fixing anchors explicitly is recommended for sequential use so that the desirability scale does not drift between iterations. Desirabilities are combined by a weighted geometric mean (combine = "geom", with a small floor inside the log) or a weighted arithmetic mean (combine = "mean"); weights are normalized to sum to one.

Thompson draws

For each response the full member-by-candidate prediction matrix is formed once from object$coef_matrix (all models must be fitted with nBoot >= 2 so that coef_matrix is available). For slot b = 1, \dots, batch_size and response r, a member index m_{b,r} is drawn uniformly at random and the slot score at candidate x is

s_b(x) = \prod_r d_r\{\hat y^{(m_{b,r})}_r(x)\}^{w_r}

(or the weighted arithmetic mean when combine = "mean"). The proposal for slot b is the eligible candidate maximizing s_b(x); ties keep the earliest candidate row. Draws are controlled by the R random number generator, so use set.seed() for reproducibility.

Predictions are always computed with debiasing disabled (debias = FALSE), matching svem_score_random. Binomial responses are handled on the probability scale.

Typical sequential workflow

  1. Fit one SVEMnet() model per response on the current data.

  2. Call svem_thompson_batch() to propose the next batch of runs (optionally reserving some slots for medoids from svem_select_from_score_table, e.g. top-score medoids for exploitation or widest-CI medoids for global gap-filling).

  3. Run the proposed settings, append the new data, refit, repeat.

Value

An object of class "svem_thompson_batch": a list with components

proposals

Data frame with batch_size rows: the selected candidate settings (all columns of the candidate table), plus slot (batch position), ts_score (the slot's Thompson score at the selected candidate), <resp>_draw (the drawn member's prediction at the selected candidate), <resp>_des (its desirability), and <resp>_pred (the ensemble-mean prediction, for reference).

members

Integer matrix (batch_size by number of responses) of the bootstrap member indices drawn for each slot.

candidate_index

Integer vector: row indices of the proposals in the candidate table.

candidates

The candidate table used (predictor columns).

weights

Normalized response weights.

ds_params

Per-response Derringer–Suich parameters actually used (after anchor inference), useful for keeping anchors fixed across sequential iterations.

call

The matched call.

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

References

Thompson, W. R. (1933). On the likelihood that one unknown probability exceeds another in view of the evidence of two samples. Biometrika, 25(3–4), 285–294. doi:10.1093/biomet/25.3-4.285

Kandasamy, K., Krishnamurthy, A., Schneider, J., and Poczos, B. (2018). Parallelised Bayesian optimisation via Thompson sampling. Proceedings of the 21st International Conference on Artificial Intelligence and Statistics (AISTATS), PMLR 84, 133–142.

Russo, D. J., Van Roy, B., Kazerouni, A., Osband, I., and Wen, Z. (2018). A tutorial on Thompson sampling. Foundations and Trends in Machine Learning, 11(1), 1–96. doi:10.1561/2200000070

Derringer, G., and Suich, R. (1980). Simultaneous optimization of several response variables. Journal of Quality Technology, 12(4), 214–219. doi:10.1080/00224065.1980.11980968

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

See Also

SVEMnet, svem_score_random, svem_select_from_score_table, svem_random_table_multi

Examples

## Small runnable two-response proposal
set.seed(21)
n_toy <- 36
toy <- data.frame(x1 = runif(n_toy), x2 = runif(n_toy))
toy$yield <- 1 + 1.5 * toy$x1 - toy$x2 + rnorm(n_toy, sd = 0.15)
toy$impurity <- 1.2 - toy$x1 + 0.4 * toy$x2 + rnorm(n_toy, sd = 0.10)

fit_yield <- SVEMnet(yield ~ x1 + x2, toy, nBoot = 5,
                     glmnet_alpha = 1, relaxed = FALSE)
fit_impurity <- SVEMnet(impurity ~ x1 + x2, toy, nBoot = 5,
                        glmnet_alpha = 1, relaxed = FALSE)

toy_prop <- svem_thompson_batch(
  objects = list(yield = fit_yield, impurity = fit_impurity),
  goals = list(
    yield = list(goal = "max", weight = 0.7),
    impurity = list(goal = "min", weight = 0.3)
  ),
  batch_size = 2,
  n = 100,
  verbose = FALSE
)
toy_prop$proposals

## Not run: 
## Production-scale lipid workflow. This is not run automatically because
## it fits wide ensembles and searches thousands of constrained candidates.
## ------------------------------------------------------------------------
## Thompson-sampling batch proposals for the lipid screen
## ------------------------------------------------------------------------

data(lipid_screen)

spec <- bigexp_terms(
  Potency ~ PEG + Helper + Ionizable + Cholesterol +
    Ionizable_Lipid_Type + N_P_ratio + flow_rate,
  data             = lipid_screen,
  factorial_order  = 2,
  polynomial_order = 2
)

set.seed(1)
fit_pot <- SVEMnet(bigexp_formula(spec, "Potency"), lipid_screen)
fit_siz <- SVEMnet(bigexp_formula(spec, "Size"),    lipid_screen)

objs <- list(Potency = fit_pot, Size = fit_siz)

goals <- list(
  Potency = list(goal = "max", weight = 0.7),
  Size    = list(goal = "min", weight = 0.3)
)

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
))

set.seed(2)
prop <- svem_thompson_batch(
  objects        = objs,
  goals          = goals,
  batch_size     = 4,
  n              = 3000,
  mixture_groups = mix
)

prop$proposals[, c("PEG", "Helper", "Ionizable", "Cholesterol",
                   "ts_score", "Potency_draw", "Size_draw")]

## Keep the inferred desirability anchors fixed at later iterations by
## passing explicit anchors extracted from prop$ds_params, e.g.:
p <- prop$ds_params$Potency
goals$Potency <- list(goal = "max", weight = 0.7,
                      lower_acceptable = p$L, upper_acceptable = p$U)

## ------------------------------------------------------------------------
## Hybrid batch: exploit medoids + Thompson-sampling slots
## ------------------------------------------------------------------------

set.seed(3)
scored <- svem_score_random(objs, goals, n = 3000,
                            mixture_groups = mix, verbose = FALSE)
sel <- svem_select_from_score_table(scored$score_table,
                                    target = "score", k = 2)

set.seed(4)
ts <- svem_thompson_batch(objs, goals, batch_size = 2,
                          n = 3000, mixture_groups = mix,
                          verbose = FALSE)

pred_cols <- attr(scored$score_table, "svem_predictor_cols")
batch <- rbind(sel$candidates[, pred_cols],
               ts$proposals[, pred_cols])
batch

## End(Not run)


Whole-model tests for multiple SVEM responses (WMT wrapper)

Description

Convenience wrapper around svem_significance_test_parallel for running whole-model tests (WMT) on multiple responses that share the same dataset and mixture constraints. This helper:

The resulting multipliers vector is designed to be passed directly to downstream scoring functions (for example, as an optional WMT argument to svem_score_random()), with response names matched by names().

Usage

svem_wmt_multi(
  formulas,
  data,
  mixture_groups = NULL,
  wmt_transform = c("neglog10", "one_minus_p"),
  wmt_control = list(seed = 123),
  plot = TRUE,
  verbose = TRUE
)

Arguments

formulas

A single formula or a (preferably named) list of formulas, one per response. If unnamed, response names are inferred from the left-hand side of each formula; non-unique names are made unique.

data

Data frame containing the predictors and responses referenced in formulas.

mixture_groups

Optional mixture and simplex constraints passed to svem_significance_test_parallel.

wmt_transform

Character; transformation used to convert WMT p-values into multipliers. One of:

  • "neglog10": f(p) = [-\log_{10}(p)]^{\text{strength}},

  • "one_minus_p": f(p) = (1 - p)^{\text{strength}}.

Currently, strength = 1 is used internally.

wmt_control

Optional list of extra arguments passed directly to svem_significance_test_parallel. By default this is list(seed = 123) so that WMT calls are reproducible; you may override or extend this (e.g. list(seed = 999, nPerm = 300)). A nonempty list must be fully named. Arguments formula, data, mixture_groups, and response are controlled by this wrapper and ignored with a warning; each response, including any transformation, comes from its formula. Other duplicated names are rejected. A shared spec is allowed: it supplies the expansion and sampling schema for every formula, takes precedence over attached specifications, and preserves each formula's left-hand side. The WMT is serial by default. Pass nCore explicitly to opt into parallel execution, and use rng_sample_kind = "Rounding" only when reproducing the legacy pre-3.4.0 random stream. Any entries not recognized by svem_significance_test_parallel are ignored by that function.

plot

Logical; if TRUE (default), attempt to plot all successfully computed WMT objects together via plot.svem_significance_test.

verbose

Logical; if TRUE (default), print progress and a compact summary of p-values and multipliers.

Value

A list of class "svem_wmt_multi" with components:

wmt_objects

Named list of WMT objects (one per response), as returned by svem_significance_test_parallel(). Entries are NULL where a WMT call failed.

p_values

Named numeric vector of per-response p-values (bounded away from 0/1), or NA when unavailable.

multipliers

Named numeric vector of per-response WMT multipliers derived from the p-values using wmt_transform. Failed tests retain multiplier 1 and emit a warning regardless of verbose; this fallback is not evidence of model significance.

wmt_transform

The transformation used.

wmt_control

The remaining arguments passed through to svem_significance_test_parallel() after removing the wrapper-controlled entries.

Acknowledgments

Generative AI tools were used to assist with coding and roxygen documentation; all content was reviewed and finalized by the author.

References

Karl, A. T. (2026). SVEMnet: An R package for self-validated elastic-net ensembles and multi-response optimization in small-sample mixture-process experiments. Chemometrics and Intelligent Laboratory Systems, 271, 105660. doi:10.1016/j.chemolab.2026.105660

Gotwalt, C., & Ramsey, P. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849873/redirect_from_archived_page/true

Karl, A. T. (2024). A randomized permutation whole-model test heuristic for Self-Validated Ensemble Models (SVEM). Chemometrics and Intelligent Laboratory Systems, 249, 105122. doi:10.1016/j.chemolab.2024.105122

Karl, A., Wisnowski, J., & Rushing, H. (2022). JMP Pro 17 Remedies for Practical Struggles with Mixture Experiments. JMP Discovery Conference. doi:10.13140/RG.2.2.34598.40003/1

Lemkus, T., Gotwalt, C., Ramsey, P., & Weese, M. L. (2021). Self-Validated Ensemble Models for Design of Experiments. Chemometrics and Intelligent Laboratory Systems, 219, 104439. doi:10.1016/j.chemolab.2021.104439

Xu, L., Gotwalt, C., Hong, Y., King, C. B., & Meeker, W. Q. (2020). Applications of the Fractional-Random-Weight Bootstrap. The American Statistician, 74(4), 345–358. doi:10.1080/00031305.2020.1731599

Ramsey, P., Gaudard, M., & Levin, W. (2021). Accelerating Innovation with Space Filling Mixture Designs, Neural Networks and SVEM. JMP Discovery Conference. https://community.jmp.com/t5/Abstracts/Accelerating-Innovation-with-Space-Filling-Mixture-Designs/ev-p/756841

Ramsey, P., & Gotwalt, C. (2018). Model Validation Strategies for Designed Experiments Using Bootstrapping Techniques With Applications to Biopharmaceuticals. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/Model-Validation-Strategies-for-Designed-Experiments-Using/ev-p/849647/redirect_from_archived_page/true

Ramsey, P., Levin, W., Lemkus, T., & Gotwalt, C. (2021). SVEM: A Paradigm Shift in Design and Analysis of Experiments. JMP Discovery Conference - Europe. https://community.jmp.com/t5/Abstracts/SVEM-A-Paradigm-Shift-in-Design-and-Analysis-of-Experiments-2021/ev-p/756634

Ramsey, P., & McNeill, P. (2023). CMC, SVEM, Neural Networks, DOE, and Complexity: It's All About Prediction. JMP Discovery Conference.

Friedman, J. H., Hastie, T., and Tibshirani, R. (2010). Regularization Paths for Generalized Linear Models via Coordinate Descent. Journal of Statistical Software, 33(1), 1-22.

Meinshausen, N. (2007). Relaxed Lasso. Computational Statistics & Data Analysis, 52(1), 374-393.

Kish, L. (1965). Survey Sampling. Wiley.

Lumley, T. (2004). Analysis of complex survey samples. Journal of Statistical Software, 9(1), 1–19.

Lumley, T. and Scott, A. (2015). AIC and BIC for modelling with complex survey data. Journal of Survey Statistics and Methodology, 3(1), 1–18.

Tibshirani, R. J. and Taylor, J. (2012). Degrees of freedom in lasso problems. The Annals of Statistics, 40(2), 1198–1232. doi:10.1214/12-AOS1003

See Also

svem_significance_test_parallel, plot.svem_significance_test

Examples

## Not run: 
## Production WMT workflow: not run automatically because each response
## requires repeated SVEM fits under many response permutations.
data(lipid_screen)

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")

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
))

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_transform  = "neglog10",
  wmt_control    = list(seed = 123, nCore = 1L),
  plot           = TRUE
)

wmt_out$p_values
wmt_out$multipliers

## later: pass wmt_out$multipliers into svem_score_random()

## End(Not run)


Evaluate code with the spec's recorded contrast options

Description

with_bigexp_contrasts() temporarily restores the contrasts options that were active when the spec was built, runs a block of code, and then restores the original options. This is useful when a modeling function uses the global options("contrasts") to decide how to encode factors (for example, lm(), glm(), or other modeling functions that call model.matrix() internally).

Usage

with_bigexp_contrasts(spec, code)

Arguments

spec

A "bigexp_spec" object with stored contrasts_options in settings.

code

Code to evaluate with temporarily restored options.

Examples

set.seed(1)
df4 <- data.frame(
  y  = rnorm(10),
  X1 = rnorm(10),
  G  = factor(sample(c("A", "B"), 10, replace = TRUE))
)

spec4 <- bigexp_terms(
  y ~ X1 + G,
  data             = df4,
  factorial_order  = 2,
  polynomial_order = 2
)

with_bigexp_contrasts(spec4, {
  mm4 <- model.matrix(spec4$formula, df4)
  head(mm4)
})