Class SaasboSampler
- Namespace
- OptunaSharp.Samplers
- Assembly
- OptunaSharp.dll
A sampler using SAASBO — high-dimensional Bayesian optimization with sparse axis-aligned subspaces (Eriksson & Jankowiak 2021). Each trial it fits an RBF ARD Gaussian process under the SAAS prior — a half-Cauchy sparsity prior on the per-dimension inverse-squared length scales that "turns off" most dimensions unless the data demands otherwise (automatic relevance determination) — and proposes the next point by maximizing the expected improvement averaged over posterior hyperparameter samples. This is the paper's answer to the curse of dimensionality: work directly in the full space, but let a sparsity-inducing function prior keep the surrogate parsimonious enough to infer from a few hundred queries.
Inference. The default is the No-U-Turn Sampler (Nuts,
the paper's "gold standard"), which averages EI over a bag of L posterior draws;
Map is a faster point-estimate alternative (Adam over a small
shrinkage grid + leave-one-out selection). The single prior hyperparameter is α (global
shrinkage, default 0.1); every other knob is purely computational.
Cost footgun. SAASBO is deliberately expensive: the paper's default NUTS budget
(512 warmup + 256 samples, thinned to 16) runs Hamiltonian Monte Carlo over an O(N³D) gradient
per leapfrog step, and the default EI maximization screens Q = 5000 Sobol candidates before
three L-BFGS-B polishes — appropriate when the objective is expensive to evaluate (the paper targets
a few hundred queries) but heavy otherwise. Lower numWarmup/numSamples/numCandidates
(or use Map) to trade fidelity for speed. Best for
N ≲ 500 observations.
Continuous / integer only. The SAAS GP models a continuous [0, 1]^D box, so the
relative search space is Float/Int only; categorical and single-valued parameters are delegated to
the independent sampler, exactly as in TurboSampler / CmaEsSampler.
Dynamic search spaces fall back to independent sampling with a warning. Single-objective only.
State is reconstructed, not persisted. Like TurboSampler, SAASBO stores no
optimizer blob: each trial re-fits the GP to the completed observations, so a study resumes from
storage with no serialized state. The one thing it writes is the Sobol row each initialization trial
took (the system attr "saasbo:init"), so trials asked at the same time (Optimize(nJobs))
take distinct rows instead of all receiving the row the completed count names.
Conformance to the paper (audited; see SaasboPaperClaimsTests). The RBF kernel
(Eqn 1), closed-form posterior (Eqns 4-5), SAAS prior (Eqn 8) with the reparameterization (Eqn 12),
the EI acquisition and its sample-averaging (Eqns 6-7, 10), the NUTS budget (§A.1), the MAP recipe
(§A.2), and the EI-maximization strategy (§B) are all implemented as stated; the empirical claims of
§5.2 (relevant-subspace identification, α controlling shrinkage) and §4.1/§5.7 (adaptive sparsity —
more data turns on more dimensions) are asserted on seeded chains. Four deliberate, documented scope
choices differ from the paper's full generality, none of them a correctness gap: (1) the RBF
kernel only — the paper's default — not the optional Matérn-5/2; (2) a fixed tiny noise
(the paper's noise-free primary setting), so the noisy-objective extension (a LN(0,10²) prior
on σ²) is not modeled; (3) standardized targets (a numerical convention shared with the
reference implementation and the rest of the GP stack); and (4) SAASBO-MAP maximizes the density in
the constrained parameter (the paper's literal Eqn 13, "maximize U_s w.r.t. ψ"), not the
unconstrained-space mode. The MAP shrinkage grid follows the authoritative §A.2 (S=4,
{1,1e-1,1e-2,1e-3}), resolving the main text's smaller §4.2.2 list. Parity is behavioral
(HMC is stochastic), not byte-exact vs NumPyro/BoTorch — the TuRBO/REMBO validation tier.
public sealed class SaasboSampler : BaseSampler
- Inheritance
-
SaasboSampler
- Inherited Members
Examples
// A 2-D objective embedded in 20 dimensions — SAASBO discovers the two relevant axes.
var study = Optuna.CreateStudy(sampler: new SaasboSampler(
seed: 0, nStartupTrials: 8,
// light budget for a fast demo (paper defaults are far heavier):
numWarmup: 64, numSamples: 64, thinning: 8, numCandidates: 256));
study.Optimize(t =>
{
double a = t.SuggestFloat("x0", -5, 10), b = t.SuggestFloat("x1", 0, 15);
for (int i = 2; i < 20; i++) t.SuggestFloat($"x{i}", 0, 1); // irrelevant dimensions
return (b - 5.1 / (4 * Math.PI * Math.PI) * a * a + 5 / Math.PI * a - 6) * 2; // Branin-like
}, nTrials: 40);
Constructors
SaasboSampler(BaseSampler?, bool, int?, int, SaasboInferenceMethod, double, int, int, int, int, double, int, int, int, int, double, double)
Configures SAASBO's Sobol startup, SAAS-GP inference, and EI maximization.
public SaasboSampler(BaseSampler? independentSampler = null, bool warnIndependentSampling = true, int? seed = null, int nStartupTrials = 10, SaasboInferenceMethod inferenceMethod = SaasboInferenceMethod.Nuts, double alpha = 0.1, int numWarmup = 512, int numSamples = 256, int thinning = 16, int maxTreeDepth = 6, double targetAccept = 0.8, int mapSteps = 1500, int numCandidates = 5000, int numRestarts = 3, int maxLbfgsEvals = 100, double noiseVariance = 1E-06, double kernelVarianceLogStd = 10)
Parameters
independentSamplerBaseSamplerSampler for parameters outside the relative search space and for the Sobol-init fallback (default RandomSampler).
warnIndependentSamplingboolEmit a warning when a parameter is sampled independently.
seedint?Random seed; a fixed seed reproduces the trajectory on a platform.
nStartupTrialsintSobol initialization trials before the SAAS GP takes over (paper's
m; default 10, the paper's Branin/Rosenbrock value). Must be at least 1.inferenceMethodSaasboInferenceMethodNUTS (paper default) or MAP.
alphadoubleThe SAAS global-shrinkage hyperparameter
α > 0(NUTS only; MAP ignores it). Smaller is more conservative (prefers smaller subspaces); the paper's default is0.1.numWarmupintNUTS warmup iterations (paper: 512). Ignored for MAP.
numSamplesintNUTS post-warmup iterations before thinning (paper: 256). Ignored for MAP.
thinningintNUTS thinning factor — every
thinning-th post-warmup draw is retained (paper: 16, givingL = 16samples). Ignored for MAP.maxTreeDepthintNUTS maximum tree depth (paper: 6). Ignored for MAP.
targetAcceptdoubleNUTS dual-averaging target acceptance rate (0.8). Ignored for MAP.
mapStepsintMAP Adam iterations per shrinkage value (paper: 1500). Ignored for NUTS.
numCandidatesintEI-maximization Sobol screen size
Q(paper: 5000).numRestartsintEI-maximization L-BFGS-B restarts
K(paper: 3).maxLbfgsEvalsintEI-maximization per-restart L-BFGS-B evaluation cap (paper: 100).
noiseVariancedoubleFixed GP observation-noise variance (paper:
1e-6, noise-free). Must be positive.kernelVarianceLogStddoubleStd of the log-Normal prior on the kernel variance
σ²_k(paper:10).
Exceptions
- ArgumentException
A count is below one or a positive-valued knob is non-positive.
Methods
AfterTrial(Study, FrozenTrial, TrialState, IReadOnlyList<double>?)
Trial post-processing: called after the objective function returns and right before the trial is finished and its state is stored.
public override void AfterTrial(Study study, FrozenTrial trial, TrialState state, IReadOnlyList<double>? values)
Parameters
studyStudyStudy that owns the trial being finished.
trialFrozenTrialTrial snapshot before its terminal state is written to storage.
stateTrialStateTerminal state being recorded: complete, pruned, or failed.
valuesIReadOnlyList<double>Resulting trial values in objective order; non-null for a successful trial.
BeforeTrial(Study, FrozenTrial)
Trial pre-processing: called during trial initialization, just before the InferRelativeSearchSpace(Study, FrozenTrial) call.
public override void BeforeTrial(Study study, FrozenTrial trial)
Parameters
studyStudyStudy that owns the newly initialized trial.
trialFrozenTrialNew trial snapshot available before search-space inference.
InferRelativeSearchSpace(Study, FrozenTrial)
Infer the search space that will be used by relative sampling in the target trial.
This method is called right before SampleRelative(Study, FrozenTrial, OrderedDict<string, BaseDistribution>), and the search space returned by this method is passed to it. The parameters not contained in the search space will be sampled by using SampleIndependent(Study, FrozenTrial, string, BaseDistribution).
public override OrderedDict<string, BaseDistribution> InferRelativeSearchSpace(Study study, FrozenTrial trial)
Parameters
studyStudyStudy supplying trial history, directions, and storage.
trialFrozenTrialCurrent trial snapshot; copy it before modifying its contents.
Returns
- OrderedDict<string, BaseDistribution>
Parameter distributions eligible for joint sampling, in sampler-defined order.
ReseedRng()
Reseed sampler's random number generator.
Parallel optimization calls this hook before a worker's sequential trial loop. Concrete samplers refresh their random streams so workers do not deliberately reuse a seeded sequence. OptunaSharp workers share the study's sampler instance; this hook does not clone it or make concurrent sampling reproducible.
public override void ReseedRng()
SampleIndependent(Study, FrozenTrial, string, BaseDistribution)
Sample a parameter for a given distribution.
This method is called only for the parameters not contained in the search space returned by SampleRelative(Study, FrozenTrial, OrderedDict<string, BaseDistribution>). This method is suitable for sampling algorithms that do not use relationship between parameters such as random sampling and TPE.
public override object? SampleIndependent(Study study, FrozenTrial trial, string paramName, BaseDistribution paramDistribution)
Parameters
studyStudyStudy supplying trial history, directions, and storage.
trialFrozenTrialCurrent trial snapshot; copy it before modifying its contents.
paramNamestringName of the parameter being suggested.
paramDistributionBaseDistributionDistribution defining the parameter's domain and scale.
Returns
- object
A value in the distribution's external representation, including null categorical choices.
Exceptions
- ValueError
The study has multiple objectives, but SAASBO requires a scalar objective.
SampleRelative(Study, FrozenTrial, OrderedDict<string, BaseDistribution>)
Sample parameters in a given search space.
The trial invokes this method lazily when it first needs relative parameters and caches the result for its remaining suggestions. This method is suitable for sampling algorithms that use relationship between parameters such as Gaussian Process and CMA-ES.
public override OrderedDict<string, object?> SampleRelative(Study study, FrozenTrial trial, OrderedDict<string, BaseDistribution> searchSpace)
Parameters
studyStudyStudy supplying trial history, directions, and storage.
trialFrozenTrialCurrent trial snapshot; copy it before modifying its contents.
searchSpaceOrderedDict<string, BaseDistribution>Distributions returned by InferRelativeSearchSpace(Study, FrozenTrial).
Returns
- OrderedDict<string, object>
Jointly sampled external parameter values; omitted parameters use independent sampling.
Exceptions
- ValueError
The study has multiple objectives, but SAASBO requires a scalar objective.