Struct FrozenTrial
pub struct FrozenTrial {Show 18 fields
pub id: TrialId,
pub number: TrialNumber,
pub study: StudyId,
pub state: TrialState,
pub params: Assignment,
pub distributions: BTreeMap<String, Distribution>,
pub values: Option<Vec<f64>>,
pub intermediates: Vec<(u64, Vec<f64>)>,
pub created_at_millis: u64,
pub finished_at_millis: Option<u64>,
pub heartbeat_millis: Option<u64>,
pub retries: u32,
pub error: Option<String>,
pub worker: Option<WorkerId>,
pub parent: Option<TrialId>,
pub replicates: Vec<ReplicateResult>,
pub constraints: Vec<f64>,
pub user_attrs: UserAttrs,
}Expand description
The immutable snapshot of a trial.
This is what storage hands to samplers, schedulers and the study view. It is a value: cloning one and holding it while other workers advance the study is safe and is the intended usage.
Objective values are a Vec<f64> from day one — multi-objective is not a
later bolt-on
— and neither are intermediate
values, which are keyed by the study’s declared
ResourceUnit.
Fields§
§id: TrialIdStorage-global identity.
number: TrialNumberPer-study ordinal; the determinism key (see crate::id).
study: StudyIdThe owning study.
state: TrialStateLife-cycle state.
params: AssignmentThe sampled parameters, sorted by name.
distributions: BTreeMap<String, Distribution>The distribution each parameter was sampled from, as recorded at sampling time. Kept per trial because a define-by-run space may drift between trials.
values: Option<Vec<f64>>The final objective values, once known.
intermediates: Vec<(u64, Vec<f64>)>Intermediate reports as (step, values), in report order.
created_at_millis: u64Creation time, from the study’s Clock.
finished_at_millis: Option<u64>Completion time, once finished.
heartbeat_millis: Option<u64>Last heartbeat; used to detect a dead worker.
retries: u32How many times this trial has been retried.
Written by the fail-over path, not by the study loop. When a policy
requeues a trial it took from a dead worker, the
ReplacementSpec handed to the
finalization outbox carries the failed trial’s count plus one, and the
backend stamps that onto the replacement it creates. A trial nobody
failed over therefore reads 0, and all backends agree.
This is only the count. Which trial replaced which, and why, is
recorded by the study loop as typed state — see
RetryRecord.
error: Option<String>Why the trial failed, if it did. Preserved for failed trials on
purpose. Written through
Storage::set_error.
worker: Option<WorkerId>The worker that owns (or last owned) the trial.
Ownership moves with the transition into
Running. Whoever starts a trial — or restarts
one after a fail-over — is stamped here, and a worker that merely
adjudicates somebody else’s stale trial is not, which is what stops a
sweeping process from being recorded as the owner of every trial it
failed over.
TrialLifecycleStorage::claim
stamps the claimant for the same reason. A backend that has a worker
identity also stamps it at creation, so a freshly created trial names the
process that created it rather than reading None.
It is not a cross-backend invariant, because that identity belongs to
the handle rather than to the history:
JournalStorage takes it from
JournalOptions::worker and
atune::storage::SqliteStorage from SqliteOptions::worker, while
MemStorage has no identity at all — every
handle to it is the same process — so it stamps only the claim, and a
trial it created but never leased stays None. One sequence of
operations replayed against two backends may therefore differ here
legitimately, which is why the differential suites blank the field before
comparing records.
parent: Option<TrialId>The trial this one was forked from, copied verbatim from the
TrialTemplate it was created with.
Purely descriptive in M1: nothing samples from it, filters on it or follows it, because no M1 scheduler produces a fork. It exists now because PBT-style lineage (M4) has to be recorded when the child is created, and the journal format (M2) would otherwise have to change to grow the field later.
replicates: Vec<ReplicateResult>The per-seed sub-results of a multi-seed fan.
A trial run under a SeedProtocol
with a fan width above one evaluates the same configuration under
several seeds; each replicate carries its index, its seed and its own
objective value(s). The trial-level values is
the Aggregate over these — the one
number the sampler ranks — while the per-seed values are kept here and
never lost to aggregation.
The authoritative fan lives in a per-trial state blob — the frozen
Storage trait has no method to write it onto
the record, so the study loop persists it with
put_state on the independently
keyed fan_scope (blob kind
FAN_BLOB_KIND). This field is
therefore empty on a snapshot read straight from storage — the
sampler’s hot path pays nothing for it and needs only the aggregate — and
is filled on demand by the fan-aware readers on
Study (see
Study::trial_fan). It is data, not the
trait: it round-trips through serde and defaults to empty, so a trial
written before the protocol existed reads back as a single implicit
replicate (an empty fan).
constraints: Vec<f64>The typed constraint values the trial recorded, if any — see Constraints.
≤ 0 is satisfied, > 0 is violated — the convention the whole
feasibility-first machinery rests on
(pareto::constraint). An objective records
them with
TrialCtx::record_constraints;
Nsga2 ranks with them through
constrained_dominates, so a
feasible trial outranks an infeasible one whatever their objectives say.
The authoritative values live in a per-trial state blob, exactly as
the replicate fan does and for the same reason — the frozen
Storage trait has no method to write them
onto the record — so this field is empty on a snapshot read straight
from storage. The study loop fills it on the finishing trial before
after_trial, which is the
sampler’s channel, and
Study::trial_constraints /
Study::pareto_front fill it on
demand for a reader. It round-trips through serde and defaults to empty,
so a trial written before constraints existed reads back as one that
declared none.
user_attrs: UserAttrsUser-owned JSON metadata, keyed deterministically by string.
The atune: namespace is reserved for generated interoperability
metadata and cannot be written through public setters.
Implementations§
§impl FrozenTrial
impl FrozenTrial
pub fn new(
id: TrialId,
number: TrialNumber,
study: StudyId,
created_at_millis: u64,
) -> Self
pub fn new( id: TrialId, number: TrialNumber, study: StudyId, created_at_millis: u64, ) -> Self
A freshly created, Waiting trial with no parameters.
pub fn last_intermediate(&self) -> Option<&(u64, Vec<f64>)>
pub fn last_intermediate(&self) -> Option<&(u64, Vec<f64>)>
The most recent intermediate report, if any.
pub fn objective_value(&self) -> Option<&[f64]>
pub fn objective_value(&self) -> Option<&[f64]>
The value a sampler should treat as this trial’s objective.
| State | Value |
|---|---|
Complete | the recorded values |
Pruned | the recorded values if present, else the last intermediate |
| anything else | None |
The pruned rule is Optuna’s, and it is what makes successive-halving schedulers correct: a trial stopped at rung k still contributes the performance it had reached.
use atune_core::id::{StudyId, TrialId, TrialNumber};
use atune_core::trial::{FrozenTrial, TrialState};
let mut trial = FrozenTrial::new(TrialId::new(1), TrialNumber::new(0), StudyId::new(0), 0);
trial.intermediates.push((10, vec![0.7]));
trial.intermediates.push((20, vec![0.4]));
trial.state = TrialState::Pruned;
assert_eq!(trial.objective_value(), Some(&[0.4][..]));pub fn declared_constraints(&self) -> Option<&[f64]>
pub fn declared_constraints(&self) -> Option<&[f64]>
The trial’s constraint values as the pareto
comparison layer wants them: None when the trial declared none.
An empty list is not a declaration — see
Point::new.
pub fn single_objective_value(&self) -> Option<f64>
pub fn single_objective_value(&self) -> Option<f64>
The single objective value, for single-objective studies.
None if the trial has no objective value yet, or if it has more than
one (use objective_value).
pub const fn is_finished(&self) -> bool
pub const fn is_finished(&self) -> bool
true if the trial is in a terminal state.
Trait Implementations§
§impl Clone for FrozenTrial
impl Clone for FrozenTrial
§fn clone(&self) -> FrozenTrial
fn clone(&self) -> FrozenTrial
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more