Skip to main content

LogWeight

Struct LogWeight 

Source
pub struct LogWeight(/* private fields */);
Expand description

Log weight for numerically stable probability computation.

The log semiring addresses numerical stability issues inherent in probability computation by working in the negative log domain. This enables handling of very small probabilities (e.g., $10^{-100}$) that would underflow in linear probability space, while maintaining mathematically equivalent probabilistic semantics.

§Mathematical Definition

The log semiring is defined as $(\mathbb{R} \cup \{+\infty\}, \oplus_{\log}, +, +\infty, 0)$:

OperationDefinitionInterpretation
$a \oplus b$$-\log(e^{-a} + e^{-b})$Combine probabilities
$a \otimes b$$a + b$Multiply probabilities
$\bar{0}$$+\infty$Impossible event ($p = 0$)
$\bar{1}$$0$Certain event ($p = 1$)

§Relationship to Probability Semiring

The log semiring is isomorphic to the probability semiring through the transformation $w = -\log p$ where $p$ is a probability:

  • If $p, q$ are probabilities: $-\log p \oplus -\log q = -\log(p + q)$
  • If $p, q$ are probabilities: $-\log p \otimes -\log q = -\log(p \times q)$

This preserves probabilistic semantics while providing numerical stability.

§Use Cases

§Large-Vocabulary Speech Recognition

use arcweight::prelude::*;

// Acoustic model probabilities (very small values)
let acoustic_prob = LogWeight::from_probability(1e-50);  // Converts to log space
let language_prob = LogWeight::from_probability(0.001);

// Combine probabilities safely
let combined = acoustic_prob.times(&language_prob);  // Multiplication in log space

// Convert back to probability if needed
let final_prob = combined.to_probability();
println!("Final probability: {:.2e}", final_prob);  // ~1e-53

§Machine Translation with Large Models

use arcweight::prelude::*;

// Translation model scores (often very small probabilities)
let phrase_prob = LogWeight::from_probability(1e-20);
let alignment_prob = LogWeight::from_probability(1e-15);
let reordering_prob = LogWeight::from_probability(0.1);

// Combine all model scores
let translation_score = phrase_prob
    .times(&alignment_prob)
    .times(&reordering_prob);

// Alternative translations (add probabilities)
let alternative1 = LogWeight::from_probability(1e-35);
let alternative2 = LogWeight::from_probability(2e-35);
let combined_alternatives = alternative1.plus(&alternative2);  // LogSumExp

§Neural Language Model Integration

use arcweight::prelude::*;

// Softmax probabilities from neural networks
let word_probs = vec![
    LogWeight::from_probability(0.4),   // Most likely word
    LogWeight::from_probability(0.3),   // Second choice
    LogWeight::from_probability(0.2),   // Third choice
    LogWeight::from_probability(0.1),   // Least likely
];

// Compute probability of any of these words
let any_word_prob = word_probs.into_iter()
    .fold(LogWeight::zero(), |acc, prob| acc.plus(&prob));

// Should be close to 1.0 (sum of probabilities)
assert!((any_word_prob.to_probability() - 1.0).abs() < 1e-10);

§Sequence Analysis in Bioinformatics

use arcweight::prelude::*;

// DNA sequence alignment with very long sequences
let base_prob = LogWeight::from_probability(0.25);  // Each base equally likely
let sequence_length = 1000;

// Probability of specific sequence (would underflow in linear space)
let mut sequence_prob = LogWeight::one();
for _ in 0..sequence_length {
    sequence_prob = sequence_prob.times(&base_prob);
}

// Convert to scientific notation for display
println!("Sequence probability: {:.2e}", sequence_prob.to_probability());

§Working with FSTs

use arcweight::prelude::*;

let log_p1 = LogWeight::from_probability(0.5);   // Convert from probability
let log_p2 = LogWeight::from_probability(0.25);  // Convert from probability

// Addition performs log-sum-exp (combines probabilities)
let sum = log_p1 + log_p2;  // -log(0.5 + 0.25) = -log(0.75)
assert!((sum.to_probability() - 0.75).abs() < 1e-6);

// Multiplication is addition in log space (multiplies probabilities)
let product = log_p1 * log_p2;  // -log(0.5 × 0.25) = -log(0.125)
assert!((product.to_probability() - 0.125).abs() < 1e-10);

// Identity elements
assert_eq!(LogWeight::zero(), LogWeight::INFINITY);  // Impossible event
assert_eq!(LogWeight::one(), LogWeight::new(0.0));   // Certain event

§Numerical Stability Implementation

The log semiring implements numerically stable log-sum-exp operation:

-log(e^(-a) + e^(-b)) = -max(a,b) - log(1 + e^(-|a-b|))

This formulation prevents overflow/underflow by:

  • Working with the larger magnitude value first
  • Computing the difference in a stable manner
  • Using the identity: log(1 + x) ≈ x for small x

§Performance Characteristics

  • Arithmetic: Addition is expensive (log-sum-exp), multiplication is O(1)
  • Memory: 8 bytes per weight (single f64)
  • Precision: Double precision for high-accuracy probability computation
  • Conversion: Efficient probability ↔ log conversions available
  • Range: Handles probabilities from ~10^-308 to 1.0

§Conversion Utilities

use arcweight::prelude::*;

// Convert from probability to log weight
let prob = 0.001;
let log_weight = LogWeight::from_probability(prob);
assert_eq!(log_weight.value(), &(-prob.ln()));

// Convert back to probability
let recovered_prob = log_weight.to_probability();
assert!((recovered_prob - prob).abs() < 1e-15);

// Handle edge cases
let zero_prob = LogWeight::from_probability(0.0);
assert!(<LogWeight as num_traits::Zero>::is_zero(&zero_prob));
assert_eq!(zero_prob.to_probability(), 0.0);

§Advanced Usage

§Normalization in Log Space

use arcweight::prelude::*;

// Normalize a probability distribution in log space
let log_probs = vec![
    LogWeight::new(1.0),  // Unnormalized log probabilities
    LogWeight::new(2.0),
    LogWeight::new(0.5),
];

// Compute log partition function (log of sum of probabilities)
let log_z = log_probs.iter()
    .fold(LogWeight::zero(), |acc, &p| acc.plus(&p));

// Normalize each probability
let normalized: Vec<_> = log_probs.iter()
    .map(|&p| p.divide(&log_z).unwrap())
    .collect();

// Verify normalization (sum should be 1.0)
let sum = normalized.iter()
    .fold(LogWeight::zero(), |acc, &p| acc.plus(&p));
assert!((sum.to_probability() - 1.0).abs() < 1e-10);

§Integration with FST Algorithms

Log weights work with all FST algorithms while providing numerical stability:

  • Shortest Path: Finds maximum probability paths
  • Forward-Backward: Stable computation of path probabilities
  • Composition: Combines probabilistic models
  • Determinization: Maintains probability distributions

§Algebraic Properties

  • Commutative: Both $\oplus$ and $\otimes$ are commutative
  • Not idempotent: $a \oplus a \neq a$ in general
  • No path property: $a \oplus b \notin \{a, b\}$ in general
  • Divisible: Division is subtraction: $a \oslash b = a - b$

§See Also

§References

  • Mohri, M. (2002). Semiring frameworks and algorithms for shortest-distance problems. Journal of Automata, Languages and Combinatorics, 7(3), 321–350.

  • Mohri, M., Pereira, F., & Riley, M. (2002). Weighted finite-state transducers in speech recognition. Computer Speech & Language, 16(1), 69–88.

Implementations§

Source§

impl LogWeight

Source

pub const INFINITY: Self

Positive infinity (zero element)

Source

pub fn new(value: f64) -> Self

Create a new log weight

Source

pub fn from_probability(p: f64) -> Self

Convert from probability

Source

pub fn to_probability(&self) -> f64

Convert to probability

Trait Implementations§

Source§

impl Add for LogWeight

Source§

type Output = LogWeight

The resulting type after applying the + operator.
Source§

fn add(self, rhs: Self) -> Self::Output

Performs the + operation. Read more
Source§

impl Clone for LogWeight

Source§

fn clone(&self) -> LogWeight

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
Source§

impl Copy for LogWeight

Source§

impl Debug for LogWeight

Source§

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

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for LogWeight

Source§

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

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

impl Display for LogWeight

Source§

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

Formats the value using the given formatter. Read more
Source§

impl DivisibleSemiring for LogWeight

Source§

fn divide(&self, other: &Self) -> Option<Self>

Division operation
Source§

impl Eq for LogWeight

Source§

impl Hash for LogWeight

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Mul for LogWeight

Source§

type Output = LogWeight

The resulting type after applying the * operator.
Source§

fn mul(self, rhs: Self) -> Self::Output

Performs the * operation. Read more
Source§

impl One for LogWeight

Source§

fn one() -> Self

Returns the multiplicative identity element of Self, 1. Read more
Source§

fn set_one(&mut self)

Sets self to the multiplicative identity element of Self, 1.
Source§

fn is_one(&self) -> bool
where Self: PartialEq,

Returns true if self is equal to the multiplicative identity. Read more
Source§

impl Ord for LogWeight

Source§

fn cmp(&self, other: &LogWeight) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 (const: unstable) · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 (const: unstable) · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for LogWeight

Source§

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

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

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

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl PartialOrd for LogWeight

Source§

fn partial_cmp(&self, other: &LogWeight) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 (const: unstable) · Source§

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

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 (const: unstable) · Source§

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

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 (const: unstable) · Source§

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

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 (const: unstable) · Source§

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

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl Semiring for LogWeight

Source§

type Value = f64

Type of the underlying value
Source§

fn new(value: Self::Value) -> Self

Create a new weight from a value
Source§

fn value(&self) -> &Self::Value

Get the underlying value
Source§

fn properties() -> SemiringProperties

Semiring properties
Source§

fn approx_eq(&self, other: &Self, epsilon: f64) -> bool

Approximate equality for floating-point weights
Source§

fn plus(&self, other: &Self) -> Self

Semiring addition (⊕)
Source§

fn times(&self, other: &Self) -> Self

Semiring multiplication (⊗)
Source§

fn plus_assign(&mut self, other: &Self)

In-place semiring addition
Source§

fn times_assign(&mut self, other: &Self)

In-place semiring multiplication
Source§

fn is_zero(&self) -> bool

Check if weight is zero (additive identity)
Source§

fn is_one(&self) -> bool

Check if weight is one (multiplicative identity)
Source§

impl Serialize for LogWeight

Source§

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

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

impl SimdOps<LogWeight> for LogWeight

Source§

fn simd_plus(left: &[LogWeight], right: &[LogWeight], result: &mut [LogWeight])

Perform parallel semiring addition (plus operation) on weight arrays. Read more
Source§

fn simd_times(left: &[LogWeight], right: &[LogWeight], result: &mut [LogWeight])

Perform parallel semiring multiplication (times operation) on weight arrays. Read more
Source§

fn simd_min(weights: &[LogWeight]) -> LogWeight

Find minimum weight in array using SIMD parallel reduction. Read more
Source§

fn simd_max(weights: &[LogWeight]) -> LogWeight

Find maximum weight in array using SIMD parallel reduction. Read more
Source§

impl StructuralPartialEq for LogWeight

Source§

impl Zero for LogWeight

Source§

fn zero() -> Self

Returns the additive identity element of Self, 0. Read more
Source§

fn is_zero(&self) -> bool

Returns true if self is equal to the additive identity.
Source§

fn set_zero(&mut self)

Sets self to the additive identity element of Self, 0.

Auto Trait Implementations§

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> ArchivePointee for T

Source§

type ArchivedMetadata = ()

The archived version of the pointer metadata for this type.
Source§

fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata

Converts some archived metadata to the pointer metadata for itself.
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
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<Q, K> Comparable<K> for Q
where Q: Ord + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
Source§

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

Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> LayoutRaw for T

Source§

fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>

Returns the layout of the type.
Source§

impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
where T: SharedNiching<N1, N2>, N1: Niching<T>, N2: Niching<T>,

Source§

unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool

Returns whether the given value has been niched. Read more
Source§

fn resolve_niched(out: Place<NichedOption<T, N1>>)

Writes data to out indicating that a T is niched.
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Pointee for T

Source§

type Metadata = ()

The metadata type for pointers and references to this type.
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> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. 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.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

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

Source§

fn vzip(self) -> V

Source§

impl<T> WasmNotSend for T
where T: Send,

Source§

impl<T> WasmNotSendSync for T

Source§

impl<T> WasmNotSync for T
where T: Sync,