mod noise_accept

module noise_accept

Noise-aware acceptance (Ball, Branke & Meisel 2018 sequential OSA rule). Noise-aware acceptance: the sequential rule of Ball, Branke & Meisel (2018), “Optimal Sampling for Simulated Annealing under Noise,” INFORMS Journal on Computing 30(1):200-215 (doi:10.1287/ijoc.2017.0774).

The objective difference is observed only through noisy samples delta_i ~ Normal(Delta, sigma^2) with known sigma. For each proposed move the rule accumulates c_n = c_{n-1} + delta_n and, at every draw, makes a three-way decision (accept, reject, or sample again), stopping at the first accept or reject. Their universally optimal per-step acceptance rule (Eq. 19) is

A(c_n, c_{n-1}) = min(1, exp(-2 (c_n + beta sigma^2 / 2)
                             (c_{n-1} + beta sigma^2 / 2) / sigma^2)),

with the simple optimal rejection threshold c* = 0. The procedure obeys detailed balance at each step while maximizing the acceptance probability per sample, so it is the principled acceptance rule when Delta is known only up to noise – exactly the regime of the finite-precision audit, where the rounding error on the energy difference is a bounded noise channel.

Unlike [AcceptRule](crate::accept::AcceptRule), whose (delta_e, T) -> p shape assumes an exact delta_e, OSA consumes a sampler of noisy energy differences plus a known noise scale, so it is its own component rather than an AcceptRule impl. This Rust component is the typed counterpart of the reference experiments/osa.py.

Structs and Unions

struct OsaAccept

The noise-aware OSA acceptance component.

c_star is the rejection threshold on the cumulative difference (0.0 is the simple optimal strategy of the paper); max_samples caps the samples per decision so the inner chain cannot run unbounded.

c_star: f64

Rejection threshold on the cumulative cost difference. 0.0 is the simple optimal strategy of Ball, Branke & Meisel (2018).

max_samples: usize

Cap on the number of samples drawn for a single decision.

Implementations

impl OsaAccept

Functions

fn acceptance_rate<R: Rng>(&self, delta: f64, temp: f64, sigma: f64, trials: usize, rng: &mut R) -> (f64, f64)

Empirical OSA acceptance rate and mean samples per decision for a fixed true difference delta observed through Normal(delta, sigma^2) noise.

Mirrors acceptance_rate in experiments/osa.py; used by the tests and exposed to Python so the Rust port can be checked against the reference.

fn decide<F, R>(&self, mut sample_delta: F, temp: f64, sigma: f64, rng: &mut R) -> OsaResult
where
    F: FnMut(&mut R) -> f64,
    R: Rng

Decides accept/reject for one move from noisy cost-difference samples.

sample_delta(rng) returns one observation delta_i ~ Normal(Delta, sigma^2); temp and sigma must be positive. Returns the decision and the number of samples drawn.

fn new() -> Self

Constructs an OSA component with the simple optimal threshold c* = 0 and a generous sample cap.

fn with_params(c_star: f64, max_samples: usize) -> Self

Constructs an OSA component with an explicit threshold and sample cap.

Traits implemented

impl Default for OsaAccept
struct OsaResult

Outcome of one OSA decision: whether the move was accepted and how many noisy energy-difference samples the decision consumed.

accepted: bool

true iff the move was accepted.

n_samples: usize

Number of noisy samples drawn before the accept/reject decision.