Skip to content

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. Both surfaces share one implementation, and the parity gate checks that they produce bit-identical results.

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, atune/pb2.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. NumPy is optional; install atune[dataframe] for this operation. Import and optimization do not load it.

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.

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.

categorical_labels property

Stable categorical labels as {name: (choices, selected_label)}.

Unlike params, this read keeps storage labels rather than typed scalar payloads. Rust-created histories may use a custom label for a scalar, or several labels for the same scalar. The Optuna reading adapter needs these exact labels to match file exports. Boolean distributions are not categorical label sets and are omitted. Returns a new dict and choice lists; inconsistent records raise.

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.

LedgerEntry

One trial's line of a ResourceLedger, an entry of Study.resource_ledger.

age property

The resource trained since initialization or the last restart.

evaluations property

Each stored evaluation stage ("configuration", "checkpoint", "recipe") with its replicate count.

lineage property

The resource its lineage represents: its highest reported step, where its recipe ends (0 before it reports).

number property

The trial's per-study number.

state property

The trial's state as a TrialState member: pruned, failed, and replaced members are charged like any other.

trained property

The resource this trial trained itself and is charged for: its highest reported step less the step of the checkpoint it started from.

LineageNode

One trial's place in the fork genealogy, an entry of Study.lineage.

fork_step property

The step of the checkpoint this trial started from, or None for a root, a fresh restart, a child that started without a checkpoint, or a trial recorded before fork steps were.

is_fork property

Whether this trial is a plain fork of another.

is_restart property

Whether this trial is a restart child of another.

number property

The trial's per-study number.

parent property

The number of the trial this one forked or restarted from, or None for a root.

restart property

The restart mode, "fresh" or "shrink_perturb", or None for a trial that is not a restart child.

value property

The trial's single objective value, or None (multi-objective or unfinished).

Mabc

The clustered sliding-window UCB policy for OnlineTuner.

OnlineTuner

Storage-free, deterministic tuning of clustered numeric choices.

clusters maps names to candidate float values; its insertion order is the cluster order returned by suggest. policy accepts Mabc or PbtLite. Use suggest() and then observe(round_return) once per round. The callback- driven drive_round and RNG-object round_rng methods have no Python counterpart and are intentionally not bound.

clusters property

The number of clusters this tuner manages.

direction property

The optimization direction, as a StudyDirection member.

is_flush_boundary property

True when the round just observed lands on a flush-interval boundary, the cue to snapshot() the tuner. Always False without flush_every.

rounds property

The number of rounds observed so far.

observe(round_return)

Credits a round's return to the values from the latest suggest().

restore(snapshot)

Restores a snapshot into a tuner with the same clusters and policy.

The snapshot's direction, seed, flush interval and progress replace this tuner's own. Invalid, foreign or mismatched bytes raise AtuneError and leave the tuner unchanged.

snapshot()

Compact UTF-8 JSON bytes of the Rust StateBlob, including kind/version.

suggest()

Returns an owned dictionary of the next round's values in cluster order.

OutcomeUncertainError

Bases: AtuneError

Raised when a mutation was submitted but whether it committed is unknown. Do not retry it blindly, which can apply it twice: read the study's durable state first and resubmit only what is missing.

Paused

Bases: AtuneError

Raised when a scheduler pauses the running trial; the trial is resumable, not finished.

PbtLite

The deterministic scalar-perturbation policy for OnlineTuner.

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.

ResourceLedger

The study's resource ledger (Study.resource_ledger).

total property

The study's total charge, the sum of every entry's trained: what the fidelity budget has consumed.

trials property

One LedgerEntry per trial, in trial-number order.

unit property

The resource unit every amount is in ("steps", "episodes", "walltime_ms", or a custom name).

RetryableError

Bases: AtuneError

Raised when a concurrent worker won a race or the trial's lease was lost. Re-read state and retry: the error is transient, though replaying an arbitrary operation is not guaranteed safe.

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.

resource_unit property

The label of the unit Trial.report steps and the fidelity budget count in, as create_study(resource_unit=...) recorded it: "steps" (environment steps, the default), "episodes", "walltime_ms", or a custom name.

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.

add_trial(trial)

Adds terminal history without re-evaluation. Source distributions, parameters, intermediates, JSON attributes, and canonical constraints are preserved; destination numbering and timestamps are new. Only complete, pruned, and failed trials are accepted. Existing queued configurations stay queued; sampler/scheduler hooks are not invoked.

add_trials(trials)

Adds terminal trials in iterable order. Like Optuna, this is not an atomic batch: a later invalid entry leaves earlier additions committed. A failed backend reconstruction leaves a diagnosable failed record.

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.

evaluate_checkpoints(objective, top_k)

The checkpoint estimand: evaluate each of the top top_k tune-ranked trials' latest checkpoint under the held-out test seeds, without training.

The objective sees trial.evaluation == "checkpoint", the checkpoint as trial.parent_checkpoint (its step as trial.parent_step), and the trial's configuration from suggest_*; it loads the checkpoint and evaluates it. A trial that never recorded a checkpoint is reported with a NaN test_value and the objective does not run for it. Returns the same report as reevaluate, and needs the same single-objective study with test seeds. Results are stored beside the trial and reused for the same checkpoint; the trial's own values are untouched.

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 donor trial's number, or None for a root), whether it is_fork or is_restart, its restart mode and fork_step, and its single objective value. Reconstructed from parent links, recorded parameters and each child's lineage record, 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, *, max_resource=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, 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".

max_resource=

A keyword-only budget in the study's resource_unit — environment steps by default — or None for none: this call starts no new trial once the trials it ran have been charged max_resource units. Like n_trials it counts per call, on top of what the study had already spent when the call began. Each trial is charged its highest Trial.report step less the step of the checkpoint it was forked from, so a population-based fork pays only for the steps it trains itself, and the resource_ledger total is the same sum.

It is a cooperative stop, checked before each trial starts, like timeout: the trial that crosses the line finishes, so a single worker overshoots by at most one trial's charge and n_jobs workers by at most that many. An objective that never calls report is charged nothing and never exhausts it, so pair it with n_trials or timeout for such objectives. A non-positive amount raises.

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, conditional=False)

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. conditional=True scores every parameter any completed trial recorded, treating a trial without it as its own group (conditional PED-ANOVA, Optuna 5's default); the default False keeps the original evaluator, which drops parameters some trials lack.

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.

recipe(trial_number)

The schedule that produced trial trial_number, as the parsed atune.recipe version-1 JSON object.

segments run from the start of the trial's inherited training to the trial itself, each with its trial number, start_step, end_step, typed params and the fork or restart event that began it. Raises AtuneError naming the trial when a trial on the path is a fork without a lineage record or names a donor the study does not contain.

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.

replay_recipes(objective, top_k)

The recipe estimand: train each of the top top_k tune-ranked trials afresh following its recipe under the held-out test seeds.

The objective sees trial.evaluation == "recipe" and the schedule as trial.recipe (the Study.recipe dict); suggest_* returns the recipe's first-segment configuration, and there is no parent checkpoint. Returns the same report as reevaluate; a trial whose lineage is incomplete raises AtuneError before any objective runs. Results are stored beside the trial and reused for the same recipe.

resource_ledger()

The resource ledger: per trial, the resource it was charged for (trained), the resource its lineage represents (lineage), its training age since initialization or the last restart, and each stored evaluation stage's replicate count, plus the study total — what the fidelity budget has consumed — in the study's unit.

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, or the explicit state="complete") 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) or explicit state="complete" 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). 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.

constraints property

A dict copy of this evaluation's named constraints so far. Changing the copy does not change the trial; use set_constraint to declare a value.

evaluation property

The final-stage estimand this evaluation computes, or None.

"checkpoint" during Study.evaluate_checkpoints: load parent_checkpoint (the trial's latest checkpoint) and evaluate it under replicate_seed without training. "recipe" during Study.replay_recipes: train afresh following recipe. None for a live trial and during Study.reevaluate.

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.

recipe property

The schedule a recipe replay follows, as the parsed atune.recipe version-1 dict (Study.recipe's shape), or None unless evaluation is "recipe". suggest_* returns its first segment's configuration; switch to each later segment's params at its start_step.

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.

restart property

The restart this trial was started as, or None.

A dict for the first run of a restart child: mode ("fresh", which has no parent_checkpoint and re-initializes, or "shrink_perturb", which loads parent_checkpoint and then shrinks and perturbs it), the reproducible seed for that re-initialization or noise, and for "shrink_perturb" the shrink factor and noise scale. None for an ordinary trial, a plain fork, a resumed trial, and a re-evaluation.

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. 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_constraint(key, value)

Sets one named constraint (<= 0 feasible). Values are float-castable and must be finite. A repeated key warns and retains its first value. Declared key sets must agree across the study. The vector is committed when evaluation finishes or tell runs, in numeric-then-lexical key order matching Optuna import. Infinities are refused unlike Optuna.

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.

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, 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. Exactly one seed form must be given:

  • (low, high), with log=True for a logarithmic seed; or
  • around= with exactly one of times= (multiplicative, log-scaled) or plus= (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. The value is drawn from the live range: the seed on first use, then the widened range once growth has moved it.

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.

MaxTrialsCallback(n_trials, states='complete') builtin

Creates a callback stopping after n_trials records in states, including existing history. The returned callable takes (study, frozen_trial).

The default counts complete trials; states=None counts every state, and an empty sequence counts none. As with all callbacks, n_jobs=1 is required. A threshold already reached stops after the next callback, not before the next evaluation, matching Optuna's callback semantics. This is a factory for an opaque callable rather than Optuna's callback class.

create_study(direction=None, *, directions=None, search_space=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, fan_sync_steps=None, target_value=None, resource_unit=None) 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. search_space optionally declares fixed atune distribution descriptions (the dict shape FrozenTrial.distributions returns). Population and GP models require a declared space; omission preserves their independent fallback. Auto carries its explicit space into the study automatically.

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.

resource_unit names what Trial.report steps count and is stored with the study: "steps" (environment steps, the default), "episodes", "walltime" (milliseconds, labelled "walltime_ms"), or any other non-empty name as a custom unit. The resource ledger and optimize(max_resource=...) count in it. A reopen keeps the stored unit, and an explicit different one raises.

Two settings belong to the handle rather than to the stored study, so [load_study] accepts them too and a reopened study needs them again. fan_sync_steps makes a multi-seed fan rung-synchronous: every replicate runs to each listed step before the scheduler decides on the aggregate, so a pruner can cut the fan there (pass the pruner's rung steps). The objective must then be resumable: record a checkpoint before report, let the atune.Paused it raises at a sync step propagate, and continue from trial.parent_checkpoint at trial.parent_step. target_value ends optimize (and makes ask refuse) once a feasible best trial is at least as good as it; it needs a single-objective study.

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.

Three arguments accept Optuna's spelling as well as atune's. directions= is accepted for direction=, pruner= 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. 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.

create_trial(*, state='complete', value=None, values=None, params=None, distributions=None, user_attrs=None, intermediate_values=None, constraints=None) builtin

Creates immutable terminal history without evaluating an objective.

value and values are mutually exclusive. Complete trials require a nonempty, non-NaN objective vector. params and distributions must have identical keys; distributions use atune description dicts, not Optuna objects. Named constraints are finite and use numeric-then-lexical order. Only complete, pruned, and failed states are supported. A pruned value is derived from the last intermediate; explicit values on pruned/failed records are refused. User attributes are JSON-compatible; there is no system_attrs junk drawer. The detached number is -1; adding assigns new destination numbers and timestamps. The returned object stays immutable.

load_study(storage, *, name=None, study_name=None, sampler=None, scheduler=None, pruner=None, fan_sync_steps=None, target_value=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. fan_sync_steps= and target_value= are accepted as [create_study] accepts them, because the study does not record them. 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, Nsga3, CmaEs, GpEi, Auto, Carbs — 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, Pb2 — each returns an opaque handle for create_study(scheduler=...).

atune.pb2

InRunPopulation(search_space, members, *, ...) and Ipbt(search_space, members, budget, initial_step, *, ...) are storage-free controllers for learners owned by the host. They accept float/int distribution description dicts and expose Rust builder defaults as keywords, including their own seed. The generated atune/pb2.pyi carries the complete constructor and method signatures.

InRunPopulation.report records each member's boundary progress and fitness; decide returns exploit dicts with recipient, donor, and params. Ipbt.round returns slots, step, and evaluate_start; report_start and report supply fitness, and decide returns ordered action dicts with kind (copy, reinit, retire, archive) and the corresponding fields. The host applies learner copies, reinitializations and archives before installing the controller's new parameters. Both expose snapshot() -> bytes and restore(bytes) using the complete Rust StateBlob as UTF-8 JSON; save host learners and archives separately. See Population-based training for the host protocol and snapshot timing.

atune.optuna

An optional, pure-Python adapter. Its inline annotations ship in the wheel; the generator validates its public export and generates its API link. Calling it requires atune[optuna]; importing the module does not load Optuna.

atune.optuna.to_optuna_study(study)

Return an independent snapshot suitable for Optuna's reading interface.

The copy uses InMemoryStorage. atune storage remains the source of truth; changing or optimizing the copy never writes back. Stop concurrent writers before copying if study attributes and trials must describe one instant. Reading a live study does not lock its writers.

Matches the journal/RDB converters: paused trials become RUNNING, categorical choices and parameters become their stored string labels, boolean distributions stay boolean categoricals, constraints use the constraints system attribute, and multi-objective intermediate reports retain objective zero. Per-trial ranges, trial numbers, objective values, directions and JSON user attributes are preserved. A NaN objective becomes FAIL with the RDB converter's degradation attributes.

Optuna assigns fresh timestamps. Worker, retry, checkpoint, lineage and per-seed payloads are outside this Python reading snapshot. Wide integer parameters share Optuna's float representation limitation above 2**53. This adapter promises no Optuna optimization or storage interoperability.

Parameters:

Name Type Description Default
study Study

An atune study, read through its public Python accessors.

required

Returns:

Type Description
Study

An in-memory Optuna study with trials in source-number order.

Raises:

Type Description
ImportError

Install atune[optuna] when Optuna is unavailable.

ValueError

Source trial numbers are not contiguous and zero-based.