sci_form/hf/
mo_transform.rs1use super::integrals::get_eri;
10use nalgebra::DMatrix;
11
12pub struct MoIntegrals {
17 data: Vec<f64>,
19 n_mo: usize,
21}
22
23impl MoIntegrals {
24 #[inline]
26 pub fn get(&self, p: usize, q: usize, r: usize, s: usize) -> f64 {
27 let pq = if p >= q {
29 p * (p + 1) / 2 + q
30 } else {
31 q * (q + 1) / 2 + p
32 };
33 let rs = if r >= s {
34 r * (r + 1) / 2 + s
35 } else {
36 s * (s + 1) / 2 + r
37 };
38 let (idx1, idx2) = if pq >= rs { (pq, rs) } else { (rs, pq) };
39 let index = idx1 * (idx1 + 1) / 2 + idx2;
40 if index < self.data.len() {
41 self.data[index]
42 } else {
43 0.0
44 }
45 }
46
47 #[inline]
49 pub fn antisymmetrized(&self, p: usize, q: usize, r: usize, s: usize) -> f64 {
50 self.get(p, r, q, s) - self.get(p, s, q, r)
51 }
52
53 pub fn n_mo(&self) -> usize {
55 self.n_mo
56 }
57}
58
59pub fn ao_to_mo_transform(
74 coefficients: &DMatrix<f64>,
75 ao_eris: &[f64],
76 n_ao: usize,
77 n_mo: usize,
78) -> MoIntegrals {
79 let n_pair = n_mo * (n_mo + 1) / 2;
81 let n_total = n_pair * (n_pair + 1) / 2;
82 let mut mo_eris = vec![0.0; n_total];
83
84 let mut half1 = vec![0.0; n_mo * n_ao * n_ao * n_ao];
98
99 for p in 0..n_mo {
101 for nu in 0..n_ao {
102 for lam in 0..n_ao {
103 for sig in 0..=lam {
104 let mut val = 0.0;
105 for mu in 0..n_ao {
106 let c_mp = coefficients[(mu, p)];
107 if c_mp.abs() > 1e-14 {
108 val += c_mp * get_eri(ao_eris, mu, nu, lam, sig, n_ao);
109 }
110 }
111 let idx = ((p * n_ao + nu) * n_ao + lam) * n_ao + sig;
112 half1[idx] = val;
113 let idx2 = ((p * n_ao + nu) * n_ao + sig) * n_ao + lam;
115 half1[idx2] = val;
116 }
117 }
118 }
119 }
120
121 let mut half2 = vec![0.0; n_mo * n_mo * n_ao * n_ao];
123 for p in 0..n_mo {
124 for q in 0..=p {
125 for lam in 0..n_ao {
126 for sig in 0..n_ao {
127 let mut val = 0.0;
128 for nu in 0..n_ao {
129 let c_nq = coefficients[(nu, q)];
130 if c_nq.abs() > 1e-14 {
131 let idx = ((p * n_ao + nu) * n_ao + lam) * n_ao + sig;
132 val += c_nq * half1[idx];
133 }
134 }
135 let idx2 = ((p * n_mo + q) * n_ao + lam) * n_ao + sig;
136 half2[idx2] = val;
137 }
138 }
139 }
140 }
141 drop(half1);
142
143 let mut half3 = vec![0.0; n_mo * n_mo * n_mo * n_ao];
145 for p in 0..n_mo {
146 for q in 0..=p {
147 for r in 0..n_mo {
148 for sig in 0..n_ao {
149 let mut val = 0.0;
150 for lam in 0..n_ao {
151 let c_lr = coefficients[(lam, r)];
152 if c_lr.abs() > 1e-14 {
153 let idx = ((p * n_mo + q) * n_ao + lam) * n_ao + sig;
154 val += c_lr * half2[idx];
155 }
156 }
157 let idx3 = ((p * n_mo + q) * n_mo + r) * n_ao + sig;
158 half3[idx3] = val;
159 }
160 }
161 }
162 }
163 drop(half2);
164
165 for p in 0..n_mo {
167 for q in 0..=p {
168 let pq = p * (p + 1) / 2 + q;
169 for r in 0..n_mo {
170 for s in 0..=r {
171 let rs = r * (r + 1) / 2 + s;
172 if pq < rs {
173 continue; }
175 let mut val = 0.0;
176 for sig in 0..n_ao {
177 let c_ss = coefficients[(sig, s)];
178 if c_ss.abs() > 1e-14 {
179 let idx = ((p * n_mo + q) * n_mo + r) * n_ao + sig;
180 val += c_ss * half3[idx];
181 }
182 }
183 let index = pq * (pq + 1) / 2 + rs;
184 mo_eris[index] = val;
185 }
186 }
187 }
188 }
189
190 MoIntegrals {
191 data: mo_eris,
192 n_mo,
193 }
194}
195
196#[cfg(feature = "parallel")]
198pub fn ao_to_mo_transform_parallel(
199 coefficients: &DMatrix<f64>,
200 ao_eris: &[f64],
201 n_ao: usize,
202 n_mo: usize,
203) -> MoIntegrals {
204 use rayon::prelude::*;
205
206 let n_pair = n_mo * (n_mo + 1) / 2;
207 let n_total = n_pair * (n_pair + 1) / 2;
208
209 let half1: Vec<Vec<f64>> = (0..n_mo)
211 .into_par_iter()
212 .map(|p| {
213 let mut slice = vec![0.0; n_ao * n_ao * n_ao];
214 for nu in 0..n_ao {
215 for lam in 0..n_ao {
216 for sig in 0..=lam {
217 let mut val = 0.0;
218 for mu in 0..n_ao {
219 let c_mp = coefficients[(mu, p)];
220 if c_mp.abs() > 1e-14 {
221 val += c_mp * get_eri(ao_eris, mu, nu, lam, sig, n_ao);
222 }
223 }
224 slice[nu * n_ao * n_ao + lam * n_ao + sig] = val;
225 slice[nu * n_ao * n_ao + sig * n_ao + lam] = val;
226 }
227 }
228 }
229 slice
230 })
231 .collect();
232
233 let mut half2 = vec![0.0; n_mo * n_mo * n_ao * n_ao];
235 for p in 0..n_mo {
236 for q in 0..=p {
237 for lam in 0..n_ao {
238 for sig in 0..n_ao {
239 let mut val = 0.0;
240 for nu in 0..n_ao {
241 let c_nq = coefficients[(nu, q)];
242 if c_nq.abs() > 1e-14 {
243 val += c_nq * half1[p][nu * n_ao * n_ao + lam * n_ao + sig];
244 }
245 }
246 half2[((p * n_mo + q) * n_ao + lam) * n_ao + sig] = val;
247 }
248 }
249 }
250 }
251 drop(half1);
252
253 let mut half3 = vec![0.0; n_mo * n_mo * n_mo * n_ao];
255 for p in 0..n_mo {
256 for q in 0..=p {
257 for r in 0..n_mo {
258 for sig in 0..n_ao {
259 let mut val = 0.0;
260 for lam in 0..n_ao {
261 let c_lr = coefficients[(lam, r)];
262 if c_lr.abs() > 1e-14 {
263 val += c_lr * half2[((p * n_mo + q) * n_ao + lam) * n_ao + sig];
264 }
265 }
266 half3[((p * n_mo + q) * n_mo + r) * n_ao + sig] = val;
267 }
268 }
269 }
270 }
271 drop(half2);
272
273 let mut mo_eris = vec![0.0; n_total];
275 for p in 0..n_mo {
276 for q in 0..=p {
277 let pq = p * (p + 1) / 2 + q;
278 for r in 0..n_mo {
279 for s in 0..=r {
280 let rs = r * (r + 1) / 2 + s;
281 if pq < rs {
282 continue;
283 }
284 let mut val = 0.0;
285 for sig in 0..n_ao {
286 let c_ss = coefficients[(sig, s)];
287 if c_ss.abs() > 1e-14 {
288 val += c_ss * half3[((p * n_mo + q) * n_mo + r) * n_ao + sig];
289 }
290 }
291 mo_eris[pq * (pq + 1) / 2 + rs] = val;
292 }
293 }
294 }
295 }
296
297 MoIntegrals {
298 data: mo_eris,
299 n_mo,
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn test_mo_integrals_symmetry() {
309 let s = std::f64::consts::FRAC_1_SQRT_2;
311 let c = DMatrix::from_row_slice(2, 2, &[s, s, s, -s]);
312 let eris = vec![0.5; 16];
314 let mo = ao_to_mo_transform(&c, &eris, 2, 2);
315 assert!((mo.get(0, 0, 1, 1) - mo.get(1, 1, 0, 0)).abs() < 1e-10);
317 }
318}