Skip to main content

StudyView

Struct StudyView 

pub struct StudyView { /* private fields */ }
Expand description

A consistent, incrementally maintained view of a study.

One storage sync per decision point, incremental afterwards: the study loop calls Storage::sync once, folds the deltas in with apply, and hands the result to the sampler and the scheduler. That is what removes Optuna’s per-suggest chattiness and its N+1 re-reads by construction.

§Visibility rules (architecture invariants, not preferences)

  • visible — what a sampler may learn from: Complete and Pruned trials. Failed trials are invisible. Their records survive (with parameters and error text) but a crashed run must not teach the sampler that its parameters were bad.
  • running — Running and Paused trials. Pending-trial awareness is a loop-level guarantee, so constant-liar and qEI-style repulsion work for every sampler, not just the ones that remembered to ask.
  • Iteration is always by trial number, so two workers folding the same deltas in a different order still see the same sequence.

Implementations§

§

impl StudyView

pub fn new( study: StudyId, directions: Vec<Direction>, space: Option<SpaceSchema>, ) -> Self

An empty view positioned at Cursor::BEGIN.

pub const fn study(&self) -> StudyId

The study this view belongs to.

pub fn directions(&self) -> &[Direction]

The objective directions.

pub const fn space(&self) -> Option<&SpaceSchema>

The statically declared search space, if the study has one.

pub const fn cursor(&self) -> Cursor

The position this view has folded up to.

pub fn all(&self) -> impl Iterator<Item = &FrozenTrial>

Every trial the view knows about, ordered by trial number.

pub fn len(&self) -> usize

The number of known trials.

pub fn is_empty(&self) -> bool

true if no trial is known yet.

pub fn get(&self, number: TrialNumber) -> Option<&FrozenTrial>

The trial with this number, if the view has it.

pub fn visible(&self) -> impl Iterator<Item = &FrozenTrial>

The trials a sampler may learn from: Complete and Pruned.

Failed trials are deliberately excluded.

pub fn completed(&self) -> impl Iterator<Item = &FrozenTrial>

The successfully completed trials only.

pub fn running(&self) -> impl Iterator<Item = &FrozenTrial>

The in-flight trials: Running and Paused.

A paused trial still occupies a slot in the population and still holds a checkpoint, so parallel-repulsion samplers must see it.

pub fn failed(&self) -> impl Iterator<Item = &FrozenTrial>

The failed trials.

Not visible to samplers; exposed here because reports, retry policies and humans all want them.

pub fn best(&self) -> Option<&FrozenTrial>

The best trial of a single-objective study, feasibility first.

Returns None for a multi-objective study — there is no total order on a Pareto front, and silently picking the first objective would be a lie. Pruned trials compete on their last intermediate value (FrozenTrial::objective_value). Ties go to the lower trial number, so the answer does not depend on completion order.

§Constraints outrank the objective

Ranking is constrained_dominates, the same relation pareto_front uses, so a trial that recorded constraints (TrialCtx::record_constraints) and satisfies them beats one that violates them whatever their objectives say, and two infeasible trials are ranked by total violation. That is what “typed constraints, feasibility-first best” means (Constraints): a constrained study whose best was an illegal configuration would be reporting an answer the user cannot use. A study where no trial recorded constraints is ranked by the objective alone, exactly as before — the relation degenerates to Direction::is_better for one objective.

§A view synced from storage carries no constraints

The same caveat pareto_front carries, and for the same reason: FrozenTrial::constraints is empty on a snapshot read straight from storage (the frozen Storage seam cannot write it), so this method is constraint-blind unless the caller has attached them. Study::best_trial is the constrained-aware entry point.

pub fn pareto_front(&self) -> Vec<&FrozenTrial>

The Pareto front: the non-dominated set of trials a sampler may learn from.

The multi-objective answer to best, which returns None for a multi-objective study on purpose — there is no total order on a front, so the honest answer is a set (read the front, not the best). Trials come back in trial-number order, so the answer does not depend on completion order.

Candidates are visible trials with an objective vector of the right arity (a pruned trial competes on its last intermediate, as everywhere else); a trial with a NaN objective is dominated by every well-formed one and can never reach the front (dominates). Ranking is constrained_dominates over each trial’s constraints, so a feasible trial outranks an infeasible one.

§A view synced from storage carries no constraints

FrozenTrial::constraints is empty on a snapshot read straight from storage (the frozen Storage seam cannot write it), so this method is constraint-blind unless the caller has attached them. Study::pareto_front is the constrained-aware entry point: it attaches each trial’s recorded values first. For an unconstrained study the two agree exactly.

§Complexity

O(M·N²) in the number of comparable trials — a reporting call, not a per-suggest one.

pub fn nadir_point(&self) -> Option<Vec<f64>>

The nadir point of the comparable trials: the componentwise worst objective value observed.

The conventional default reference for hypervolume. It is taken over every comparable trial rather than over the front alone, so it is the more stable of the two choices; a hypervolume history must still be measured against one fixed reference (see nadir_point). None when nothing is comparable yet.

pub fn hypervolume(&self, reference: &[f64]) -> Result<f64>

The hypervolume the front covers, bounded by reference.

Computed over the pareto_front, keeping only its feasible members: an infeasible solution has not earned the objective space it appears to cover. (It reaches the front at all only when nothing feasible exists.)

§Errors

Everything hypervolume reports — notably Error::InvalidSpace above MAX_EXACT_HYPERVOLUME_DIM objectives.

pub fn apply(&mut self, deltas: Vec<TrialDelta>, cursor: Cursor) -> Result<()>

Folds storage deltas in and advances the cursor.

Each delta carries a whole-trial snapshot, so merging is a replace keyed by trial number (which is a stable bijection with the trial id inside one study).

§Errors

Error::Conflict if cursor moves backwards. A cursor is monotone by contract; a backwards move means the caller mixed up two storages or two studies, and silently accepting it would corrupt the view. Applying the same cursor twice is allowed (a no-op resync).

pub fn upsert(&mut self, trial: FrozenTrial)

Inserts or replaces a trial without touching the cursor.

The escape hatch for building a view by hand — tests, and the study loop’s own just-created trial, which it already holds and need not wait for a sync to see.

Trait Implementations§

§

impl Clone for StudyView

§

fn clone(&self) -> StudyView

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for StudyView

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
§

impl<'de> Deserialize<'de> for StudyView

§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
§

impl PartialEq for StudyView

§

fn eq(&self, other: &StudyView) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
§

impl Serialize for StudyView

§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
§

impl StructuralPartialEq for StudyView

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more