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)$:
| Operation | Definition | Interpretation |
|---|---|---|
$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) ≈ xfor smallx
§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
ProbabilityWeightfor simple probability computationTropicalWeightfor optimization problems
§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§
Trait Implementations§
impl Copy for LogWeight
Source§impl<'de> Deserialize<'de> for LogWeight
impl<'de> Deserialize<'de> for LogWeight
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl DivisibleSemiring for LogWeight
impl DivisibleSemiring for LogWeight
impl Eq for LogWeight
Source§impl Ord for LogWeight
impl Ord for LogWeight
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl PartialOrd for LogWeight
impl PartialOrd for LogWeight
Source§impl Semiring for LogWeight
impl Semiring for LogWeight
Source§fn properties() -> SemiringProperties
fn properties() -> SemiringProperties
Source§fn approx_eq(&self, other: &Self, epsilon: f64) -> bool
fn approx_eq(&self, other: &Self, epsilon: f64) -> bool
Source§fn plus_assign(&mut self, other: &Self)
fn plus_assign(&mut self, other: &Self)
Source§fn times_assign(&mut self, other: &Self)
fn times_assign(&mut self, other: &Self)
Source§impl SimdOps<LogWeight> for LogWeight
impl SimdOps<LogWeight> for LogWeight
Source§fn simd_plus(left: &[LogWeight], right: &[LogWeight], result: &mut [LogWeight])
fn simd_plus(left: &[LogWeight], right: &[LogWeight], result: &mut [LogWeight])
Source§fn simd_times(left: &[LogWeight], right: &[LogWeight], result: &mut [LogWeight])
fn simd_times(left: &[LogWeight], right: &[LogWeight], result: &mut [LogWeight])
impl StructuralPartialEq for LogWeight
Auto Trait Implementations§
impl Freeze for LogWeight
impl RefUnwindSafe for LogWeight
impl Send for LogWeight
impl Sync for LogWeight
impl Unpin for LogWeight
impl UnsafeUnpin for LogWeight
impl UnwindSafe for LogWeight
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Comparable<K> for Q
impl<Q, K> Comparable<K> for Q
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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 moreSource§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.