sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
pub fn boolean_network_step(state: &[bool], rules: &[Vec<(usize, bool)>]) -> Vec<bool> {
    let n = state.len();
    let mut next = vec![false; n];
    for i in 0..n {
        if i < rules.len() {
            let mut active = false;
            for &(input_idx, polarity) in &rules[i] {
                if input_idx < n && state[input_idx] == polarity {
                    active = true;
                    break;
                }
            }
            next[i] = active;
        }
    }
    next
}

pub fn boolean_network_simulate(
    initial: &[bool],
    rules: &[Vec<(usize, bool)>],
    steps: usize,
) -> Vec<Vec<bool>> {
    let mut result = Vec::with_capacity(steps + 1);
    let mut state = initial.to_vec();
    result.push(state.clone());
    for _ in 0..steps {
        state = boolean_network_step(&state, rules);
        result.push(state.clone());
    }
    result
}

pub fn gene_regulatory_hill(
    activators: &[(f64, f64, f64)],
    repressors: &[(f64, f64, f64)],
    basal: f64,
) -> f64 {
    let mut activation = basal;
    for &(conc, k, n) in activators {
        let cn = conc.powf(n);
        activation += cn / (k.powf(n) + cn);
    }
    for &(conc, k, n) in repressors {
        let cn = conc.powf(n);
        activation *= k.powf(n) / (k.powf(n) + cn);
    }
    activation
}

pub fn repressilator(
    m: &[f64; 3],
    p: &[f64; 3],
    alpha: f64,
    alpha0: f64,
    beta: f64,
    n: f64,
) -> ([f64; 3], [f64; 3]) {
    let mut dm = [0.0; 3];
    let mut dp = [0.0; 3];
    for i in 0..3 {
        let repressor = p[(i + 2) % 3];
        let rn = repressor.powf(n);
        dm[i] = -m[i] + alpha / (1.0 + rn) + alpha0;
        dp[i] = -beta * (p[i] - m[i]);
    }
    (dm, dp)
}

pub fn repressilator_simulate(
    m0: [f64; 3],
    p0: [f64; 3],
    alpha: f64,
    alpha0: f64,
    beta: f64,
    n: f64,
    dt: f64,
    steps: usize,
) -> Vec<([f64; 3], [f64; 3])> {
    let mut result = Vec::with_capacity(steps + 1);
    let mut m = m0;
    let mut p = p0;
    result.push((m, p));
    for _ in 0..steps {
        let (dm, dp) = repressilator(&m, &p, alpha, alpha0, beta, n);
        for i in 0..3 {
            m[i] = (m[i] + dm[i] * dt).max(0.0);
            p[i] = (p[i] + dp[i] * dt).max(0.0);
        }
        result.push((m, p));
    }
    result
}

pub fn genetic_toggle_switch(
    u: f64,
    v: f64,
    alpha1: f64,
    alpha2: f64,
    beta: f64,
    gamma: f64,
) -> (f64, f64) {
    let du = alpha1 / (1.0 + v.powf(beta)) - u;
    let dv = alpha2 / (1.0 + u.powf(gamma)) - v;
    (du, dv)
}

pub fn toggle_switch_simulate(
    u0: f64,
    v0: f64,
    alpha1: f64,
    alpha2: f64,
    beta: f64,
    gamma: f64,
    dt: f64,
    steps: usize,
) -> Vec<(f64, f64)> {
    let mut result = Vec::with_capacity(steps + 1);
    let mut u = u0;
    let mut v = v0;
    result.push((u, v));
    for _ in 0..steps {
        let (du, dv) = genetic_toggle_switch(u, v, alpha1, alpha2, beta, gamma);
        u = (u + du * dt).max(0.0);
        v = (v + dv * dt).max(0.0);
        result.push((u, v));
    }
    result
}

pub fn feed_forward_loop(input: f64, x: f64, k_xy: f64, k_iy: f64, n: f64, coherent: bool) -> f64 {
    let x_contrib = x.powf(n) / (k_xy.powf(n) + x.powf(n));
    let i_contrib = input.powf(n) / (k_iy.powf(n) + input.powf(n));
    if coherent {
        x_contrib * i_contrib
    } else {
        x_contrib * (1.0 - i_contrib)
    }
}

pub fn sensitivity_analysis(f: impl Fn(f64) -> f64, param: f64, delta: f64) -> f64 {
    let y0 = f(param);
    let y1 = f(param + delta);
    if y0.abs() < 1e-30 {
        return (y1 - y0) / delta;
    }
    ((y1 - y0) / delta) * (param / y0)
}

pub fn robustness_coefficient(nominal_output: f64, perturbed_outputs: &[f64]) -> f64 {
    if perturbed_outputs.is_empty() {
        return 1.0;
    }
    let mean_deviation = perturbed_outputs
        .iter()
        .map(|&p| ((p - nominal_output) / nominal_output.max(1e-30)).abs())
        .sum::<f64>()
        / perturbed_outputs.len() as f64;
    1.0 / (1.0 + mean_deviation)
}