2 unstable releases

0.2.0 Jan 19, 2024
0.1.0 Jan 17, 2024

#432 in Simulation

MIT/Apache

25KB
485 lines

logprob

This crate defines a basic LogProb wrapper for floats. The struct is designed so that only values that are coherent for a log-probability are acceptable. This means that LogProb can store:

  • Any finite negative float value (e.g. -0.23, -32535.05, -66.0).
  • Negative infinity (corresponding to 0.0 probability)
  • 0.0 and -0.0.

The crate is intended for careful implementations of computations involving log-probabilities.

Features:

  • A way to add LogProbs (equivalent take the product of their corresponding raw probabilities)
  • Take the product of a LogProb and an unsigned integer (e.g. equivalent to p^n).
  • Ord and Eq trait on LogProb as there is no NaN.
  • A relatively efficient implementation of LogSumExp for slices and iterators.

For examples, see the documentation.


lib.rs:

This crate defines a basic LogProb wrapper for floats. The struct is designed so that only values that are coherent for a log-probability are acceptable. This means that LogProb can store: - Any finite negative float value (e.g. -0.23, -32535.05, -66.0). - Negative infinity (corresponding to 0.0 probability) - 0.0 and -0.0.

If any other value is passed, LogProb::new returns a FloatIsNanOrPositive error. You can also construct new LogProb from values in [0,1] by using LogProb::from_raw_prob

The crate also includes the ability to add log probabilities (equivalent take the product of their corresponding raw probabilities):

use logprob::LogProb;
let x = LogProb::from_raw_prob(0.5).unwrap();
let y = LogProb::from_raw_prob(0.5).unwrap();
let z = x + y;
assert_eq!(z, LogProb::from_raw_prob(0.25).unwrap());

It is also possible to take product of a LogProb and an unsigned integer, which corresponds to taking the exponent of the log-probability to the power of the integer.

let x = LogProb::from_raw_prob(0.5_f64).unwrap();
let y: u8 = 2;
let z = x * y;
assert_eq!(z, LogProb::from_raw_prob(0.25).unwrap());

Finally, the crate also includes reasonably efficient implementations of LogSumExp so that one can take the sum of raw-probabilities directly with LogProb.

let x = LogProb::from_raw_prob(0.5_f64).unwrap();
let y = LogProb::from_raw_prob(0.25).unwrap();
let z = x.add_log_prob(y).unwrap();
assert_eq!(z, LogProb::from_raw_prob(0.75).unwrap());

This can also work for slices or iterators (by importing log_sum_exp or the trait, LogSumExp respectively. Note that for empty vectors or iterators, the functions return a LogProb with negative infinity, corresponding to 0 probability.

use logprob::{LogSumExp, log_sum_exp};
let x = LogProb::from_raw_prob(0.5_f64).unwrap();
let y = LogProb::from_raw_prob(0.25).unwrap();
let z = [x,y].iter().log_sum_exp().unwrap();
assert_eq!(z, LogProb::from_raw_prob(0.75).unwrap());
let v = log_sum_exp(&[x,y]).unwrap();
assert_eq!(z, LogProb::from_raw_prob(0.75).unwrap());

By default, the both log_sum_exp and LogProb::add_log_prob return a ProbabilitiesSumToGreaterThanOne error if the sum is overflows what is a possible LogProb value. However, one can use either the clamped or float versions of these functions to return either a value clamped at 0.0 or the underlying float value which may be greater than 0.0.

let x = LogProb::from_raw_prob(0.5_f64).unwrap();
let y = LogProb::from_raw_prob(0.75).unwrap();
let z = [x,y].into_iter().log_sum_exp_clamped();
assert_eq!(z, LogProb::new(0.0).unwrap());
let z = [x,y].into_iter().log_sum_exp_float();
approx::assert_relative_eq!(z, (1.25_f64).ln());

Dependencies

~150KB