use super::integrals::get_eri;
use nalgebra::DMatrix;
pub struct MoIntegrals {
data: Vec<f64>,
n_mo: usize,
}
impl MoIntegrals {
#[inline]
pub fn get(&self, p: usize, q: usize, r: usize, s: usize) -> f64 {
let pq = if p >= q {
p * (p + 1) / 2 + q
} else {
q * (q + 1) / 2 + p
};
let rs = if r >= s {
r * (r + 1) / 2 + s
} else {
s * (s + 1) / 2 + r
};
let (idx1, idx2) = if pq >= rs { (pq, rs) } else { (rs, pq) };
let index = idx1 * (idx1 + 1) / 2 + idx2;
if index < self.data.len() {
self.data[index]
} else {
0.0
}
}
#[inline]
pub fn antisymmetrized(&self, p: usize, q: usize, r: usize, s: usize) -> f64 {
self.get(p, r, q, s) - self.get(p, s, q, r)
}
pub fn n_mo(&self) -> usize {
self.n_mo
}
}
pub fn ao_to_mo_transform(
coefficients: &DMatrix<f64>,
ao_eris: &[f64],
n_ao: usize,
n_mo: usize,
) -> MoIntegrals {
let n_pair = n_mo * (n_mo + 1) / 2;
let n_total = n_pair * (n_pair + 1) / 2;
let mut mo_eris = vec![0.0; n_total];
let mut half1 = vec![0.0; n_mo * n_ao * n_ao * n_ao];
for p in 0..n_mo {
for nu in 0..n_ao {
for lam in 0..n_ao {
for sig in 0..=lam {
let mut val = 0.0;
for mu in 0..n_ao {
let c_mp = coefficients[(mu, p)];
if c_mp.abs() > 1e-14 {
val += c_mp * get_eri(ao_eris, mu, nu, lam, sig, n_ao);
}
}
let idx = ((p * n_ao + nu) * n_ao + lam) * n_ao + sig;
half1[idx] = val;
let idx2 = ((p * n_ao + nu) * n_ao + sig) * n_ao + lam;
half1[idx2] = val;
}
}
}
}
let mut half2 = vec![0.0; n_mo * n_mo * n_ao * n_ao];
for p in 0..n_mo {
for q in 0..=p {
for lam in 0..n_ao {
for sig in 0..n_ao {
let mut val = 0.0;
for nu in 0..n_ao {
let c_nq = coefficients[(nu, q)];
if c_nq.abs() > 1e-14 {
let idx = ((p * n_ao + nu) * n_ao + lam) * n_ao + sig;
val += c_nq * half1[idx];
}
}
let idx2 = ((p * n_mo + q) * n_ao + lam) * n_ao + sig;
half2[idx2] = val;
}
}
}
}
drop(half1);
let mut half3 = vec![0.0; n_mo * n_mo * n_mo * n_ao];
for p in 0..n_mo {
for q in 0..=p {
for r in 0..n_mo {
for sig in 0..n_ao {
let mut val = 0.0;
for lam in 0..n_ao {
let c_lr = coefficients[(lam, r)];
if c_lr.abs() > 1e-14 {
let idx = ((p * n_mo + q) * n_ao + lam) * n_ao + sig;
val += c_lr * half2[idx];
}
}
let idx3 = ((p * n_mo + q) * n_mo + r) * n_ao + sig;
half3[idx3] = val;
}
}
}
}
drop(half2);
for p in 0..n_mo {
for q in 0..=p {
let pq = p * (p + 1) / 2 + q;
for r in 0..n_mo {
for s in 0..=r {
let rs = r * (r + 1) / 2 + s;
if pq < rs {
continue; }
let mut val = 0.0;
for sig in 0..n_ao {
let c_ss = coefficients[(sig, s)];
if c_ss.abs() > 1e-14 {
let idx = ((p * n_mo + q) * n_mo + r) * n_ao + sig;
val += c_ss * half3[idx];
}
}
let index = pq * (pq + 1) / 2 + rs;
mo_eris[index] = val;
}
}
}
}
MoIntegrals {
data: mo_eris,
n_mo,
}
}
#[cfg(feature = "parallel")]
pub fn ao_to_mo_transform_parallel(
coefficients: &DMatrix<f64>,
ao_eris: &[f64],
n_ao: usize,
n_mo: usize,
) -> MoIntegrals {
use rayon::prelude::*;
let n_pair = n_mo * (n_mo + 1) / 2;
let n_total = n_pair * (n_pair + 1) / 2;
let half1: Vec<Vec<f64>> = (0..n_mo)
.into_par_iter()
.map(|p| {
let mut slice = vec![0.0; n_ao * n_ao * n_ao];
for nu in 0..n_ao {
for lam in 0..n_ao {
for sig in 0..=lam {
let mut val = 0.0;
for mu in 0..n_ao {
let c_mp = coefficients[(mu, p)];
if c_mp.abs() > 1e-14 {
val += c_mp * get_eri(ao_eris, mu, nu, lam, sig, n_ao);
}
}
slice[nu * n_ao * n_ao + lam * n_ao + sig] = val;
slice[nu * n_ao * n_ao + sig * n_ao + lam] = val;
}
}
}
slice
})
.collect();
let mut half2 = vec![0.0; n_mo * n_mo * n_ao * n_ao];
for p in 0..n_mo {
for q in 0..=p {
for lam in 0..n_ao {
for sig in 0..n_ao {
let mut val = 0.0;
for nu in 0..n_ao {
let c_nq = coefficients[(nu, q)];
if c_nq.abs() > 1e-14 {
val += c_nq * half1[p][nu * n_ao * n_ao + lam * n_ao + sig];
}
}
half2[((p * n_mo + q) * n_ao + lam) * n_ao + sig] = val;
}
}
}
}
drop(half1);
let mut half3 = vec![0.0; n_mo * n_mo * n_mo * n_ao];
for p in 0..n_mo {
for q in 0..=p {
for r in 0..n_mo {
for sig in 0..n_ao {
let mut val = 0.0;
for lam in 0..n_ao {
let c_lr = coefficients[(lam, r)];
if c_lr.abs() > 1e-14 {
val += c_lr * half2[((p * n_mo + q) * n_ao + lam) * n_ao + sig];
}
}
half3[((p * n_mo + q) * n_mo + r) * n_ao + sig] = val;
}
}
}
}
drop(half2);
let mut mo_eris = vec![0.0; n_total];
for p in 0..n_mo {
for q in 0..=p {
let pq = p * (p + 1) / 2 + q;
for r in 0..n_mo {
for s in 0..=r {
let rs = r * (r + 1) / 2 + s;
if pq < rs {
continue;
}
let mut val = 0.0;
for sig in 0..n_ao {
let c_ss = coefficients[(sig, s)];
if c_ss.abs() > 1e-14 {
val += c_ss * half3[((p * n_mo + q) * n_mo + r) * n_ao + sig];
}
}
mo_eris[pq * (pq + 1) / 2 + rs] = val;
}
}
}
}
MoIntegrals {
data: mo_eris,
n_mo,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mo_integrals_symmetry() {
let s = std::f64::consts::FRAC_1_SQRT_2;
let c = DMatrix::from_row_slice(2, 2, &[s, s, s, -s]);
let eris = vec![0.5; 16];
let mo = ao_to_mo_transform(&c, &eris, 2, 2);
assert!((mo.get(0, 0, 1, 1) - mo.get(1, 1, 0, 0)).abs() < 1e-10);
}
}