topos 0.13.1

An autodiff compiler stack in Rust.
Documentation
use std::cell::Cell;

use super::coverage::Fidelity;

/// The numerics posture of an execution scope: the fidelity
/// it demands of every kernel.
///
/// `Exact` demands the bit-identity fidelity — the reference *bits*, in
/// every build. Today no offer-dispatched kernel meets it, so chain
/// work computes on the built-in reference paths, while the fused
/// cells serve under both postures: the reduce-window walk answers
/// the reference bits outright, and the composed cells honor the
/// demand by composing on the reference paths. `Fast` demands only
/// the envelope fidelity: the chain as compiled, backends engaging
/// above their per-task thresholds, which are cost heuristics inside
/// this posture, never correctness boundaries.
///
/// The posture is a value, not a build flag: it rides an
/// [`Entry`](crate::Entry) onto every executor's runs — interpreted
/// or lowered — so an exact oracle result and a fast result are
/// comparable in one process. The default — for entries and for
/// host-side payload calls outside any run — is `Fast`: enabling a
/// backend feature keeps meaning "use it", and features change
/// speed, never behavior classes. The one road that consults no
/// default is [`Network::forward`](crate::Network::forward):
/// whole-spec evaluation is the proving road and always runs
/// `Exact`, so its bits are the same in every build.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Numerics {
    /// Only bit-certified kernels serve: the reference bits, the
    /// same in every build.
    Exact,
    /// The compiled backend chain above its cost thresholds.
    #[default]
    Fast,
}

impl Numerics {
    /// The certified fidelity this posture demands of every kernel.
    pub fn fidelity(self) -> Fidelity {
        match self {
            Numerics::Exact => Fidelity::BitIdentical,
            Numerics::Fast => Fidelity::Envelope,
        }
    }

    /// Runs `body` with the `Exact` posture installed for the current
    /// thread, restoring the previous posture on return or panic.
    ///
    /// It is the direct-call road to the reference bits: a payload
    /// operation outside any run consults the ambient posture, so
    /// `Numerics::exactly(|| a.matmul(&b))` compares a direct call
    /// against the reference without compiling a plan. Runs keep
    /// their own posture regardless: an entry's numerics override
    /// the ambient one for the whole run.
    pub fn exactly<Output>(body: impl FnOnce() -> Output) -> Output {
        let _scope = NumericsScope::enter(Numerics::Exact);
        body()
    }
}

thread_local! {
    /// The posture the chain entries consult; written only through
    /// [`NumericsScope`], so it always restores.
    static CURRENT: Cell<Numerics> = const { Cell::new(Numerics::Fast) };
}

/// Returns the posture of the current scope.
pub(crate) fn current() -> Numerics {
    CURRENT.with(Cell::get)
}

/// Installs a posture for the enclosing scope; dropping restores the
/// previous one, so run-scoped postures nest and never leak.
pub(crate) struct NumericsScope {
    previous: Numerics,
}

impl NumericsScope {
    pub(crate) fn enter(numerics: Numerics) -> Self {
        let previous = CURRENT.with(|cell| cell.replace(numerics));
        Self { previous }
    }
}

impl Drop for NumericsScope {
    fn drop(&mut self) {
        CURRENT.with(|cell| cell.set(self.previous));
    }
}

#[cfg(test)]
#[path = "tests/numerics_tests.rs"]
mod tests;