sci-form 0.15.2

High-performance 3D molecular conformer generation using ETKDG distance geometry
Documentation
#![allow(
    unused_imports,
    unused_variables,
    dead_code,
    clippy::unnecessary_cast,
    clippy::needless_range_loop,
    clippy::manual_repeat_n,
    clippy::manual_str_repeat,
    clippy::manual_is_multiple_of,
    clippy::redundant_field_names,
    clippy::useless_vec,
    clippy::single_range_in_vec_init
)]
use sci_form::distgeom::bounds::{calculate_bounds_matrix_opts, triangle_smooth_tol};
use sci_form::graph::Molecule;

#[test]
fn test_compare_bounds_matrix() {
    let smiles = "C#CC(C)(COCCC1CCCNCC1)OCC(C)=O";
    let mol = Molecule::from_smiles(smiles).unwrap();
    let n = mol.graph.node_count();

    println!("Molecule: {}", smiles);
    println!("Atoms: {}", n);

    let raw = calculate_bounds_matrix_opts(&mol, true);
    let mut bounds = raw;
    let smooth_ok = triangle_smooth_tol(&mut bounds, 0.0);
    println!(
        "Triangle smooth: {}",
        if smooth_ok { "OK" } else { "FAILED" }
    );

    let rdkit_bounds_path = "/tmp/rdkit_bounds.npy";
    let data = std::fs::read(rdkit_bounds_path).expect("Run Python script first");

    let major = data[6] as u16;
    let header_len = if major >= 2 {
        u32::from_le_bytes([data[8], data[9], data[10], data[11]]) as usize
    } else {
        u16::from_le_bytes([data[8], data[9]]) as usize
    };
    let data_start = if major >= 2 {
        12 + header_len
    } else {
        10 + header_len
    };
    let float_data = &data[data_start..];

    let rdkit_bounds: Vec<f64> = float_data
        .chunks_exact(8)
        .map(|chunk| f64::from_le_bytes(chunk.try_into().unwrap()))
        .collect();

    assert_eq!(rdkit_bounds.len(), n * n, "Size mismatch");

    let mut max_ub_diff = 0.0f64;
    let mut max_lb_diff = 0.0f64;
    let mut ub_diffs = 0;
    let mut lb_diffs = 0;
    let mut total_pairs = 0;

    for i in 0..n {
        for j in (i + 1)..n {
            let our_ub = bounds[(i, j)];
            let our_lb = bounds[(j, i)];
            let rdkit_ub = rdkit_bounds[i * n + j];
            let rdkit_lb = rdkit_bounds[j * n + i];

            let ub_diff = (our_ub - rdkit_ub).abs();
            let lb_diff = (our_lb - rdkit_lb).abs();

            if ub_diff > 1e-10 {
                ub_diffs += 1;
            }
            if lb_diff > 1e-10 {
                lb_diffs += 1;
            }
            if ub_diff > max_ub_diff {
                max_ub_diff = ub_diff;
            }
            if lb_diff > max_lb_diff {
                max_lb_diff = lb_diff;
            }
            total_pairs += 1;

            if ub_diff > 0.001 || lb_diff > 0.001 {
                println!("  DIFF ({},{}): our_ub={:.10}, rdkit_ub={:.10}, d={:.2e}; our_lb={:.10}, rdkit_lb={:.10}, d={:.2e}",
                    i, j, our_ub, rdkit_ub, ub_diff, our_lb, rdkit_lb, lb_diff);
            }
        }
    }

    println!("\n=== Bounds matrix comparison ===");
    println!("Total pairs: {}", total_pairs);
    println!("UB diffs (>1e-10): {}", ub_diffs);
    println!("LB diffs (>1e-10): {}", lb_diffs);
    println!("Max UB diff: {:.2e}", max_ub_diff);
    println!("Max LB diff: {:.2e}", max_lb_diff);

    let mut our_ub_sum = 0.0f64;
    let mut our_lb_sum = 0.0f64;
    for i in 0..n {
        for j in (i + 1)..n {
            our_ub_sum += bounds[(i, j)];
            our_lb_sum += bounds[(j, i)];
        }
    }
    println!("\nChecksums:");
    println!("  Our UB sum:   {:.10}", our_ub_sum);
    println!("  RDKit UB sum: 7872.4329018951");
    println!("  Our LB sum:   {:.10}", our_lb_sum);
    println!("  RDKit LB sum: 2654.3162734026");

    println!("\n=== First row of our bounds matrix ===");
    for j in 1..std::cmp::min(n, 20) {
        println!(
            "  bm[0,{:2}] = {:.10} (UB), bm[{:2},0] = {:.10} (LB)",
            j,
            bounds[(0, j)],
            j,
            bounds[(j, 0)]
        );
    }
}