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, 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's WilcoxonPruner(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. Pass reduction_factor=4 for 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.

is_fork property

Whether this trial is a fork of another.

number property

The trial's per-study number.

parent property

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

value property

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

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), 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. 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=...).