Skip to main content

Open

Struct Open 

pub struct Open { /* private fields */ }
Expand description

One parameter’s declared growth policy: the seed it starts from, which side(s) may move, and the numbers that govern and bound that movement.

Also the ergonomic builder: Open::log, Open::linear, Open::around, then Open::up / Open::down / Open::limit / Open::factor / Open::max_expansions. Every setter is infallible and merely records what it is told – nothing is validated eagerly, and no growth decision is made by the builder. Call Open::validate to check the result before passing it to the replay or widening engine.

use atune_core::space::{Open, Spread};

let lr = Open::log(1e-5..=1e-2).up().limit(..=1.0);
assert!(lr.validate().is_ok());

let width = Open::around(256.0, Spread::Times(2.0)).down();
assert!(width.validate().is_ok());

// A degenerate seed cannot grow (§7.3) -- caught by `validate`, not by
// the builder itself.
let degenerate = Open::linear(0.5..=0.5);
assert!(degenerate.validate().is_err());

Open is serialized as a self-describing object – seed, sides, limit (a named {"low": ..., "high": ...} pair, never a bare tuple), factor and max_expansions – and compares with PartialEq, not Eq: it holds f64 fields, and every one of them is validated finite (§7.4), so PartialEq already behaves as a total equality in practice without claiming the stronger trait for a type that holds a float (matching crate::space::Assignment’s own reasoning).

Deserialization runs Open::validate, mirroring Distribution’s own DistributionRepr precedent (space/distribution.rs): a hand-edited state blob cannot introduce a factor <= 1.0 or a max_expansions above the cap and have it reload silently.

Implementations§

§

impl Open

pub fn new(seed: Distribution) -> Self

The general constructor: an open policy around an already-built seed, with Sides::Both, no limit on either side, factor = 2.0 and max_expansions = 8.

This is what a caller that already has a Distribution – an integer range, a stepped float, a log integer – reaches for; Open::log, Open::linear and Open::around are sugar over this for the common continuous-float cases. Nothing is validated eagerly – see Open::validate.

A stepped seed is routed back through its own validated constructor first, so a hand-built, off-grid high (legal per Distribution::validate, but never reachable through Distribution::float_step/Distribution::int_step) is snapped onto the grid here rather than surviving until the next deserialization does it instead – see renormalize_seed, below. An already-invalid seed is left exactly as given, so Open::validate, not this constructor, is what reports it.

pub fn log(range: RangeInclusive<f64>) -> Self

An open seed sampled uniformly in the logarithm of the value, over range – sugar for Open::new with a logarithmic Distribution::Float. Mirrors Distribution::float_log’s bounds without its up-front validation; call Open::validate to check the result.

pub fn linear(range: RangeInclusive<f64>) -> Self

An open seed sampled uniformly over range – sugar for Open::new with a linear Distribution::Float.

pub fn around(center: f64, spread: Spread) -> Self

An open seed centered at center, spread by spread – §6.1’s around, the “no idea at all” declaration. Spread::Times is multiplicative and samples logarithmically: [center / f, center * f]. Spread::Plus is additive and samples linearly: [center - d, center + d].

pub fn around_int(center: i64, spread: Spread) -> Self

Open::around’s integer twin: an open integer seed centered at center, spread by spread. Spread::Times samples logarithmically over [center / f, center * f]; Spread::Plus linearly over [center - d, center + d]. Fractional bounds round away from the centre — a spread is a declared uncertainty, and rounding it inward would quietly shrink what the user asked for.

This constructor is infallible like its siblings, so an unrepresentable spread — non-finite, or bounds outside f64’s exact-integer window — is carried rather than guessed at: the seed comes out inverted (low > high) and Open::validate refuses it at declaration, exactly where a float around’s non-finite bound is refused. Saturating instead would manufacture a legal-looking i64::MAX bound out of an inf the user never meant.

pub fn up(self) -> Self

Restricts growth to the upper bound only.

pub fn down(self) -> Self

Restricts growth to the lower bound only.

pub fn limit(self, bound: impl Into<LimitBound>) -> Self

Sets a hard bound on one side, merging with whatever the other side already had. Spelled with ..=/.. on the standard range types – §6.5 settles ..= as the one spelling for a limit, because a limit is attainable: ..=1.0 sets the upper bound at 1.0, 1e-8.. sets the lower bound at 1e-8. Calling this twice, once per side, sets both.

pub fn factor(self, factor: f64) -> Self

Overrides the default growth factor (2.0): the span is multiplied by this on each growth step. Validated by Open::validate, not here – see §7.4.

pub fn max_expansions(self, max_expansions: u32) -> Self

Overrides the default expansion cap (8, counted per side). Validated by Open::validate, not here – see §7.4.

pub fn validate(&self) -> Result<()>

Checks this policy’s own declaration-time invariants – §7.3, §7.4 and §9.6. This is the single function that owns policy validation. #[derive(Space)] rejects malformed attribute combinations at compile time, while its generated policy construction routes values through this runtime validation. The rules below remain the canonical declaration-time checks:

  • the seed is itself a valid, finite Distribution – Distribution::validate: both bounds finite (and, for a stepped float, a finite positive step), low <= high, and a strictly positive low when logarithmic. This is also where a non-finite Spread is caught: Open::around bakes Spread’s value into the seed’s bounds before Open ever stores it, so an infinite or NaN Spread surfaces here as a non-finite seed bound, not as a separate rule (Error::InvalidSpace, §7.4);
  • the seed is an ordered distribution – Distribution::Float or Distribution::Int (Error::Unsupported otherwise: a categorical or boolean parameter has no bound to grow);
  • the seed has non-zero span (§7.3);
  • factor is finite and strictly greater than 1.0 (§7.4);
  • max_expansions is at most 64 (§7.4);
  • every bound of limit is finite, when set (§7.4);
  • no bound of limit excludes the seed’s own range (§9.6);
  • no bound of limit sits on a side sides does not permit to move (§9.6).
§Errors

Error::InvalidSpace naming the first violated rule above, or Error::Unsupported for a categorical or boolean seed.

use atune_core::error::Error;
use atune_core::space::Open;

// factor == 1.0 moves no bound while still consuming an expansion.
let no_op_factor = Open::linear(0.0..=1.0).factor(1.0);
assert!(matches!(no_op_factor.validate(), Err(Error::InvalidSpace(_))));

// A ceiling below the seed's own high excludes the seed (§9.6).
let bad_limit = Open::log(1e-5..=1e-2).limit(..=1e-3);
assert!(matches!(bad_limit.validate(), Err(Error::InvalidSpace(_))));

pub fn seed(&self) -> &Distribution

The seed this policy grows from: what was first declared (or, for Open::around, what it computed), before any growth. Immutable once declared (§7.2); study policy registration enforces write-once equality.

pub fn sides(&self) -> Sides

Which side(s) this policy permits to grow.

pub fn limit_low(&self) -> Option<f64>

The hard lower bound this policy will not grow past, if any.

pub fn limit_high(&self) -> Option<f64>

The hard upper bound this policy will not grow past, if any.

pub fn factor_value(&self) -> f64

The growth factor this policy applies per expansion (§7’s factor).

pub fn max_expansions_value(&self) -> u32

The per-side expansion cap (§7’s max_expansions).

Trait Implementations§

§

impl Clone for Open

§

fn clone(&self) -> Open

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for Open

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
§

impl<'de> Deserialize<'de> for Open

§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
§

impl PartialEq for Open

§

fn eq(&self, other: &Open) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
§

impl Serialize for Open

§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
§

impl StructuralPartialEq for Open

Auto Trait Implementations§

§

impl Freeze for Open

§

impl RefUnwindSafe for Open

§

impl Send for Open

§

impl Sync for Open

§

impl Unpin for Open

§

impl UnsafeUnpin for Open

§

impl UnwindSafe for Open

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more