mixle.doe.optimizer module

Ask-tell Bayesian-optimization interface for mixle.doe (WS-E).

A small stateful optimizer object for the common human/experiment-in-the-loop workflow, where the objective is expensive or physical and evaluated outside the loop:

opt = BayesianOptimizer(bounds, acq="ei")
for _ in range(n):
    x = opt.ask()          # next point(s) to evaluate
    y = run_experiment(x)  # ... done by the caller, however slow
    opt.tell(x, y)         # feed the result back
opt.best                   # best (x, y) so far

It holds the observation history and delegates proposals to the functional API (mixle.doe.bayesopt): the first n_init asks come from a space-filling Latin-hypercube design (a GP needs data before it is useful), after which asks are GP-acquisition proposals (ask(q>1) returns a kriging-believer batch). Constrained and multi-objective problems keep their functional drivers (constrained_minimize / multi_minimize).

Every point ask() returns is tracked as pending until a matching BayesianOptimizer.tell() call resolves it – see that method’s docstring for the exact match / duplicate / unsolicited policy.

class BayesianOptimizer(bounds, *, acq='ei', acq_kwargs=None, maximize=False, n_init=None, xi=0.0, n_candidates=512, fit_kwargs=None, seed=None)[source]

Bases: object

Stateful ask-tell wrapper around the GP Bayesian-optimization loop.

ask proposes the next point (or a batch), tell records evaluated observations, and best returns the incumbent. Minimizes by default; set maximize=True to maximize. The acquisition is selected by acq ("ei" / "pi" / "ucb" or any registered name / callable) with per-acquisition parameters in acq_kwargs.

ask() tracks every point it returns as pending (asked, not yet told) until a matching tell() call resolves it. A second, overlapping ask() – issued before an earlier one’s points are told, as in a parallel/async evaluation campaign – folds those still-pending points into the surrogate fit as fantasized (kriging-believer) observations, the same mechanism propose_batch() already uses to keep a single batch’s own picks apart, so acquisition is steered away from proposing them again (MXR-080-0188).

Parameters:
  • bounds (Bounds)

  • acq (str | Acquisition)

  • acq_kwargs (dict[str, Any] | None)

  • maximize (bool)

  • n_init (int | None)

  • xi (float)

  • n_candidates (int)

  • fit_kwargs (dict[str, Any] | None)

  • seed (int | RandomState | None)

property x: ndarray

Return the observed points as an (N, d) array.

property y: ndarray

Return the observed objective values as an (N,) array.

property n_observations: int

Return the number of recorded observations.

property pending: ndarray

Return currently pending (asked, not yet told) points as a (P, d) array.

property n_pending: int

Return the number of currently pending (asked, not yet told) points.

property best: BayesOptResult

Return the incumbent (best observed point) as a BayesOptResult.

ask(q=1)[source]

Return the next point to evaluate as a (d,) array, or a (q, d) batch when q > 1.

The first n_init points come from a space-filling design; subsequent points are GP acquisition proposals (a kriging-believer batch when q > 1). Every returned point is tracked as pending until tell() resolves it; a second, overlapping ask() before any tell() folds still-pending points into the GP fit as fantasized observations so it is steered away from proposing them again (see the class docstring, MXR-080-0188).

This call is atomic: if it raises (e.g. a GP proposal attempted with zero observations), no internal state changes, so a failed call is always safe to retry.

Parameters:

q (int)

Return type:

ndarray

tell(x, y)[source]

Record one or more evaluated observations; returns self for chaining.

x is a (d,) point or (m, d) batch and y the matching scalar or (m,) values. Every point must be finite and within bounds, and every outcome must be finite – a NaN/Inf in either would otherwise poison every downstream GP fit and acquisition score silently. Every point must also match a currently pending proposal, i.e. one this same optimizer’s own ask() actually returned and that has not already been told:

  • a point that was never returned by ask() (an unsolicited observation) is rejected – this is almost always a caller bug (wrong array, a stale point from a different optimizer), so it is rejected by default rather than silently accepted;

  • a point that was already told (whether repeated within this same call or told in a previous one) is rejected as a duplicate – tell() is write-once per point, there is no “update an existing observation” path.

(MXR-080-0188.) Raises ValueError for any of the above. Validation covers the whole batch before anything is applied, so a batch that is only partly invalid never half-applies.

Parameters:
Return type:

BayesianOptimizer