Struct StudyBuilder
pub struct StudyBuilder { /* private fields */ }Expand description
Builds a Study, injecting the handles it drives.
Every seam is injected and every default is stated: a study never constructs a clock, reaches for the system time, or picks a storage backend on the caller’s behalf.
| Knob | Default |
|---|---|
| storage | MemStorage — in memory, nothing survives the process |
| sampler | Random |
| scheduler | NopScheduler — nothing is ever pruned |
| clock | SystemClock with the system feature, otherwise a ManualClock frozen at 0 |
| observer | none (structured tracing events are still emitted) |
| worker | WorkerId(0) |
| budget | Budget::unbounded |
| parallelism | 1 |
| retry policy | RetryPolicy::none |
use atune_core::clock::ManualClock;
use atune_core::sampler::Grid;
use atune_core::space::{Distribution, ParamSpec, SpaceSchema};
use atune_core::study::{Study, StudyConfig};
use std::sync::Arc;
let space = SpaceSchema::new([
ParamSpec::new("opt", Distribution::cat_labels(["adam", "sgd"])?)?,
])?;
let study = Study::builder()
.sampler(Arc::new(Grid::new(space.clone())?))
.clock(Arc::new(ManualClock::new(0)))
.create(StudyConfig::new("grid").with_space(space))?;
assert_eq!(study.trial_count()?, 0);Implementations§
§impl StudyBuilder
impl StudyBuilder
pub const fn new() -> Self
pub const fn new() -> Self
Starts a builder with every seam at its default.
pub fn storage(self, storage: Arc<dyn Storage>) -> Self
pub fn storage(self, storage: Arc<dyn Storage>) -> Self
Uses storage as the study’s backend.
In-memory by default: a study built without this call runs against a
fresh MemStorage, which is right for a
first program, a test and a study whose results are read before the
process exits — and wrong for anything durable, resumable or shared
between processes. Call this for those.
A backend used for Study-driven trial execution must expose
TrialLifecycleStorage through Storage::trial_lifecycle. The base
Storage trait alone supports direct CRUD and observation, but
Study::ask and Study::optimize return Error::Unsupported when
the atomic lifecycle capability is absent.
Shares template-queue ownership with source for a compatible load.
The queue is the only source state retained. A subsequent
load must name the same study and use the exact same
storage Arc; otherwise it fails before reading or recovering the
backend. This method is intentionally incompatible with
create, which rejects it before creating anything.
Shares finalization admission with source for a compatible load.
This is an explicit companion to share_queue_from
for adapters that rebuild a handle while reusing the same underlying
mutable sampler and scheduler state. Transparent wrappers may delegate
to those seams; this method does not select or verify the seam objects.
It shares only the local finalization runtime, and queue ownership
remains independent unless share_queue_from
is also used. A subsequent load must name the same study
and use the exact same storage Arc; otherwise it fails before reading
or recovering the backend. This method is intentionally incompatible
with create, which rejects it before creating anything.
pub fn scheduler(self, scheduler: Arc<dyn Scheduler>) -> Self
pub fn scheduler(self, scheduler: Arc<dyn Scheduler>) -> Self
Uses scheduler instead of NopScheduler.
pub fn observer(self, observer: Arc<dyn StudyObserver>) -> Self
pub fn observer(self, observer: Arc<dyn StudyObserver>) -> Self
Sends typed lifecycle events to observer.
Delivery is synchronous with each emitting operation; parallel trials may invoke the observer concurrently. Observer failures never become study errors. Implementations should enqueue lightweight notifications and perform blocking work elsewhere.
pub const fn worker(self, worker: WorkerId) -> Self
pub const fn worker(self, worker: WorkerId) -> Self
Names this handle among the study’s workers.
Two workers sharing a study must not share an id: it is what a lease
names as its owner, what
FrozenTrial::worker records, and
therefore what tells a fail-over sweep — and a human reading the study —
whose trial a stale heartbeat belongs to. The identity is readable back
through Study::worker, and every lease this handle claims stamps it
onto the trial record.
It names the handle, not the Storage behind it. A backend that
carries its own worker option
(JournalOptions::worker,
SqliteOptions::worker) stamps that one on the writes it performs
itself — see
FrozenTrial::worker for the full
rule.
pub const fn parallelism(self, n: usize) -> Self
pub const fn parallelism(self, n: usize) -> Self
Evaluates n trials at once.
n <= 1 is sequential and spawns nothing, which is the only form
available on wasm32-unknown-unknown; n > 1 needs the system
feature and is rejected by Study::optimize without it.
pub const fn retry_policy(self, policy: RetryPolicy) -> Self
pub const fn retry_policy(self, policy: RetryPolicy) -> Self
Re-enqueues failed configurations according to policy.
pub const fn seed_protocol(self, protocol: SeedProtocol) -> Self
pub const fn seed_protocol(self, protocol: SeedProtocol) -> Self
Evaluates each trial as a multi-seed fan under protocol.
The default is SeedProtocol::new (single-seed, no fan), which takes
the ordinary one-evaluation-per-trial path. A protocol with a fan width
above one turns a trial into k replicates of the same configuration
under k seeds, aggregated into the sampler-visible objective
(multi-seed
protocol). See
Study::reevaluate for the final held-out-seed stage.
§This applies only when create-ing
The protocol is a persisted property of the study
(StudyConfig::seed_protocol): create writes what you set here into
the config it stores, and the study evaluates trials under it. On
load this setting is ignored — a resumed study
takes the protocol from its stored config, so it can never silently mix a
fanned study’s aggregate objective with fan-free raw draws. Passing a
protocol here and then load-ing is therefore a no-op, not an override.
pub fn policy(self, policy: SpacePolicy) -> Self
pub fn policy(self, policy: SpacePolicy) -> Self
Declares this study’s growth policy — which parameters are open, and how (§6.4 of the open-search-spaces plan).
Create-time plumbing, exactly as sampler is:
create persists one
Scope::Named slot per named
parameter, and load ignores this field — a
resumed handle reads the stored slots on demand, so the storage is the
single source of truth (§7.1). StudyConfig is untouched.
Every Open is validated at
create
(§7.3, §7.4, §9.6), and a policy naming a declared parameter must
seed exactly the declared distribution — the range has one home, not
two.
pub fn create(self, cfg: StudyConfig) -> Result<Study>
pub fn create(self, cfg: StudyConfig) -> Result<Study>
Creates a new study in storage.
§Errors
Error::InvalidSpace if the configuration does not validate;
Error::Conflict if the backend enforces unique names and the name
is taken; Error::Storage on a backend fault.
pub fn load(self, id: StudyId) -> Result<Study>
pub fn load(self, id: StudyId) -> Result<Study>
Loads an existing study — the resume path.
The configuration (and therefore the seed and the
SeedProtocol) comes from
storage, so a second handle reproduces the first one’s sampling and its
per-trial fan: a study created with a k-seed fan resumes as a k-seed
fan even from a plain builder that set no protocol. Any protocol passed to
this builder via seed_protocol is
ignored. Trial numbering continues where storage left it, and the trial
budget counts the trials already in the study.
Writable load also recovers durable finalization outboxes before it
returns. If another owner holds the study-wide finalization claim, a
system build reclaims it only after the complete claim observation
stays unchanged for three seconds of local monotonic time, using
nominal 100 ms polling. Without system, held ownership fails immediately
with backpressure. This recovery grace is independent of the Journal’s
append-lock policy.
§Errors
Error::NotFound if the study does not exist; Error::Backpressure
for same-handle admission or an active durable finalization owner;
Error::Unsupported if the backend lacks finalization ownership or
bounded outbox paging;
Error::ResourceLimit if recovery cannot traverse its outbox within
the backend’s supported bounds; Error::Conflict or
Error::Incompatible for invalid persisted state; Error::LeaseLost
if the exact acquisition observation loses a race; or Error::Storage
on a backend fault.