use rand::RngExt;
use crate::conformer::distgeom::BoundsMatrix;
const EIGVAL_TOL: f64 = 0.001;
pub fn pick_random_dist_mat<R: RngExt + ?Sized>(bounds: &BoundsMatrix, rng: &mut R) -> Vec<f64> {
let n = bounds.len();
let mut dist = vec![0.0; n * n];
for i in 1..n {
for j in 0..i {
let ub = bounds.upper(i, j);
let lb = bounds.lower(i, j);
let r: f64 = rng.random::<f64>();
let d = lb + r * (ub - lb);
dist[i * n + j] = d;
dist[j * n + i] = d;
}
}
dist
}
pub fn compute_initial_coords<R: RngExt + ?Sized>(
dist: &[f64],
n: usize,
dim: usize,
rng: &mut R,
rand_neg_eig: bool,
num_zero_fail: usize,
) -> Option<Vec<f64>> {
let mut sq = vec![0.0; n * n];
let mut sum_sq = 0.0;
for k in 0..n * n {
sq[k] = dist[k] * dist[k];
sum_sq += sq[k];
}
sum_sq /= (n * n) as f64;
let mut sq_d0i = vec![0.0; n];
for i in 0..n {
let mut acc = 0.0;
for j in 0..n {
acc += sq[i * n + j];
}
acc /= n as f64;
acc -= sum_sq;
if acc < EIGVAL_TOL && n > 3 {
return None;
}
sq_d0i[i] = acc;
}
let mut t = vec![0.0; n * n];
for i in 0..n {
for j in 0..n {
t[i * n + j] = 0.5 * (sq_d0i[i] + sq_d0i[j] - sq[i * n + j]);
}
}
let (eigvals, eigvecs) = jacobi_eigen(&t, n);
let mut order: Vec<usize> = (0..n).collect();
order.sort_by(|&a, &b| eigvals[b].partial_cmp(&eigvals[a]).unwrap());
let mut found_neg = false;
let mut zero_eigs = 0usize;
let mut scale = vec![0.0; dim];
let mut neg_dim = vec![false; dim];
for d in 0..dim {
let ev = if d < n { eigvals[order[d]] } else { 0.0 };
if ev > EIGVAL_TOL {
scale[d] = ev.sqrt();
} else if ev.abs() < EIGVAL_TOL {
scale[d] = 0.0;
zero_eigs += 1;
} else {
found_neg = true;
neg_dim[d] = true;
}
}
if found_neg && !rand_neg_eig {
return None;
}
if zero_eigs >= num_zero_fail && n > 3 {
return None;
}
let mut coords = vec![0.0; n * dim];
for i in 0..n {
for d in 0..dim {
if !neg_dim[d] {
let vec_comp = if d < n {
eigvecs[order[d] * n + i]
} else {
0.0
};
coords[i * dim + d] = scale[d] * vec_comp;
} else {
coords[i * dim + d] = 1.0 - 2.0 * rng.random::<f64>();
}
}
}
Some(coords)
}
pub fn compute_random_coords<R: RngExt + ?Sized>(
n: usize,
dim: usize,
box_size: f64,
rng: &mut R,
) -> Vec<f64> {
let mut coords = vec![0.0; n * dim];
for c in coords.iter_mut() {
*c = box_size * (rng.random::<f64>() - 0.5);
}
coords
}
fn jacobi_eigen(a: &[f64], n: usize) -> (Vec<f64>, Vec<f64>) {
let mut m = a.to_vec();
let mut v = vec![0.0; n * n];
for i in 0..n {
v[i * n + i] = 1.0;
}
if n == 0 {
return (Vec::new(), v);
}
let max_sweeps = 100;
for _ in 0..max_sweeps {
let mut off = 0.0;
for p in 0..n {
for q in (p + 1)..n {
off += m[p * n + q] * m[p * n + q];
}
}
if off < 1e-30 {
break;
}
for p in 0..n {
for q in (p + 1)..n {
let apq = m[p * n + q];
if apq.abs() < 1e-300 {
continue;
}
let app = m[p * n + p];
let aqq = m[q * n + q];
let theta = (aqq - app) / (2.0 * apq);
let t = theta.signum() / (theta.abs() + (theta * theta + 1.0).sqrt());
let c = 1.0 / (t * t + 1.0).sqrt();
let s = t * c;
for k in 0..n {
let akp = m[k * n + p];
let akq = m[k * n + q];
m[k * n + p] = c * akp - s * akq;
m[k * n + q] = s * akp + c * akq;
}
for k in 0..n {
let apk = m[p * n + k];
let aqk = m[q * n + k];
m[p * n + k] = c * apk - s * aqk;
m[q * n + k] = s * apk + c * aqk;
}
for k in 0..n {
let vkp = v[k * n + p];
let vkq = v[k * n + q];
v[k * n + p] = c * vkp - s * vkq;
v[k * n + q] = s * vkp + c * vkq;
}
}
}
}
let eigvals: Vec<f64> = (0..n).map(|i| m[i * n + i]).collect();
let mut eigvecs = vec![0.0; n * n];
for k in 0..n {
for i in 0..n {
eigvecs[k * n + i] = v[i * n + k];
}
}
(eigvals, eigvecs)
}