Python API¶
The reference for the atune package: every class, every method and every
factory, with the signatures and defaults the extension module actually
carries. It is generated from the installed module, so it cannot describe a
binding that is not there.
The same objects are documented one layer down in the Rust API —
atune.Study is a handle on atune::Study, and the two cannot disagree,
because there is only one implementation.
The wheel uses a mixed package: the import shim and native extension sit beside
the generated typing files atune/__init__.pyi, atune/samplers.pyi,
atune/schedulers.pyi, and the atune/py.typed marker.
Study.trials_dataframe() returns a column mapping: numeric columns are NumPy
arrays, while state and categorical columns are Python lists.
Remote storage is not a Python capability
The Python wheel does not enable the facade's remote feature. Its
storage= argument accepts :memory:, journal paths such as study.atj,
and SQLite paths such as study.db; atune://host:port is not accepted by
create_study or load_study. Use RemoteStorage from Rust or the CLI.
seed= on a sampler raises
atune.samplers.Random(seed=…) and atune.samplers.Tpe(seed=…) keep
Optuna's keyword in their signatures, and passing it raises AtuneError
naming the knob that does work. Determinism comes from the study seed —
atune.create_study(seed=…) — from which every per-trial sampler, scheduler
and objective seed is derived, so a per-sampler seed has nothing left to
govern. It raises rather than being ignored because a keyword that is
accepted and does nothing makes the script run and mean something else.
Four of Optuna's names are accepted for atune's
The four spellings a migrated script most often has to change are accepted
as they stand: create_study(pruner=…) for scheduler=, study_name= for
name= (on atune.load_study too), atune.TrialPruned for
atune.Pruned, and Trial.should_prune() for Trial.should_stop(). The
canonical names are atune's — they are what the reference below, the
stub and the error messages use — and each Optuna spelling is the same
argument, method or class object, not a second one. TrialPruned is
Pruned, so except atune.TrialPruned catches what report raises and
raise atune.TrialPruned() is recorded as a prune.
Passing both spellings of one argument raises rather than one of them
silently winning. Class names are not aliased: Tpe, Nsga2, CmaEs,
Qmc, Asha and Median keep atune's names, so the catalogue stays one
name per concept.
Two Optuna-familiar enums, and where they differ
atune.TrialState and atune.StudyDirection are str enums, so every
member is the lowercase string this binding has always returned:
trial.state == "complete" and trial.state == atune.TrialState.COMPLETE
are both true, and the members work anywhere a label does
(study.get_trials(states=[atune.TrialState.COMPLETE]),
trials_dataframe()'s "state" column). Member names are Optuna's where
the state exists in both — including FAIL, Optuna's name for the state
atune labels "failed". TrialState.PAUSED is atune's own (Optuna has no
paused state), and Optuna's StudyDirection.NOT_SET has no counterpart
here, because an atune study always has a direction.
Where the constructor keywords differ from Optuna's
Where a keyword means exactly what Optuna's means, it is spelled Optuna's
way: CmaEs(sigma0=, popsize=), Median(n_min_trials=) (and
Percentile's), Nsga2(mutation_prob=). Three places deliberately keep
atune's spelling or defaults, because the meaning is not the same:
Wilcoxon(min_pairs=)is a count of paired observations, not Optuna'sWilcoxonPruner(n_startup_steps=)step count;Nsga2(crossover_eta=, mutation_eta=)are sampler keywords where Optuna configures SBX and polynomial mutation through operator objects;Asha(min_resource, max_resource, reduction_factor=3)requires both bounds positionally (Optuna infers them from the first completed trial) and defaults the factor to 3 where Optuna defaults to 4. Passreduction_factor=4for Optuna's ladder.
atune¶
atune
¶
Public Python package for the native :mod:atune.atune extension.
__version__ = '0.1.0'
module-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
AtuneError
¶
Bases: Exception
Base class for every error raised by the atune bindings.
FrozenTrial
¶
An immutable snapshot of a finished (or in-flight) trial, as Python sees it.
Mirrors atune_facade::trial::FrozenTrial. Categoricals cross the storage
boundary by label, never by index (see Search
spaces); reading one back
resolves that label to a Python scalar of the type it was suggested as, under
the rule params documents.
constraints
property
¶
The declared constraint values, or None when this trial has no
constraint declaration.
datetime_complete
property
¶
When the trial finished, as a timezone-aware UTC datetime.datetime.
Optuna's datetime_complete, and None while the trial is unfinished. As
with datetime_start the value is aware
UTC. A pruned or failed trial has one too — it finished, it just did
not complete.
datetime_start
property
¶
When the trial was created, as a timezone-aware UTC datetime.datetime.
Optuna's datetime_start. None for a trial that has not started —
a waiting trial created by
enqueue and never claimed has no start time.
The value is aware, with tzinfo=datetime.timezone.utc: core records
an unambiguous epoch instant, and a naive value would invite reading it as
local time. Optuna's are naive local times, so a comparison against one of
those raises rather than silently comparing two different scales.
distributions
property
¶
The distribution each parameter was sampled from, as
dict[str, dict[str, Any]].
Recorded per trial, because a define-by-run space may differ from trial to
trial, and holding exactly the parameters params
holds.
Each value is atune's own description dict rather than a distribution
object: a "kind" naming the shape, plus what that shape carries.
{"lr": {"kind": "float", "low": 1e-05, "high": 0.1, "log": True, "step": None},
"n": {"kind": "int", "low": 1, "high": 8, "log": False, "step": 1},
"opt": {"kind": "categorical", "choices": ["adam", "sgd"]},
"amp": {"kind": "bool"}}
choices entries carry the exact tagged scalar types params gives
them; labels are never parsed to guess a type.
This is not Optuna's shape. Optuna returns
dict[str, BaseDistribution] — FloatDistribution/IntDistribution/
CategoricalDistribution objects with attribute access (dist.low),
single(), to_external_repr() and pickling. atune has no Python
distribution classes and does not invent them here, so code that reaches
for an attribute needs dist["low"] instead. The keys, the parameter names
and the meaning of each bound are the same.
duration
property
¶
How long the trial took, as a datetime.timedelta, or None unless it
has both a start and a finish.
Optuna's duration. Computed as
datetime_complete - datetime_start — the subtraction of the two objects
this class hands out, so the three accessors cannot drift apart, and so no
millisecond arithmetic of ours can overflow.
error
property
¶
The failure message for a failed trial, else None. Preserved on
purpose — a failed trial keeps the most interesting data a run produces.
intermediate_values
property
¶
The intermediate reports as a dict[step, value].
Optuna's spelling, and the one to reach for when you want to look a step
up (trial.intermediate_values[10]) or ask which steps were reported
(.keys()). intermediates is the same
data as an ordered list[tuple[step, value]], kept because it preserves
report order — which a dict built from it does not promise beyond
insertion order — and because it is the shape the rest of this binding's
reports use.
A step reported twice keeps its last value here, as any dict build would; the list keeps both.
For a multi-objective trial (any report carrying more than one value) each
value is a list[float], exactly as in intermediates: the shape is
chosen once per trial, so the dict is dict[int, float] or
dict[int, list[float]] and never a mixture. Taking a single component
instead would keep the annotation shorter by silently dropping the other
objectives, which is the trade this binding does not make.
intermediates
property
¶
The intermediate reports as a list of (step, value) tuples.
For a single-objective trial each entry is (int, float); for a
multi-objective one (any report carrying more than one value) each entry
is (int, list[float]), so the list has a single, consistent shape.
This is the report-order view, and the shape the rest of this
binding's reports use;
intermediate_values is the same data
as the dict[step, value] Optuna calls by that name.
last_step
property
¶
The largest reported step, or None if the trial reported nothing.
The last step, not the last report: a trial that reported steps out of order still answers with the highest one, which is what "how far did this trial get?" asks.
number
property
¶
The per-study trial number — the determinism key.
params
property
¶
The sampled parameters as a dict.
A parameter reads back as the type it was suggested as: suggest_float
gives a float, suggest_int an int, suggest_bool a bool, and
suggest_categorical("n", [16, 32, 64]) the int 32 — the same object
type the objective received — so Model(**best_trial.params) is safe.
A categorical stores a stable label plus a tagged scalar payload. Reads use that payload without parsing the label, so mixed scalar lists retain each entry's type and numeric-looking strings remain strings. Old untagged string arrays remain readable as strings; their original source types cannot be recovered.
state
property
¶
The trial's life-cycle state, as an atune.TrialState
member.
Each member is the lowercase string this accessor has always returned
(waiting/running/paused/complete/pruned/failed), because
TrialState subclasses str — so trial.state == "complete" and
trial.state == atune.TrialState.COMPLETE are both true, and code written
either way keeps working. Returning the member rather than a bare string
is what makes the Optuna reading work at all.
user_attrs
property
¶
User-owned JSON metadata attached to this trial.
value
property
¶
The single objective value, or None if the trial has none yet or is
multi-objective (use values).
values
property
¶
Every objective value, or None if the trial has none yet.
LineageNode
¶
One trial's place in the fork genealogy, an entry of Study.lineage.
Paused
¶
Bases: AtuneError
Raised when a scheduler pauses the running trial; the trial is resumable, not finished.
Pruned
¶
Bases: AtuneError
Raised when a scheduler prunes the running trial. See Prune and schedule.
ReevalEntry
¶
One configuration's tune-vs-test comparison, an entry of a ReevalReport.
number
property
¶
The trial's per-study number.
overfit_gap
property
¶
The signed overfit gap: positive when tuning looked better than it held up under the held-out seeds.
test_value
property
¶
Its objective aggregated over the disjoint test seeds.
tune_value
property
¶
Its objective aggregated over the tune seeds — what the sampler saw.
ReevalReport
¶
The held-out-seed re-evaluation report (Study.reevaluate).
best
property
¶
The test-seed-ranked best trial's per-study number, or None if
nothing was re-evaluated or every re-evaluation diverged.
direction
property
¶
The study's single objective direction, "minimize" or "maximize".
entries
property
¶
One ReevalEntry per re-evaluated configuration, in tune-ranked order.
Study
¶
A Python-callable study handle.
best_params
property
¶
The best trial's parameters as a dict, or None if the study has no best
trial.
best_trial.params in one step — Optuna's quickstart accessor. The None
rather than an exception is this binding's own style, the same answer
best_trial gives for the same situation (no
completed trial yet, or a multi-objective study, where there is no single
best across a Pareto front — read best_trials).
Note that Optuna raises ValueError here instead.
A categorical reads back as the type it was suggested as, under the rule
FrozenTrial.params documents, so
Model(**study.best_params) is safe.
best_trial
property
¶
The best trial so far, or None if the study has none.
None for a multi-objective study (there is no single best across a
Pareto front — read best_trials instead).
best_trials
property
¶
The best trials as a list[FrozenTrial].
For a multi-objective study this is the Pareto front; for a
single-objective study it is [best_trial], or [] when the study has no
trial with a value yet (mirroring Optuna's best_trials).
best_value
property
¶
The best trial's single objective value, or None if the study has no
best trial.
best_trial.value in one step. None for the same three reasons that
accessor gives it — no completed trial, a multi-objective study (read
best_trials), or a best trial that carries no value
— and, as with best_params, a None rather than
the ValueError Optuna raises.
direction
property
¶
The study's single objective direction, "minimize" or "maximize", as
an atune.StudyDirection member.
Raises for a multi-objective study, naming
directions — unlike
best_value, which answers None there. The
difference is deliberate: "no best value yet" is a real answer about a
real study, while a study with two directions has no single direction at
all, and a None would let if study.direction == "minimize" read as
"maximize" for it. Optuna raises here too.
directions
property
¶
The study's objective directions as a list of
atune.StudyDirection members, one per objective.
Always at least one entry, so len(study.directions) is the objective
count. Each member is the lowercase label create_study(direction=…)
accepts, because StudyDirection subclasses str, so
study.directions[0] == "minimize" and
study.directions[0] == atune.StudyDirection.MINIMIZE are both true.
metric_names
property
¶
The objective names create_study(metric_names=…) recorded, or None if
none were.
One name per direction when present, in objective order — the labels
reports and dashboards use for the columns
trials_dataframe calls values_0, values_1,
….
name
property
¶
The study's name.
The name= create_study recorded, read back out of storage — so a
study reopened with load_study answers with the name
it was created under. Optuna spells the keyword study_name; the property
is study.study_name there and study.name here.
trials
property
¶
Every trial the study knows about, ordered by number.
A property, as Optuna's study.trials is — len(study.trials), not
len(study.trials()). A property cannot take arguments, so the filtered
read is get_trials.
user_attrs
property
¶
User-owned JSON metadata attached to this study.
ask()
¶
Asks for the next trial (one outstanding at a time).
The sequential, single-evaluation path: one ask/tell round-trip is
exactly one objective evaluation, with no multi-seed fan and no PBT forks
(as in Optuna's ask/tell). Use optimize when a study
configured with n_seeds > 1 or a forking scheduler must materialize its
fan or its population.
Errors if a previous trial has not been told yet.
enqueue(params)
¶
Queues a configuration to be evaluated as the next trial (warm start).
atune's spelling of Optuna's enqueue_trial: params is a dict of
name -> value, and those values outrank the sampler for the trial
that claims them — every suggest_* naming an enqueued parameter returns
the enqueued value instead of a sampled one. Templates are consumed in FIFO
order, so several enqueue calls run in the order they were made and the
sampler takes over once the queue is empty. This is how a configuration you
already trust gets evaluated first:
study.enqueue({"lr": 1e-3, "batch": 64})
study.optimize(objective, n_trials=20) # trial 0 is exactly that config
It is not the same thing as warm-starting from a prior study's history
(atune::transfer, which injects already-known values as finished trials
and re-evaluates nothing); an enqueued configuration is one the loop runs.
A bool, int or float value crosses as itself. Anything else crosses
as its str(x) label, exactly as
suggest_categorical stores it, and the label is
resolved to its index in the choice set the study already knows for that
parameter — its declared space, or a choice set an earlier trial recorded.
A study that has never seen the parameter has no choice set to resolve
against, and that raises rather than guessing an index.
Everything else about the configuration is checked when the trial that claims it suggests its parameters, and surfaces there as the core's own error: a name the objective never suggests is simply unused, a value outside the distribution the objective declares raises then, faithfully.
On determinism: an enqueued study is replayable, not pre-determined.
Which trial number a template lands on depends on which ask claims it,
and under n_jobs > 1 that is thread scheduling. The values are of
course exactly what was enqueued. Unconsumed templates remain in the
in-process core queue after a stopped or failed run.
get_trials(states=None)
¶
Every trial in one of states, ordered by number.
states is a sequence of the lowercase state labels
FrozenTrial.state uses — "waiting",
"running", "paused", "complete", "pruned", "failed" — and
None (the default) means every state, making
study.get_trials() the same list as trials:
done = study.get_trials(states=["complete"])
done = study.get_trials(states=[atune.TrialState.COMPLETE]) # the same
The atune.TrialState members are accepted because each
one is its label — the enum subclasses str — so no separate code
path is needed for them and the two spellings cannot answer differently.
An unrecognised label raises, naming the valid ones; it does not return an empty list, because a typo that silently finds no trials reads exactly like a study that has none.
Optuna's get_trials also takes deepcopy=, which has no meaning here:
every trial this binding hands out is already an immutable snapshot.
lineage()
¶
The fork genealogy as a list[LineageNode], in trial-number order.
Each node exposes its number, its parent (the parent trial's number,
or None for a root), whether it is_fork, and its single objective
value. Reconstructed from parent links + recorded parameters, so a PBT
study's population tree is fully recoverable from storage.
optimize(objective, n_trials=None, timeout=None, n_jobs=1, catch=None, callbacks=None)
¶
Optimizes objective for n_trials trials across n_jobs workers, or
until timeout seconds pass or stop is called.
Delegates to core's
optimize_with: it builds a
bounded (base + n_trials), parallelism(n_jobs) handle that resumes
this study — restoring sampler/scheduler state from storage — and runs the
Python callable through a PyObjective adapter. Everything core's loop
does then happens for free: the multi-seed tune fan (n_seeds > 1),
PBT Fork children and their lineage,
pruning, the is-pruned/is-paused consult, worker threading, and
stop-on-hard-error.
A fanned trial counts as one trial (D16), so base + n_trials yields
exactly n_trials new trials whatever the fan width; the fan runs the
objective k times within that one trial.
The GIL is released around the whole run (py.detach) so core's worker
threads can attach; each objective call re-takes it (single-evaluation
bookkeeping is parallel, the objectives serialize on the GIL). With
n_jobs == 1 core spawns no thread and the objective runs on this thread,
so a Trial the objective stashed is later touched on the same thread and
raises AtuneError through its liveness guard rather than a cross-thread
error.
An exception the objective raises (other than atune.Pruned/Paused,
which record the trial pruned/paused) does not fail-fast: core's
contract is that one bad configuration cannot poison a study, so the run
completes all n_trials (each raising trial recorded failed) and the
first captured exception is re-raised verbatim after the run unwinds —
ahead of any core error it produced. This is Optuna-familiar but not
identical to Optuna's default fail-fast (D6: familiar, not a drop-in).
n_trials=None¶
Optional, as Optuna's is: None means "run until something else stops
the study" — timeout, stop, or a sampler that exhausts
itself (atune.samplers.Grid ends when it has handed out every point).
With n_trials=None, no timeout, no stop() and a sampler that never
ends, the call runs forever; that is allowed, exactly as it is in
Optuna, because the alternative is an invented bound that silently ends a
study the caller meant to keep running. Given an n_trials, the bound is
unchanged: base + n_trials trials, so a resumed study runs exactly
n_trials new ones.
timeout=¶
A wall-clock budget in seconds (a float, as Optuna's is), or None
for none. It means "start no new trial after the deadline", not "kill
a running objective": a trial in flight always finishes and is recorded,
which is atune's contract everywhere and is what makes the records of a
timed-out run deterministic — every trial in the study is a whole trial.
So an objective that takes a minute can overrun a one-second timeout by
most of that minute. The deadline is checked by core's loop before it
creates each trial, so it is honoured by every worker under n_jobs > 1
too. A non-positive or non-finite timeout raises rather than being read
as "stop immediately".
callbacks=¶
A sequence of callables, invoked as callback(study, frozen_trial) after
each trial reaches a terminal state (complete, pruned or
failed), in the order given, once per trial — Optuna's shape and
arguments, so a migrated callback needs no edit. The trial is already
recorded when the callback sees it, and the callback is called before the
next trial starts, which is what lets a callback call
stop.
An exception a callback raises is treated exactly as one the objective
raises: the remaining callbacks for that trial are skipped, the run
continues, and the first exception of the run (from an objective or a
callback) is re-raised verbatim after it unwinds. It never changes what
the finished trial recorded. Optuna instead propagates it immediately and
stops the study; this binding's answer is the one its failure contract
already gives for the objective, and catch= does not apply to it.
callbacks requires n_jobs == 1 and raises otherwise. A callback runs
on the worker thread that finished the trial, and this Study handle is
not thread-safe, so study.stop() or study.best_value inside a
callback of a multi-worker run fails through the explicit owner-thread
guard rather than from inside the callback's core path.
catch=¶
A tuple of exception types whose instances are swallowed: a trial
whose objective raises one is recorded failed, the run continues, and
the exception is not re-raised when the run ends. Anything not in
catch keeps the behaviour above. A catch element that is not an
exception type raises rather than being ignored.
Two things follow, and both differ from Optuna. atune's default is
already stronger than Optuna's: catch=() there fails fast on the first
exception, while here the run always completes and re-raises afterwards,
so catch=(RuntimeError,) is a request atune already grants
unconditionally as far as finishing the study goes — all it adds is the
silence at the end. And because the loop keeps going either way, catch
cannot make a study end sooner or later than it otherwise would.
What is deliberately absent¶
gc_after_trial= has nothing to schedule: the loop is Rust and there is
no per-trial Python garbage to collect between trials, so the keyword
could only be accepted and ignored. show_progress_bar= belongs to a
program, not to a library call — atune_cli renders progress for the
study it runs, and a library that writes to your terminal by default is a
side effect a callback can implement in three lines if you want it.
param_importance(objective=0, *, normalize=True)
¶
Hyperparameter importance as a dict[str, float], most important first.
The in-house PED-ANOVA evaluator (D21): a closed-form, fully
deterministic score over the study's completed trials answering "which
hyperparameters mattered for reaching the good trials?". objective
selects the objective for a multi-objective study (0 = the usual
single-objective case). With normalize=True (the default) the values
sum to one and the dict is insertion-ordered most-important-first, exactly
as Optuna's get_param_importances returns; normalize=False yields the
raw PED-ANOVA values in the same order.
pareto_front()
¶
The Pareto front as a list[FrozenTrial].
For a multi-objective study, the feasible non-dominated trials; for a
single-objective study, [best_trial] (or []), so the accessor reads
the same whatever the objective count.
reevaluate(objective, top_k)
¶
The held-out-seed re-evaluation stage (see Re-evaluate on held-out seeds).
Re-runs objective under the disjoint test seeds for the top top_k
tune-ranked trials and returns a report whose best is the test-ranked
best (not necessarily the tune-ranked one — that gap is the point). Needs a
single-objective study whose seed protocol declares test seeds
(create_study(n_test_seeds=...)).
The Python objective is adapted into a core Objective; an exception it
raises during re-evaluation is captured and re-raised here after the stage
unwinds.
set_user_attr(key, value)
¶
Inserts or replaces one JSON-compatible user attribute on this study.
stop()
¶
Asks the run in flight to stop: the trial in flight finishes, no new
trial starts, optimize returns normally.
Optuna's cooperative stop, with the same meaning timeout= has here —
"start no new trial after this" — and callable from the same two places:
inside an objective and inside an
optimize(callbacks=…) callback.
Like everything else on this handle it is a n_jobs == 1 facility: live
operations are owner-thread-bound, so a worker thread cannot touch the
study at all (that is true of study.best_value in an objective today,
and is not new here). The flag itself is read by every worker, so a stop raised on the
sequential path ends the run whatever the worker count of the next one.
def objective(trial):
value = train(trial)
if value < 0.01: # good enough — stop the search
study.stop()
return value
study.optimize(objective, n_trials=None) # ends at the first good trial
Calling it outside a run is harmless and does nothing: each
optimize owns a fresh run-local flag, so a stray stop() cannot make
the next run end before it starts. (Optuna raises there instead. A
no-op is this binding's answer because no active session exists, and
nothing about the study is left changed by the call.)
It is a request about new trials, not a cancellation: the objective in
flight is not interrupted, and calling stop() from an objective does not
abandon it — that trial is completed and recorded like any other, with
whatever it returns.
tell(trial, values=None, *, state=None)
¶
Tells the outstanding trial its terminal state.
The default (state=None) completes the trial with values (a float
or a sequence of floats). state="pruned" records it
pruned — no values are written,
its last intermediate becomes its objective. state="failed" records it
failed, with values (if given)
used as the failure message string. This lets an ask/tell caller record a
prune or a failure, which the optimize path did implicitly through raised
sentinels.
A default (state=None) completion is not unconditionally recorded
Complete: the trial's context is consulted first, and a scheduler
decision is authoritative even on the manual path (the same rule core's
run_trial follows, and the M1-exit-review finding). If a scheduler
pruned or paused the trial through Trial.report,
the trial is recorded pruned/paused however the objective's values were
computed. This path is single-evaluation — it never materializes a fan.
trial must be the exact live handle returned by the outstanding
ask; passing another study's or an expired handle is an
error and leaves the real operation recoverable.
trials_dataframe()
¶
The study's trials as a dict[str, numpy.ndarray], one entry per column.
Columns, all of equal length (the number of trials, in
trials order): "number" (int64), "state" (an object
column of atune.TrialState members, which are the lowercase state
strings), "value" (float64, NaN where a trial has none)
for a single-objective study or "values_0".."values_{m-1}" for a
multi-objective one. A multi-objective trial whose value vector does not
have exactly one entry per direction is treated as missing, so every
values_* column contains NaN for that trial. There is also one
"param_<name>" per distinct parameter —
float64 (NaN for a trial missing it) for numeric parameters, 0.0/1.0
for booleans, and an object column for categoricals whose entries carry
the same recorded type FrozenTrial.params gives them
(bool/int/float/str), or None where a trial did not suggest the
parameter.
A dict of columns rather than a pandas.DataFrame, because atune does not
depend on pandas — but the dict is a DataFrame constructor argument, so
if you have pandas the frame is one line away:
import pandas as pd
df = pd.DataFrame(study.trials_dataframe())
The columns arrive flat, so there is no multi_index= to pass; group them
yourself with df.columns.str.split("_", n=1, expand=True) if you want
Optuna's two-level frame.
StudyDirection
¶
Bases: str, Enum
A study's optimization direction, as a str enum.
MINIMIZE and MAXIMIZE are the lowercase strings create_study(direction=…)
accepts, so study.direction == "minimize" and
study.direction == atune.StudyDirection.MINIMIZE are both true and either may
be used. Optuna's enum also has NOT_SET; an atune study always has at least
one direction, so there is nothing for that member to mean here.
Trial
¶
The handle a Python objective receives for the trial under evaluation.
Every method borrows the underlying TrialCtx through the guarded owner,
which raises AtuneError after the trial has ended or from another thread.
number
property
¶
The per-study trial number — the determinism key, and the identity Optuna objectives use constantly (checkpoint directories, run names, log lines).
Stable: it is assigned when the trial is created, is what every derived
seed comes from, and matches the number the finished
FrozenTrial carries. Numbering is
per-study and continues across a resume, so the first trial of a reopened
study is not 0.
params
property
¶
The parameters this trial has suggested so far, as a dict.
A mid-objective read, so it grows as the objective calls suggest_*: a
parameter not yet suggested is simply absent. Once the trial finishes, the
same dict is what FrozenTrial.params
returns — and a categorical resolves through the very same helper, so
trial.params["opt"] and best_trial.params["opt"] can never disagree
about a choice's recorded scalar type.
parent_checkpoint
property
¶
The checkpoint reference this trial should start from, if any (a forked child inherits its parent's; a resumed trial inherits its own).
parent_step
property
¶
The resource step parent_checkpoint was
recorded at, if this trial starts from one.
replicate_index
property
¶
The 0-based index of the replicate under evaluation, or None outside a
multi-seed fan (an ordinary single-seed evaluation).
replicate_seed
property
¶
The CRN-aware seed the current replicate must run its environment under.
This is the payload of the multi-seed RL protocol: inside a fan
(create_study(n_seeds=k)) it is replicate j's seed — common across
trials under the default pairing="paired", so trial i and trial
i + 1 evaluate their replicate j under identical randomness
(common-random-numbers variance reduction). Outside a fan it is exactly
seed("objective"). A reproducible objective seeds its RNG
(environment, network init, …) from this, never ambient entropy.
user_attrs
property
¶
User-owned JSON metadata attached to this live trial.
record_checkpoint(step, reference)
¶
Records a checkpoint reference (an opaque string atune never reads)
at resource step, for a later fork or resume to start from.
report(value, step)
¶
Reports an intermediate value at resource step.
(value, step), exactly as Optuna spells it — so a migrated
objective needs no edit, which is the whole point (D47). Rust's
TrialCtx::report keeps (step, value): the two languages differ on
purpose, each matching what its own callers already write, and the paired
examples show both forms side by side.
step is keyword-or-positional but must be given. That is the one
mitigation available for the direction this flip cannot make safe: code
written against atune's older report(step, value) still type-checks
when both arguments are integral — a reward total, a token count, a
count of correct predictions — and would silently record the pair
swapped. Nothing in the call can distinguish the two, so the honest
statement is that it is undetectable and every pre-flip report call
must be read once by a human (see Migrate from
Optuna).
Raises atune.Pruned (or atune.Paused) when the scheduler decides to
stop the trial — the sentinel is meant to propagate out of the objective
(see Prune and
schedule).
seed(stream='objective')
¶
A reproducible seed for one of this trial's random streams.
stream selects the stream: "objective" (the default — the objective's
own randomness), "sampler", or "scheduler". The seed is a pure
function of (study seed, trial number, stream), so it does not depend on
which worker ran the trial — an objective that seeds its RNG from it is
reproducible, and must never fall back to ambient entropy. Prefer
replicate_seed for the environment seed of a
multi-seed study.
set_user_attr(key, value)
¶
Inserts or replaces one JSON-compatible user attribute on this trial.
The live TrialCtx performs the write, so core's ownership fence and
reserved atune: namespace remain authoritative for both ask/tell and
objective-created trials.
should_prune()
¶
Optuna's name for should_stop, accepted so a
migrated objective needs no edit (D45).
Identical in every respect, including answering True for a paused
trial — atune's schedulers can pause as well as prune, which is why
should_stop is the canonical spelling.
should_stop()
¶
Whether the trial should stop now (pruned or paused).
Returns a bool rather than raising, so it reads naturally in an if.
should_prune() is the same method under Optuna's name (D45), so
if trial.should_prune(): raise atune.TrialPruned() — Optuna's idiom,
verbatim — works here. The canonical name is atune's should_stop,
because a scheduler may also pause a trial (resumable, checkpoint kept)
and this answers True for that too.
suggest_bool(name)
¶
Suggests a boolean.
suggest_categorical(name, choices)
¶
Suggests one of choices, returning the original chosen object.
Choices store a stable str(x) label plus an exact bool/int/finite-float/
string payload; the returned value remains choices[index], so a caller
gets back exactly the Python object it passed in. Empty, unsupported,
non-finite, and duplicate-label choice lists are rejected before write.
suggest_float(name, low, high, *, log=False, step=None)
¶
Suggests a float in [low, high].
log=True samples uniformly in the logarithm; step discretizes onto a
grid anchored at low. A log-and-step combination is rejected by the
core, faithfully surfaced as AtuneError.
suggest_int(name, low, high, *, log=False, step=1)
¶
Suggests an integer in [low, high].
log=True samples uniformly in the logarithm (and forces step=1);
step discretizes onto a grid anchored at low.
suggest_open_float(name, low=None, high=None, *, log=False, around=None, times=None, plus=None, sides=None, limit=None)
¶
Suggests a float whose range may exceed the seed you pass — the define-by-run declaration of an open parameter (the open-search-spaces plan §6.3). Exactly one seed form must be given:
(low, high), withlog=Truefor a logarithmic seed; oraround=with exactly one oftimes=(multiplicative, log-scaled) orplus=(additive, linear).
sides restricts growth to "up" or "down" (both by default), and
limit=(low, high) caps growth per side — pass None for an
unbounded side, e.g. limit=(None, 1.0).
The first call for name registers the policy write-once; a later
call must pass an equal one. On a replay the recorded value is
returned unchanged. Growth itself activates with the study loop's
active slice; until then every draw stays inside the seed.
suggest_open_int(name, low=None, high=None, *, log=False, around=None, times=None, plus=None, sides=None, limit=None)
¶
Suggests an integer whose range may exceed the seed you pass —
suggest_open_float's integer twin,
with the same seed union, write-once registration and replay rules.
TrialState
¶
Bases: str, Enum
A trial's life-cycle state, as a str enum.
Every member is the lowercase string this binding has always returned, so
trial.state == "complete" and trial.state == atune.TrialState.COMPLETE are
both true and either may be used. The members are accepted anywhere a state
label is (study.get_trials(states=[atune.TrialState.COMPLETE])).
The names are Optuna's where Optuna has the same state — WAITING, RUNNING,
COMPLETE, PRUNED, and FAIL for the state atune labels "failed". PAUSED
is atune's own: a paused trial keeps its checkpoint and can be woken again, and
Optuna has no equivalent to name it after.
create_study(direction=None, *, sampler=None, scheduler=None, pruner=None, storage=None, seed=0, name=None, study_name=None, metric_names=None, load_if_exists=False, n_seeds=1, n_test_seeds=0, aggregate='mean', pairing='paired', risk_lambda=1.0)
builtin
¶
Creates a study.
direction is "minimize" (default) or "maximize", or a list of them
for a multi-objective study; metric_names optionally names the objectives.
sampler is an opaque handle from atune.samplers (default: uniform
Random); scheduler an opaque handle from atune.schedulers (default:
prune nothing). storage is a spec string opened through the facade's
open_storage (":memory:" by default, a *.atj journal or a *.db
SQLite file otherwise). seed is the root of every derived seed and is the
whole reproducibility story (see
Determinism).
The multi-seed
protocol is configured
inline: n_seeds tune replicates per trial, n_test_seeds disjoint held-out
seeds for reevaluate, aggregate ("mean" /
"median" / "iqm" / "mean_minus_std", the last using risk_lambda) and
pairing ("paired" common-random-numbers / "independent"). All-default
leaves the study single-seed, byte-for-byte as before.
One storage spec holds exactly one study. Creating a study in a storage
that already holds one is an error, because a second study would renumber its
trials from zero and look like a resume until they were counted.
load_if_exists=True reopens the study that is there instead — the Optuna
idiom of rerunning the same script against the same file — and verifies that
its stored name is the name given here. [load_study] is the same reopen
without the create.
Two arguments accept Optuna's spelling as well as atune's.
pruner= is accepted for scheduler=, and study_name= for name=, so
that the two most common mechanical edits in a migrated script do not have to
be made (D45). The canonical names are atune's — scheduler (a scheduler
is the superset of a pruner: it can also pause and fork) and name — and
they are what the documentation, the stub and the error messages use.
Passing both spellings of the same argument raises rather than one of them
silently winning; passing neither keeps the default.
load_study(storage, *, name=None, study_name=None, sampler=None, scheduler=None, pruner=None)
builtin
¶
Loads the one study storage holds.
The reopen half of the one-study-per-storage-spec model: storage is a spec
string [create_study] would accept (a *.atj journal or a *.db SQLite
file), it holds exactly one study, and this
returns a handle over it. The trial numbering, the seed, the direction, the
metric names and the multi-seed protocol all come back out of storage, so
a resumed study cannot silently disagree with the one it continues:
study = atune.load_study("study.atj")
study.optimize(objective, n_trials=20) # trials 20..39, not 0..19
Which is also why the recorded knobs are not accepted here.
direction, metric_names, seed and the multi-seed arguments are part of
the stored configuration; a load_study(direction=...) could only either be
ignored or contradict the study on disk, and both are worse than not offering
it. sampler= and scheduler= are accepted, and default exactly as
[create_study]'s do (uniform Random, prune nothing), because a sampler is
a live object rather than something the study records — its persisted
state is restored regardless, so a Tpe handed to a loaded study continues
its model rather than restarting it.
name is optional. When supplied, the storage catalog resolves it; when no
exact named match exists, the one-study fallback still loads the sole study
so the compatibility check can report the stored and requested names. Omit
it to load whatever the catalog says is the sole study.
study_name= is accepted for name= and pruner= for scheduler=, exactly
as [create_study] accepts them and with the same rule: the canonical names
are atune's, the Optuna spellings are accepted so a migrated script needs no
edit, and passing both spellings of one argument raises. The two entry points
take the same keywords deliberately — a study_name= that worked on one and
not the other would be worse than not offering it at all.
atune.samplers¶
Compiled submodule, generated sibling stub
atune.samplers is registered by Rust's #[pymodule]. The wheel also
carries the generated atune/samplers.pyi, so mypy and pyright discover
the factory signatures even though there is no atune/samplers.py source
file. The factories are listed below because the API page imports the
compiled module for its runtime reference.
Sampler, Random, Grid, Tpe, Qmc, Dehb, Nsga2, CmaEs —
each returns an opaque handle for create_study(sampler=...). Their
parameters and defaults are in
crates/atune_dev/pystub_types.toml,
which cargo dev generate-all --mode check keeps in step with the module on
every push.
atune.schedulers¶
Compiled submodule, generated sibling stub
atune.schedulers is registered by Rust's #[pymodule]. The wheel also
carries the generated atune/schedulers.pyi, so mypy and pyright discover
the factory signatures even though there is no atune/schedulers.py source
file. The factories are listed below because the API page imports the
compiled module for its runtime reference.
Scheduler, Median, Percentile, Asha, Hyperband, Patient,
Wilcoxon, Pbt — each returns an opaque handle for
create_study(scheduler=...).