zoe 0.0.30

A nightly library for viral genomics
Documentation
//! Structs and algorithms for profile Hidden Markov Models (pHMMs).
//!
//! ## Log Space Parameters
//!
//! Internally, *Zoe* stores pHMM parameters in log space with $-\ln(\cdot)$.
//! This allows for algorithms to be performed in a numerically stable manner,
//! while also avoiding repeated logarithm computations.
//!
//! When the input model is probabilistic (so that all parameters are in
//! `0..=1`), this will result in stored parameters in `0..=INFINITY`. *Zoe*
//! does not currently provide functionality for parameters outside of this
//! range ([`SamHmmParser`] will return an error). If this is required
//! functionality, please raise an issue (supporting it will require more
//! careful handling of NaNs and negative infinity values).
//!
//! [`SamHmmParser`]: sam_parser::SamHmmParser

use crate::math::{CastAs, CastAsNumeric, CastFrom, CastFromNumeric, Float};
use std::{
    fmt::Display,
    ops::{Add, AddAssign, Mul},
};

mod errors;
mod models;
pub mod modules;
pub mod sam_parser;
pub(crate) mod state;
mod traits;
mod viterbi;

#[cfg(feature = "alignment-diagnostics")]
pub mod editing;

#[cfg(not(feature = "alignment-diagnostics"))]
#[doc(auto_cfg(hide(feature = "alignment-diagnostics")))]
pub(crate) mod indexing;
#[cfg(feature = "alignment-diagnostics")]
pub mod indexing;

#[cfg(feature = "alignment-diagnostics")]
pub mod score_from_path;

#[cfg(not(feature = "alignment-diagnostics"))]
#[doc(auto_cfg(hide(feature = "alignment-diagnostics")))]
pub(crate) mod views;
#[cfg(feature = "alignment-diagnostics")]
pub mod views;

#[cfg(feature = "alignment-diagnostics")]
pub mod visit_params;

pub use errors::*;
pub use models::*;

#[cfg(feature = "alignment-diagnostics")]
pub use state::*;

/// A trait for numeric types compatible with pHMMs.
///
/// These numeric types are used for performing pHMM calculations in negative
/// log space.
pub trait PhmmNumber:
    Copy
    + Add<Output = Self>
    + Mul<Output = Self>
    + AddAssign
    + PartialOrd
    + CastAs
    + CastAsNumeric
    + CastFrom
    + CastFromNumeric
    + Default
    + Display {
    /// Infinity, the negative log space score corresponding to probability zero
    const INFINITY: Self;
    /// Zero, the negative log space score corresponding to probability one
    const ZERO: Self;

    /// Converts a probability (a floating point value) into negative log space
    fn from_prob<T: Float>(prob: T) -> Self;

    /// Converts a negative log space score back to a probability
    fn to_prob<T: Float>(self) -> T;

    /// Returns the negative log space score as a float
    fn to_float<T: Float>(self) -> T;

    /// Computes the minimum of two negative log space scores
    #[must_use]
    fn min(self, other: Self) -> Self;
}

impl PhmmNumber for f32 {
    const INFINITY: Self = f32::INFINITY;
    const ZERO: Self = 0.0;

    #[inline]
    fn from_prob<T: Float>(prob: T) -> Self {
        // Increase precision by converting to f32 last
        let param = (-prob.ln()).cast_as::<f32>();
        if param.is_nan() { Self::INFINITY } else { param }
    }

    #[inline]
    fn to_prob<T: Float>(self) -> T {
        // Increase precision by converting from f32 first
        (-T::cast_from(self)).exp()
    }

    #[inline]
    fn to_float<T: Float>(self) -> T {
        T::cast_from(self)
    }

    #[inline]
    fn min(self, other: Self) -> Self {
        self.min(other)
    }
}

impl PhmmNumber for f64 {
    const INFINITY: Self = f64::INFINITY;
    const ZERO: Self = 0.0;

    #[inline]
    fn from_prob<T: Float>(prob: T) -> Self {
        // Increase precision by converting to f64 first
        let param = -prob.cast_as::<f64>().ln();
        if param.is_nan() { Self::INFINITY } else { param }
    }

    #[inline]
    fn to_prob<T: Float>(self) -> T {
        // Increase precision by converting from f64 last
        T::cast_from((-self).exp())
    }

    #[inline]
    fn to_float<T: Float>(self) -> T {
        T::cast_from(self)
    }

    #[inline]
    fn min(self, other: Self) -> Self {
        self.min(other)
    }
}