Skip to content

Interoperate with Optuna

Goal: keep your existing Optuna study and your existing dashboard.

Optuna is where most hyperparameter tuning happens, and "rewrite everything" is not a migration plan. atune reads and writes Optuna's own storage formats, so a study can cross in either direction without either tool knowing about the other.

This page is about moving the study. If what you are moving is the source code — an objective, a sampler argument, a trial.report call — that is Migrate from Optuna, which maps the two Python APIs onto each other and names the places where the same line means something else.

Two formats, and the difference matters:

Format What it is atune direction
Journal file Optuna's append-only op-log, one JSON object per line read and write
RDB-sqlite Optuna's relational on-disk database write only

Export an atune study to Optuna

The command is atune export, and the flags name the format. --optuna PATH writes a journal file; --optuna-rdb PATH writes the sqlite database. With neither, the journal goes to stdout. Both may be given at once, each naming its own output — see the CLI reference for the exact surface, which is generated from the binary and therefore cannot drift from it.

The RDB output is the one to reach for if the goal is the dashboard: optuna-dashboard sqlite:///PATH opens it, as does optuna.load_study(storage="sqlite:///PATH"). Nothing atune-specific is needed on the Optuna side; the schema is Optuna's own, mirrored from what the installed version actually writes.

Import an Optuna study into atune

atune import <journal> --storage <spec> reads an Optuna journal and reconstructs the study through atune's storage seam: distributions parsed by parameter name, categoricals resolved by label, non-finite values decoded from Optuna's string tokens. It reports what it did as counts, which is how you find out that something was skipped rather than discovering it later.

The reverse of the RDB path does not exist: atune writes Optuna's database but does not read it. If your study is in an RDB, export it from Optuna to a journal first.

What does not survive the round trip

This is the part worth reading before you rely on it. The mapping is faithful where the two models agree and lossy where they do not, and the losses are counted rather than silent:

  • Timestamps become import-time. Optuna's own timestamps are not carried across.
  • A paused trial exports as RUNNING. atune's Paused state has no Optuna equivalent, so it degrades to the nearest one rather than being dropped.
  • Worker attribution, retry counts, and multi-seed parent/fan structure are dropped. They have nowhere to go in Optuna's model.
  • Wide integer parameters may lose precision. Optuna represents parameter values internally as f64, so it cannot preserve every distinct integer above 2^53. Round-tripping such values can change an integer; keep Optuna integer ranges within the exact-integer range when exact fidelity matters.
  • An open search space exports its trials, not its policy. Every trial of a grown study carries the range it was actually drawn from — the export writes each trial with its own recorded distribution, so nothing about what happened is lost — but the declaration itself (open, the sides, the limit, the growth knobs) has no Optuna counterpart and is dropped. A re-imported study is therefore a closed one; declare the range open again and you have made a new declaration on a new study, exactly as the write-once rule intends.
  • A NaN objective becomes a failed trial, which is Optuna's own semantics for it, with a note attribute recording what happened. A NaN intermediate uses Optuna's native encoding and is lossless.

That last pair is not a detail. A NaN written the naive way produces a database Optuna's loader refuses to open — the whole study, not the one trial. Both cases are regression-tested against a real Optuna load.

What has no example on this site

Nearly every other how-to here pulls its code from a file under examples/ that CI runs (Read a doctor report is the other exception, for its own stated reason). This page does not, and the reason is worth stating rather than hiding:

  • The Python binding cannot do this. atune_py does not enable the optuna-compat feature and exposes no Optuna symbol, so there is no Python arm to write.
  • The Rust arm is deferred. It needs a feature-gated example, and the interop story is better told through the CLI in any case — atune_cli depends on the facade with optuna-compat and optuna-rdb unconditionally, so atune export works out of the box with nothing to configure.

So treat the commands above as documented-but-ungated until a CLI case covers them. Everything on this page comes from the generated CLI reference and from the interop implementation's own tests; none of it is a guess. But it is not held true by a test on this page, the way tune any program is, and you are entitled to know which pages carry that guarantee and which do not.

Next

  • Migrate from Optuna — the other half: the Python API mapping, and the six places a migrated line means something else.
  • Inspect a study — atune's own viewers, if the dashboard was the only reason for the round trip.
  • Feature reference — optuna-compat and optuna-rdb, and what each pulls in.
  • Storage — the seam import writes through.