Trait Scheduler
pub trait Scheduler: Send + Sync {
// Required method
fn on_report(
&self,
study: &StudyView,
trial: &TrialMeta,
step: u64,
values: &[f64],
) -> Result<Decision>;
// Provided methods
fn scripted_capability(&self) -> ScriptedSchedulerCapability { ... }
fn fan_report_mode(&self) -> FanReportMode { ... }
fn on_trial_end(
&self,
study: &StudyView,
trial: &FrozenTrial,
) -> Result<Vec<Command>> { ... }
fn resume_candidates(&self, study: &StudyView) -> Result<Vec<Command>> { ... }
fn state(&self) -> Result<Option<SchedulerState>> { ... }
fn restore_state(&self, blob: &SchedulerState) -> Result<()> { ... }
}Expand description
Decides the fate of running trials.
Object-safe and Send + Sync: the study holds Arc<dyn Scheduler> and
calls it from every worker.
Required Methods§
fn on_report(
&self,
study: &StudyView,
trial: &TrialMeta,
step: u64,
values: &[f64],
) -> Result<Decision>
fn on_report( &self, study: &StudyView, trial: &TrialMeta, step: u64, values: &[f64], ) -> Result<Decision>
Called on every intermediate report.
values is a slice: a multi-objective study reports every objective at
every step, and a scheduler is entitled to look at all of them.
step is in the study’s declared
ResourceUnit.
Implementations must be cheap — this runs inside the objective’s inner loop — and must not block on storage beyond the view they were handed.
§Errors
Error::Scheduler on failure. The
study loop treats an error as “no decision”, not as a trial failure.
Provided Methods§
fn scripted_capability(&self) -> ScriptedSchedulerCapability
fn scripted_capability(&self) -> ScriptedSchedulerCapability
Declares whether scripted ask/tell can recreate this scheduler.
External and custom schedulers fail closed by default. A scheduler may
opt into the narrow Nop marker only when its behavior is exactly the
built-in NopScheduler; the marker is persisted with the study at
creation so a later CLI process can validate it before loading work.
fn fan_report_mode(&self) -> FanReportMode
fn fan_report_mode(&self) -> FanReportMode
Declares whether this scheduler accepts aligned aggregate fan reports.
The default is fail-closed so an external scheduler cannot silently lose every report when a study’s fan width is greater than one.
fn on_trial_end(
&self,
study: &StudyView,
trial: &FrozenTrial,
) -> Result<Vec<Command>>
fn on_trial_end( &self, study: &StudyView, trial: &FrozenTrial, ) -> Result<Vec<Command>>
Called once a trial reaches a terminal state.
The place where rung tables are updated and where a promotion decides
to wake a paused trial (by returning
Command::ResumeTrial). The default does nothing.
§What is visible for a fanned trial
For a study running a multi-seed SeedProtocol,
the trial handed here carries its per-seed fan in
FrozenTrial::replicates — the
study loop fills it before this call. So a scheduler can read the
just-finished trial’s per-seed samples (values and intermediates) at
trial end, which is what a fan-aware decision needs. Two limits are
deliberate and unchanged by this: the other trials reachable through
study still have an empty replicates (the StudyView stays
fan-agnostic so the sampler hot path pays nothing for fans), and
per-replicate reports are captured and only aligned aggregate reports
are sent to on_report. A between-trials fan
comparison (this trial’s fan against an incumbent’s) therefore still
needs the incumbent’s fan, read caller-side through
Study::trial_fan. For a single-seed
study replicates is empty, exactly as before.
§Errors
Error::Scheduler on failure.
fn resume_candidates(&self, study: &StudyView) -> Result<Vec<Command>>
fn resume_candidates(&self, study: &StudyView) -> Result<Vec<Command>>
Paused trials worth waking on a freed work slot, in priority order.
The slot-driven companion to on_trial_end’s
resume: the study loop consults this in ask
each time it is about to start fresh work, before creating a new trial.
on_trial_end only fires on a terminal
completion, so a scheduler
that pauses trials which no later completion ever thaws (freeze-thaw’s
exact regime — a slot is freed by a pause, not a finish) would otherwise
strand its frozen winners: the paused pool is never drained. Returning the
paused trial it would thaw here drains it on the next freed slot.
A returned Command::ResumeTrial whose target is no longer
Paused — another worker won the wake
under parallelism — is a safe
no-op in the loop, so this may be called concurrently and need not
re-check liveness itself.
The default returns nothing, correct for every scheduler that never emits
Decision::Pause (NopScheduler, the pruners, Pbt): with no
paused trials to drain there is nothing to wake on a freed slot, and the
loop’s fresh-trial path is unchanged.
§Errors
Error::Scheduler on failure.
fn state(&self) -> Result<Option<SchedulerState>>
fn state(&self) -> Result<Option<SchedulerState>>
The scheduler’s persistable state, if it has any.
Written to Scope::Scheduler. ASHA’s rung
tables live here as typed data — not as attribute-string hacks.
§Errors
Error::Scheduler if the state
cannot be serialized.
fn restore_state(&self, blob: &SchedulerState) -> Result<()>
fn restore_state(&self, blob: &SchedulerState) -> Result<()>
Restores a scheduler from a previously persisted blob.
The framework calls this once, when a study is constructed or
resumed, before any decision: it reads
Scope::Scheduler and, if a blob is present,
hands it here so a stateful scheduler (a PBT population it carries, a
promotion ladder it must not recompute) continues from where the last
handle left off. State lives behind interior mutability, so &self
suffices.
The default is a no-op, correct for every built-in scheduler
(NopScheduler, MedianPruner, AshaPruner, HyperbandPruner,
Patient, WilcoxonPruner): they are stateless-by-recomputation
(rung tables are rebuilt from the StudyView), persist nothing
(state is None, so this is never called with a
blob of theirs), and would be broken by a restore.
§Errors
Error::Scheduler if the blob cannot
be decoded.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".