Skip to main content

Module dsl

Module dsl 

Expand description

The string DSL: a compact text notation for a Distribution.

This is the zero-code path (two ways to write a space, and a third for configuration files): any TOML-configured or CLI-driven program becomes tunable by writing a distribution as text, without a Rust type. The same notation is shared by the CLI (atune run --param lr=loguniform(1e-5,1e-2)) and the TOML overlay (the oniro path), so it is total — every byte sequence maps to either a Distribution or a descriptive Error, and nothing here can panic.

Everything lowers to the existing Distribution constructors; the DSL invents no parallel types and duplicates none of their validation — it surfaces the constructors’ own errors.

§Grammar

Whitespace (ASCII or Unicode) between tokens and around arguments is insignificant; spaces inside a choice label are part of the label. Function names and the keywords bool, log, step, open, limit, times, plus, up and down are lower-case and case-sensitive.

FormLowers toNotes
uniform(low, high)Distribution::try_new_with_floatcontinuous linear float
uniform(low, high, step)Distribution::try_new_with_stepped_floatthird arg is the step; step=<v> is also accepted
loguniform(low, high)Distribution::try_new_with_log_floata log range cannot be stepped
int(low, high)Distribution::try_new_with_intunit-step integer
int(low, high, log)Distribution::try_new_with_log_intthe bare keyword log
int(low, high, step=<n>)Distribution::try_new_with_stepped_intthe step keyword is required for ints
choice(a, b, c)Distribution::try_new_with_categorical_labelslabels in order, stable indices
boolDistribution::new_with_booleanalso written bool()
<number>a fixed value (see below)e.g. 0.001, 42, 1e-5

§Numeric literals

A literal is read as an integer when it is written with no decimal point and no exponent (42, -7), and as a float otherwise (0.1, 1e-5, -2.5E3). This is exactly the “integer vs float inferred from the literal” rule: it decides the type of a bare fixed value and of an int bound, and it is why int(2.5, 3) is rejected (an int bound must be an integer literal) while uniform(0, 1) is fine (an integer literal is accepted wherever a float is wanted, and widened). Negative numbers and scientific notation are accepted; NaN and inf literals are rejected (they can only ever name an invalid distribution).

§The step syntax

The step is written differently for the two numeric kinds, on purpose:

  • uniform takes the step as its third positional argument (uniform(0, 1, 0.25)); the explicit uniform(0, 1, step=0.25) is accepted too and means the same thing.
  • int requires the step=<n> keyword (int(2, 512, step=4)), because an int’s third slot is shared with the bare log keyword and a bare number there would read ambiguously.

§Fixed values

A bare number is a parameter that is present but not tuned — it always takes that one value. It lowers to the degenerate range of the matching numeric kind:

  • an integer literal n → Distribution::try_new_with_int(n, n), which is single-valued (the study loop assigns it without sampling);
  • a float literal x → Distribution::try_new_with_float(x, x), a degenerate continuous range. This is not is_single_valued (a continuous float reports no cardinality), but its transform pins every unit coordinate to x, so the independent-sampling path returns the constant just the same. A single-valued float would require a fabricated step, which would then have to be invented on every round trip; the degenerate continuous range avoids that and still yields the constant.

§Growing declarations

parse_growing, parse_schema_growing and parse_schema_lines_growing are a parallel set of entry points – parse and its own three callers are untouched – that accept everything above plus zero or more trailing keyword arguments declaring an Open growth policy. render_growing is the counterpart that renders a distribution back through them, since Open is deliberately not part of Distribution and Display cannot carry it.

FormAddsNotes
<form>, openan Open with Sides::Bothany form above, plus a bare open
<form>, open=upan Open with Sides::Uponly the upper bound may grow
<form>, open=downan Open with Sides::Downonly the lower bound may grow
<form>, limit=..=<n>a high Open::limitinclusive; ..<n> (exclusive) is rejected
<form>, limit=<n>..a low Open::limitinclusive; the two limit= forms combine
around(c, times=<n>)Distribution::try_new_with_log_float seed [c/n, c*n], plus an Openmultiplicative “no idea” declaration
around(c, plus=<n>)Distribution::try_new_with_float seed [c-n, c+n], plus an Openadditive “no idea” declaration

open, open=up, open=down and limit=<range> may follow any call form above (uniform, loguniform, int) or an around(...), in any combination and any order, after that form’s own arguments – which is why loguniform(1e-5, 1e-2, open=up, limit=..=1.0) has four arguments where parse accepts exactly two. around has no closed-form meaning (there is no plain Distribution for “no idea yet”), so it is recognized only by parse_growing and always produces a policy, with or without additional open/limit keywords. limit is independently optional per side, exactly as Open::limit is; setting the same side twice keeps the later value. around ... times lowers to a logarithmic seed, which cannot be stepped (compare Distribution::try_new_with_log_float), so a step= keyword after around is rejected outright rather than silently accepted.

What reaches text is only the seed, the sides and the limit – factor and max_expansions keep their defaults in text and are reachable only from the Open builder in Rust.

§Round trip

Distribution implements Display as a human-readable DSL representation. parse(&d.to_string()) reconstructs an equal distribution for every distribution parse can produce (a property test in this module asserts it across every kind). Such output is one of the grammar forms above; floats are rendered in the shortest form that round-trips and always carry float syntax where a bare integer would otherwise be re-read as an int. Display is not lossless serialization for arbitrary hand-built distributions: choice syntax has no escaping, so empty labels, commas, and leading or trailing whitespace cannot round-trip, and typed choice payloads are omitted. Quotes and backslashes are ordinary label characters, not escape syntax. Use serialization that preserves payloads when those must be retained.

render_growing extends the same guarantee to a growth policy: parse_growing(&render_growing(&d, open.as_ref())) reconstructs equal GrowingDistribution fields for every result parse_growing can produce. render_growing(&d, None) renders identically to d.to_string(), since Open – not Distribution – is the only thing it adds.

§Whole spaces

parse_schema turns an ordered sequence of (name, spec) pairs (a TOML table, a map, CLI --param pairs) into a SpaceSchema, preserving declaration order; parse_named parses a single name = spec line; and parse_schema_lines parses a block of such lines (blank and #-comment lines are skipped).

parse_schema_growing and parse_schema_lines_growing are the growing counterparts: each returns a SpacePolicy alongside the SpaceSchema, holding one Open per parameter whose spec carried a growth keyword. A spec with none contributes nothing to the policy, so the returned SpacePolicy only ever names parameters that actually declared themselves open.

Structs§

GrowingDistribution
A parsed distribution and its optional growth declaration.
GrowingSchema
A parsed search-space schema and its growth policy.
NamedDistribution
A parameter name and the distribution parsed for it.

Functions§

parse
Parses a DSL spec into a Distribution.
parse_growing
Parses a growing DSL spec into a distribution and its optional growth policy.
parse_named
Parses a single name = spec line into a NamedDistribution.
parse_schema
Parses an ordered sequence of (name, spec) pairs into a SpaceSchema.
parse_schema_growing
Parses an ordered sequence of (name, spec) pairs into a GrowingSchema.
parse_schema_lines
Parses a block of name = spec lines into a SpaceSchema.
parse_schema_lines_growing
Parses a block of name = spec lines into a GrowingSchema.
render_growing
Renders a distribution and its optional growth policy in the string DSL.