SurrogateManager#
Where Surrogate handles only fit/predict, SurrogateManager coordinates model fitting and batched prediction. An acquisition configured on the optimizer scores the returned predictions.
Optimizer.set_surrogate_manager() is a separate top-level swap point from Optimizer.set_surrogate() (a simplified version that wraps a Surrogate in LocalSurrogateManager).
SurrogateManager’s role#
SurrogateManager’s abstract method is predict(); fitting and generation hooks have default implementations.
predict(candidates_x, archive, ctx=None, *, refit=True) -> SurrogatePrediction (abstract): Predicts the candidates.
With refit=True (the default), the surrogate is retrained before prediction.
fit(archive, ctx=None) -> None: A no-op by default.
A pre-fit hook meant to be called once before a series of predict(..., refit=False) calls, used when the archive does not change.
last_accuracy: SurrogateAccuracy | None (class attribute): The accuracy metric computed by the most recent fit.
Covered in detail in Surrogate accuracy evaluation and dynamic switching.
on_generation_end(gen, archive, ctx=None) / with_on_generation_end(fn): An end-of-generation hook.
Also extensible via the same copy-and-chain approach.
The values received and returned at the two boundaries are:
Boundary |
Receives |
Returns |
Main users |
|---|---|---|---|
Stage compatibility boundary |
|
|
compatibility Stages, sequential compatibility runtime |
graph-native boundary |
|
Features the Surrogate can fit and predict, plus predictions |
Components, Compiler, structured runtime |
The current vector-form Manager API on the Stage compatibility path uses fit(archive, ctx=None) and predict(candidates_x: np.ndarray, archive, ctx=None, *, refit=True) -> SurrogatePrediction.
The current API also includes constructors such as GlobalSurrogateManager(surrogate, training_set=None, accuracy_evaluator=None), LocalSurrogateManager(surrogate, training_set=None, accuracy_evaluator=None), and PairwiseSurrogateManager(surrogate, training_set=None, n_ref=10).
In the structured runtime, the Surrogate input boundary is GenomeBatch → FeatureEncoder → FeatureBatch → Surrogate.
FeatureEncoder performs semantic conversion and determines the features the surrogate can learn. It differs from a space capability such as SamplingService.
In the current implementation, the SurrogateManager contract declares ServiceRequirement("FeatureEncoder"), and VectorSpace registers a default encoder as a service, so numeric vector spaces resolve without extra configuration. ObjectSpace, PermutationSpace, and SequenceSpace raise an error unless the user provides a FeatureEncoder. The user decides what to pass to the surrogate.
Built-in SurrogateManagers#
Class |
Approach |
|---|---|
|
Fits globally once over the entire archive and predicts every candidate at once |
|
Fits locally per candidate, using its k nearest neighbors |
|
Combines named prediction channels |
|
Predicts win rates with a pairwise-comparison surrogate |
GlobalSurrogateManager(surrogate, training_set=None, accuracy_evaluator=None) uses ArchiveObjectiveSet() when training_set is omitted.
LocalSurrogateManager(surrogate, training_set=None, accuracy_evaluator=None) uses KNNObjectiveSet(n_neighbors=50) when training_set is omitted.
n_neighbors isn’t a constructor argument of LocalSurrogateManager itself — it’s a parameter of the default training_set.
Because it reuses and refits the same surrogate instance across candidates, it isn’t thread-safe.
CompositeSurrogateManager(managers) calls each named manager’s predict() and returns a multi-channel SurrogatePrediction.
Use CompositeAcquisition to evaluate one acquisition per channel and combine the resulting score arrays with product_combine or rank_weighted_combine(weights=None).
PairwiseSurrogateManager(surrogate, training_set=None, n_ref=10) uses PairwiseComparisonSet() when training_set is omitted.
See TrainingSet and Surrogate accuracy evaluation and dynamic switching, respectively, for details on each manager’s training_set/accuracy_evaluator arguments.
Archive-based acquisitions#
These acquisitions score candidate geometry directly against the archive and do not require a surrogate prediction.
Class |
Parameters |
Meaning of the score |
|---|---|---|
|
|
Higher when the average distance to the k nearest archive points is larger |
|
|
The reciprocal of the ε-neighborhood density |
|
None |
The minimum distance among candidates plus the minimum distance to the archive |
Extending SurrogateManager and archive-based acquisitions#
If you need a custom prediction scheme, subclass SurrogateManager and implement predict().
For a custom archive-based criterion, subclass AcquisitionFunction and implement its evaluation contract.
import numpy as np
from saealib import AcquisitionFunction, AcquisitionResult
class ConstantAcquisition(AcquisitionFunction):
"""A minimal acquisition that assigns every candidate the same score."""
def evaluate(self, candidates_x, prediction, archive, ctx=None, *, prepared=None):
return AcquisitionResult(scores=np.ones(len(candidates_x)))
References#
saealib.NoveltyAcquisitionsaealib.InverseDensityAcquisitionsaealib.MaximinDistanceAcquisition