sciforge-lib 0.0.4

Scientific computing library — mathematics, physics, chemistry, biology, astronomy, geology, meteorology.
Documentation
pub fn hamming_distance(a: &[u8], b: &[u8]) -> usize {
    a.iter().zip(b.iter()).filter(|&(&x, &y)| x != y).count()
}

pub fn p_distance(a: &[u8], b: &[u8]) -> f64 {
    let d = hamming_distance(a, b);
    d as f64 / a.len() as f64
}

pub fn jukes_cantor(p: f64) -> f64 {
    if p >= 0.75 {
        return f64::INFINITY;
    }
    -0.75 * (1.0 - 4.0 * p / 3.0).ln()
}

pub fn kimura_2p(transitions: f64, transversions: f64, length: f64) -> f64 {
    let p = transitions / length;
    let q = transversions / length;
    let t1 = 1.0 - 2.0 * p - q;
    let t2 = 1.0 - 2.0 * q;
    if t1 <= 0.0 || t2 <= 0.0 {
        return f64::INFINITY;
    }
    -0.5 * t1.ln() - 0.25 * t2.ln()
}

pub fn count_transitions_transversions(a: &[u8], b: &[u8]) -> (usize, usize) {
    let mut transitions = 0;
    let mut transversions = 0;
    let purines = [b'A', b'G'];
    let pyrimidines = [b'C', b'T'];
    for (&x, &y) in a.iter().zip(b.iter()) {
        if x == y {
            continue;
        }
        let x_purine = purines.contains(&x);
        let y_purine = purines.contains(&y);
        let x_pyrimidine = pyrimidines.contains(&x);
        let y_pyrimidine = pyrimidines.contains(&y);
        if (x_purine && y_purine) || (x_pyrimidine && y_pyrimidine) {
            transitions += 1;
        } else {
            transversions += 1;
        }
    }
    (transitions, transversions)
}

pub fn distance_matrix(sequences: &[&[u8]]) -> Vec<Vec<f64>> {
    let n = sequences.len();
    let mut matrix = vec![vec![0.0; n]; n];
    for i in 0..n {
        for j in (i + 1)..n {
            let p = p_distance(sequences[i], sequences[j]);
            let d = jukes_cantor(p);
            matrix[i][j] = d;
            matrix[j][i] = d;
        }
    }
    matrix
}

pub fn log_det_distance(freq_matrix: &[[f64; 4]; 4]) -> f64 {
    let det = determinant_4x4(freq_matrix);
    if det <= 0.0 {
        return f64::INFINITY;
    }
    -det.ln() / 4.0
}

fn determinant_4x4(m: &[[f64; 4]; 4]) -> f64 {
    let mut result = 0.0;
    for j in 0..4 {
        let mut sub = [[0.0; 3]; 3];
        for i in 1..4 {
            let mut col = 0;
            for (k, &val) in m[i].iter().enumerate() {
                if k == j {
                    continue;
                }
                sub[i - 1][col] = val;
                col += 1;
            }
        }
        let minor = sub[0][0] * (sub[1][1] * sub[2][2] - sub[1][2] * sub[2][1])
            - sub[0][1] * (sub[1][0] * sub[2][2] - sub[1][2] * sub[2][0])
            + sub[0][2] * (sub[1][0] * sub[2][1] - sub[1][1] * sub[2][0]);
        let sign = if j % 2 == 0 { 1.0 } else { -1.0 };
        result += sign * m[0][j] * minor;
    }
    result
}