BADS#
Note
The BADS class implements the Bayesian Adaptive Direct Search (BADS) algorithm.
BADS attempts to solve an unbounded, bounded or nonlinearly constrained optimization (minimization) problem, and is compatible with both noiseless and noisy target functions.
To perform the optimization, first initialize a BADS object and then call bads.optimize() on the instance.
See below for more details on the BADS class methods and interface. The primary entry-points for users are the BADS class, which initializes the algorithm, and the OptimizeResult class, which represents the returned optimization solution. The Basic options may also be useful.
- class pybads.bads.BADS(fun: callable, x0: ndarray = None, lower_bounds: ndarray = None, upper_bounds: ndarray = None, plausible_lower_bounds: ndarray = None, plausible_upper_bounds: ndarray = None, non_box_cons: callable = None, options: dict = None, *, gamma_uncertain_interval: float = None, precomputed_evaluations: tuple = None)[source]#
BADS Constrained optimization using Bayesian Adaptive Direct Search.
- BADS attempts to solve problems of the form:
\(\mathtt{argmin}_x f(x)\) subject to: lower_bounds \(<= x <=\) upper_bounds, and optionally \(C(x) <= 0\)
Initialize a
BADSobject to set up the optimization problem, then runoptimize(). See the examples for more details under the examples directory.- Parameters:
- funcallable
The target function to minimize.
funtakes a pointx, a one-dimensional array of all the variables (the fixed ones included), and returns its value as a finite real number; withoptions['specify_target_noise'], it returns a tuple(f, sd), the value and the SD of its noise atx. In case the target functionfunrequires additional data/parameters, they can be handled using an anonymous function. For example:fun_for_pybads = lambda x: fun(x, data, extra_params), wherefunis the function to optimize, anddataandextra_paramsare given in the outer scope.- x0np.ndarray, optional
Starting point for the optimization, a single point of
Delements, of shape(D,)or(1, D). If not specified orNone, or if an element is not finite (nan,infor-inf) at a variable that is not fixed (see below), the starting pointx0is drawn at random, over the variables that are not fixed, inside the plausible box betweenplausible_lower_boundsandplausible_upper_bounds(see below). Withnon_box_cons, a point that violates the constraints is drawn again, up to 1000 draws in all.- lower_bounds, upper_boundsnp.ndarray, optional
lower_bounds(lb) andupper_bounds(ub) define a set of strict lower and upper bounds for the coordinate vector,x, so that the unknown function has support onlb<=x<=ub. If scalars, the bound is replicated in each dimension. UseNoneforlbandubif no bounds exist. Setlb[i] = -infif the i-th coordinate is unbounded below, andub[i] = infif it is unbounded above (while other coordinates may be bounded). Note that iflband/orubcontain infinite bounds, the respective values ofplband/orpubneed to be specified (see below). By defaultNone.- plausible_lower_bounds, plausible_upper_boundsnp.ndarray, optional
Specifies a set of
plausible_lower_bounds(plb) andplausible_upper_bounds(pub) such thatlb<=plb<pub<=ubat each variable that is not fixed (see below). Bothplbandpubneed to be finite, and are replicated in each dimension if scalars. If not specified,plbislbandpubisub:plbneeds to be specified whenlbhas an infinite bound, andpubwhenubhas one.plbandpubrepresent a plausible range, which should denote a region where the global minimum is expected to be found. As a rule of thumb, setplausible_lower_boundsandplausible_upper_boundssuch that there is > 90% probability that the minimum is found within the box (where in doubt, just setplb = lbandpub = ub).A variable whose four bounds are equal is fixed at their value;
x0holds that value there, or a non-finite value. BADS optimizes the other variables, and the defaults of the options that depend on the number of variables count only those. The points you pass to BADS or receive from it hold all the variables, the fixed ones at their values: those thatfun,non_box_consandoptions['output_fcn']receive, the result’sxandx0, the points"x"ofiteration_historyand the points ofprecomputed_evaluations; the indices ofoptions['periodic_vars']count all the variables.- non_box_conscallable, optional
A given non-box constraints function that specifies constraint violations. It takes an array of shape
(N, D), one point per row in the original space, and returns an array of shape(N,)or(N, 1), one value per point, true or positive where the point violates the constraints. For example,lambda x: np.sum(x**2, axis=1) > 1keeps the search inside the unit ball. A feasible region thinner than the mesh can resolve, such as a band narrower than the poll steps, can leave every point of the initial design and of the polls infeasible, and the run can then end early, nearx0, on the stall criterion (an improvement belowtol_funovertol_stall_itersiterations). Reparametrize such a problem so that its feasible region is wide: for the bandabs(x[0] - x[1]) <= w, for example, optimize over(x[0] + x[1]) / 2and(x[0] - x[1]) / w, the latter bounded by -1 and 1 in place of the constraint.- optionsdict, optional
Additional options can be passed as a dict. Please refer to the BADS options page for the default options. If no options are passed, the default options are used. To run BADS on a noisy (stochastic) objective function, set
options['uncertainty_handling']=True. You can help BADS by providing an estimate of the noise.options['noise_size'] = sigmaprovides a global estimate of the SD of the noise in your problem in a good region of the parameter space. (If not specified, defaultsigma = 1.0). Alternatively, you can specify the target noise at each location withoptions['specify_target_noise']=True. In this case,funis expected to return two values, the estimate of the target atxand an estimate of the SD of the noise atx(see the examples). Ifoptions['uncertainty_handling']is not specified, BADS will determine at runtime if the objective function is noisy, or turn uncertainty handling on withoptions['specify_target_noise']=True. A variable that is periodic, such as an angle, is named inoptions['periodic_vars'], a list of the indices of the variables, from 0, the fixed ones included: its hard bounds, which need to be finite, are its period, and BADS wraps it around them, so thatlbandubare the same point and the variable is optimized across them. To obtain reproducible results of the optimization, setoptions['random_seed']to a fixed integer (seerngbelow).- gamma_uncertain_intervalfloat, optional, keyword-only
With
options['stobads'], the multiplier of the half-width of the uncertainty interval of the Sto-BADS success rule. By defaultNone, which is 1.96. Deprecated along withoptions['stobads'], which is experimental and may be removed in a future release.- precomputed_evaluationstuple, optional, keyword-only
Evaluations of
funmade before the run, for instance by an earlier run, as a tuple (or a list)(X, y), or(X, y, y_sd)withoptions['specify_target_noise'], which requiresy_sd.Xholds one point per row in the original space, of shape(N, D), each within the hard bounds and satisfyingnon_box_cons;yholds the values offunat them andy_sdthe SDs of their noise, both of shape(N,). The values are finite and the SDs positive. BADS uses them as training data of its Gaussian process, and does not evaluate again the points of its initial design that they hold; they do not count towardmax_fun_evals(func_count). The run still starts fromx0: its first incumbent is the best of the points it evaluates itself (pass the best of the evaluations asx0to start there). Unlessoptions['uncertainty_handling']isTrue(oroptions['specify_target_noise']is), a point given twice must have the same value, and is kept once; otherwise each repeat is an observation of its own. By defaultNone, no evaluations.
- Attributes:
- rngnumpy.random.Generator
The generator of every random draw of the run, including the random
x0. It is created with theBADSobject fromoptions['random_seed'], which takes whatnumpy.random.default_rngtakes, such as a non-negative integer or aSeedSequence, or aGenerator, used as given. If the option isNone(default), the generator is derived from NumPy’s global random state, so thatnp.random.seedbefore creating theBADSobject fixes the run. The run does not otherwise draw from NumPy’s global random state: a target that draws from it is not fixed byrandom_seed. On Apple Silicon Macs, two runs with the same seed can end at slightly different points.
- Raises:
- ValueError
When
x0is not specified and neitherplausible_lower_boundsnorlower_boundsis, or neitherplausible_upper_boundsnorupper_boundsis: a missing plausible bound defaults to the hard bound, and the randomx0is drawn between the plausible bounds.- ValueError
When
x0or the bounds fail the checks of their shapes, values and order: for instance anx0of more than one row, plausible bounds that are not finite, bounds out of the orderlb <= plb < pub <= ubat a variable that is not fixed, a finitex0that differs from the value of a fixed variable, or bounds that fix every variable.- ValueError
When
non_box_cons, given an(N, D)array, does not return an array of shape(N,)or(N, 1), or whenx0violates the constraints, or each of the 1000 random draws of it does.- ValueError
When an option has an unknown name or a value that BADS does not take: for instance a
max_fun_evals,max_iterortol_stall_itersthat is neither a positive integer norinf, atol_meshthat is not a positive finite number, anoise_sizethat is not one or two numbers, a value other thanTrueorFalseforuncertainty_handlingor for an option whose default is one of them (plotexcepted), aperiodic_varsthat is not a list of distinct indices of variables with finite bounds, or anf_valsthat holds a finite value, a non-emptyfun_values, oracq_hedge=True, options that are not supported.- ValueError
When
precomputed_evaluationsis not a tuple (or a list) of two or three arrays of the shapes above, of finite values and positive SDs, when it hasy_sdwithoutoptions['specify_target_noise']or lacks them with it, when one of its points lies outside the hard bounds or violatesnon_box_cons, or when a point given twice has two different values where a point is kept once.- ValueError
When
options['random_seed']is a negative integer.- TypeError
When
options['random_seed']is a float that is not a whole number, a string, or another value thatnumpy.random.default_rngdoes not take.
References
[1]Singh, S. G. & Acerbi, L. (2024). “PyBADS: Fast and robust black-box optimization in Python”. Journal of Open Source Software, 9(94), 5694, https://doi.org/10.21105/joss.05694.
[2]Acerbi, L. & Ma, W. J. (2017). “Practical Bayesian Optimization for Model Fitting with Bayesian Adaptive Direct Search”. In Advances in Neural Information Processing Systems 30, pages 1834-1844. (arXiv preprint: https://arxiv.org/abs/1705.04405).
Examples
For BADS usage examples, please look up the Jupyter notebook tutorials in the PyBADS documentation: https://acerbilab.github.io/pybads/examples.html
- optimize()[source]#
Run the optimization on an initialized
BADSobject.BADS starts at
x0and finds a local minimumxof the target functionfun.A history of the optimization problem can be found in the
iteration_historyattribute of theBADSobject.A
BADSobject runs a single optimization: create a new one for each run.- Returns:
- optimize_resultOptimizeResult
Dictionary containing the result of the optimization. See the documentation of the
OptimizeResultclass for more details. For example, retrieve the final solution and its value with the attributesoptimize_result.xandoptimize_result.fval.
- Raises:
- RuntimeError
If
optimizehas already been called on this object, whether or not that run completed.- ValueError
If
funreturns a value that is not a finite real number, or, withoptions['specify_target_noise'], does not return a tuple(f, sd)with a finite positivesd. An error thatfunraises reaches the caller.