1use std::fmt;
4use std::ops::Mul;
5
6use anyhow::{self, Context, ensure, format_err};
7use approx;
8use derive_builder::Builder;
9use itertools::{Itertools, izip};
10use log;
11use ndarray::{Array1, Array2, Axis, Ix2, s};
12use ndarray_linalg::{
13 UPLO,
14 eig::Eig,
15 eigh::Eigh,
16 solve::Determinant,
17 types::{Lapack, Scalar},
18};
19use num_complex::{Complex, ComplexFloat};
20use num_traits::{Float, ToPrimitive, Zero};
21
22use crate::analysis::{
23 EigenvalueComparisonMode, Orbit, OrbitIterator, Overlap, RepAnalysis, fn_calc_xmat_complex,
24 fn_calc_xmat_real,
25};
26use crate::angmom::spinor_rotation_3d::StructureConstraint;
27use crate::auxiliary::misc::{ProductRepeat, complex_modified_gram_schmidt};
28use crate::chartab::chartab_group::CharacterProperties;
29use crate::chartab::{DecompositionError, SubspaceDecomposable};
30use crate::group::GroupType;
31use crate::symmetry::symmetry_element::symmetry_operation::SpecialSymmetryTransformation;
32use crate::symmetry::symmetry_group::SymmetryGroupProperties;
33use crate::symmetry::symmetry_transformation::{SymmetryTransformable, SymmetryTransformationKind};
34use crate::target::determinant::SlaterDeterminant;
35use crate::target::determinant::determinant_analysis::SlaterDeterminantSymmetryOrbit;
36use crate::target::orbital::MolecularOrbital;
37
38impl<'a, T, SC> Overlap<T, Ix2> for MolecularOrbital<'a, T, SC>
43where
44 T: Lapack
45 + ComplexFloat<Real = <T as Scalar>::Real>
46 + fmt::Debug
47 + Mul<<T as ComplexFloat>::Real, Output = T>,
48 <T as ComplexFloat>::Real: fmt::Debug
49 + approx::RelativeEq<<T as ComplexFloat>::Real>
50 + approx::AbsDiffEq<Epsilon = <T as Scalar>::Real>,
51 SC: StructureConstraint + Eq + fmt::Display,
52{
53 fn complex_symmetric(&self) -> bool {
54 self.complex_symmetric
55 }
56
57 fn overlap(
66 &self,
67 other: &Self,
68 metric: Option<&Array2<T>>,
69 metric_h: Option<&Array2<T>>,
70 ) -> Result<T, anyhow::Error> {
71 ensure!(
72 self.structure_constraint == other.structure_constraint,
73 "Inconsistent spin constraints between `self` and `other`."
74 );
75 ensure!(
76 self.coefficients.len() == other.coefficients.len(),
77 "Inconsistent numbers of coefficient matrices between `self` and `other`."
78 );
79
80 let sao = metric.ok_or_else(|| format_err!("No atomic-orbital metric found."))?;
81 let sao_h = metric_h.unwrap_or(sao);
82 let ov = if self.component_index != other.component_index {
83 T::zero()
84 } else if self.complex_symmetric() {
85 match (self.complex_conjugated, other.complex_conjugated) {
86 (false, false) => self.coefficients.t().dot(sao_h).dot(&other.coefficients),
87 (true, false) => self.coefficients.t().dot(sao).dot(&other.coefficients),
88 (false, true) => other.coefficients.t().dot(sao).dot(&self.coefficients),
89 (true, true) => self
90 .coefficients
91 .t()
92 .dot(&sao_h.t())
93 .dot(&other.coefficients),
94 }
95 } else {
96 match (self.complex_conjugated, other.complex_conjugated) {
97 (false, false) => self
98 .coefficients
99 .t()
100 .mapv(|x| x.conj()) .dot(sao)
102 .dot(&other.coefficients),
103 (true, false) => self
104 .coefficients
105 .t()
106 .mapv(|x| x.conj()) .dot(sao_h)
108 .dot(&other.coefficients),
109 (false, true) => other
110 .coefficients
111 .t()
112 .mapv(|x| x.conj()) .dot(sao_h)
114 .dot(&self.coefficients)
115 .conj(),
116 (true, true) => self
117 .coefficients
118 .t()
119 .mapv(|x| x.conj()) .dot(&sao.t())
121 .dot(&other.coefficients),
122 }
123 };
124 Ok(ov)
125 }
126
127 fn overlap_definition(&self) -> String {
129 let k = if self.complex_symmetric() { "κ " } else { "" };
130 format!("⟨{k}ψ_1|ψ_2⟩ = ∫ [{k}ψ_1(x)]* ψ_2(x) dx")
131 }
132}
133
134#[derive(Builder, Clone)]
145pub struct MolecularOrbitalSymmetryOrbit<'a, G, T, SC>
146where
147 G: SymmetryGroupProperties,
148 T: ComplexFloat + fmt::Debug + Lapack,
149 SC: StructureConstraint + fmt::Display,
150 MolecularOrbital<'a, T, SC>: SymmetryTransformable,
151{
152 group: &'a G,
154
155 origin: &'a MolecularOrbital<'a, T, SC>,
157
158 integrality_threshold: <T as ComplexFloat>::Real,
161
162 pub(crate) linear_independence_threshold: <T as ComplexFloat>::Real,
164
165 symmetry_transformation_kind: SymmetryTransformationKind,
168
169 #[builder(setter(skip), default = "None")]
171 smat: Option<Array2<T>>,
172
173 #[builder(setter(skip), default = "None")]
176 pub(crate) smat_eigvals: Option<Array1<T>>,
177
178 #[builder(setter(skip), default = "None")]
183 xmat: Option<Array2<T>>,
184
185 pub(crate) eigenvalue_comparison_mode: EigenvalueComparisonMode,
188}
189
190impl<'a, G, T, SC> MolecularOrbitalSymmetryOrbit<'a, G, T, SC>
195where
196 G: SymmetryGroupProperties + Clone,
197 T: ComplexFloat + fmt::Debug + Lapack,
198 SC: StructureConstraint + Clone + fmt::Display,
199 MolecularOrbital<'a, T, SC>: SymmetryTransformable,
200{
201 pub fn builder() -> MolecularOrbitalSymmetryOrbitBuilder<'a, G, T, SC> {
203 MolecularOrbitalSymmetryOrbitBuilder::default()
204 }
205
206 pub fn from_orbitals(
221 group: &'a G,
222 orbitals: &'a [Vec<MolecularOrbital<'a, T, SC>>],
223 sym_kind: SymmetryTransformationKind,
224 eig_comp_mode: EigenvalueComparisonMode,
225 integrality_thresh: <T as ComplexFloat>::Real,
226 linear_independence_thresh: <T as ComplexFloat>::Real,
227 ) -> Vec<Vec<Self>> {
228 orbitals
229 .iter()
230 .map(|orbs_spin| {
231 orbs_spin
232 .iter()
233 .map(|orb| {
234 MolecularOrbitalSymmetryOrbit::builder()
235 .group(group)
236 .origin(orb)
237 .integrality_threshold(integrality_thresh)
238 .linear_independence_threshold(linear_independence_thresh)
239 .symmetry_transformation_kind(sym_kind.clone())
240 .eigenvalue_comparison_mode(eig_comp_mode.clone())
241 .build()
242 .expect("Unable to construct a molecular orbital symmetry orbit.")
243 })
244 .collect_vec()
245 })
246 .collect_vec()
247 }
248}
249
250impl<'a, G, SC> MolecularOrbitalSymmetryOrbit<'a, G, f64, SC>
251where
252 G: SymmetryGroupProperties,
253 SC: StructureConstraint + fmt::Display,
254 MolecularOrbital<'a, f64, SC>: SymmetryTransformable,
255{
256 fn_calc_xmat_real!(
257 pub calc_xmat
269 );
270}
271
272impl<'a, G, T, SC> MolecularOrbitalSymmetryOrbit<'a, G, Complex<T>, SC>
273where
274 G: SymmetryGroupProperties,
275 T: Float + Scalar<Complex = Complex<T>>,
276 Complex<T>: ComplexFloat<Real = T> + Scalar<Real = T, Complex = Complex<T>> + Lapack,
277 SC: StructureConstraint + fmt::Display,
278 MolecularOrbital<'a, Complex<T>, SC>: SymmetryTransformable + Overlap<Complex<T>, Ix2>,
279{
280 fn_calc_xmat_complex!(
281 pub calc_xmat
293 );
294}
295
296impl<'a, G, T, SC> Orbit<G, MolecularOrbital<'a, T, SC>>
305 for MolecularOrbitalSymmetryOrbit<'a, G, T, SC>
306where
307 G: SymmetryGroupProperties,
308 T: ComplexFloat + fmt::Debug + Lapack,
309 SC: StructureConstraint + fmt::Display,
310 MolecularOrbital<'a, T, SC>: SymmetryTransformable,
311{
312 type OrbitIter = OrbitIterator<'a, G, MolecularOrbital<'a, T, SC>>;
313
314 fn group(&self) -> &G {
315 self.group
316 }
317
318 fn origin(&self) -> &MolecularOrbital<'a, T, SC> {
319 self.origin
320 }
321
322 fn iter(&self) -> Self::OrbitIter {
323 OrbitIterator::new(
324 self.group,
325 self.origin,
326 match self.symmetry_transformation_kind {
327 SymmetryTransformationKind::Spatial => |op, orb| {
328 orb.sym_transform_spatial(op).with_context(|| {
329 format!("Unable to apply `{op}` spatially on the origin orbital")
330 })
331 },
332 SymmetryTransformationKind::SpatialWithSpinTimeReversal => |op, orb| {
333 orb.sym_transform_spatial_with_spintimerev(op).with_context(|| {
334 format!("Unable to apply `{op}` spatially (with spin-including time reversal) on the origin orbital")
335 })
336 },
337 SymmetryTransformationKind::Spin => |op, orb| {
338 orb.sym_transform_spin(op).with_context(|| {
339 format!("Unable to apply `{op}` spin-wise on the origin orbital")
340 })
341 },
342 SymmetryTransformationKind::SpinSpatial => |op, orb| {
343 orb.sym_transform_spin_spatial(op).with_context(|| {
344 format!("Unable to apply `{op}` spin-spatially on the origin orbital",)
345 })
346 },
347 },
348 )
349 }
350}
351
352impl<'a, G, T, SC> RepAnalysis<G, MolecularOrbital<'a, T, SC>, T, Ix2>
357 for MolecularOrbitalSymmetryOrbit<'a, G, T, SC>
358where
359 G: SymmetryGroupProperties,
360 G::CharTab: SubspaceDecomposable<T>,
361 T: Lapack
362 + ComplexFloat<Real = <T as Scalar>::Real>
363 + fmt::Debug
364 + Mul<<T as ComplexFloat>::Real, Output = T>,
365 <T as ComplexFloat>::Real: fmt::Debug
366 + Zero
367 + approx::RelativeEq<<T as ComplexFloat>::Real>
368 + approx::AbsDiffEq<Epsilon = <T as Scalar>::Real>,
369 SC: StructureConstraint + Eq + fmt::Display,
370 MolecularOrbital<'a, T, SC>: SymmetryTransformable,
371{
372 fn set_smat(&mut self, smat: Array2<T>) {
373 self.smat = Some(smat)
374 }
375
376 fn smat(&self) -> Option<&Array2<T>> {
377 self.smat.as_ref()
378 }
379
380 fn xmat(&self) -> &Array2<T> {
381 self.xmat
382 .as_ref()
383 .expect("Orbit overlap orthogonalisation matrix not found.")
384 }
385
386 fn norm_preserving_scalar_map(&self, i: usize) -> Result<fn(T) -> T, anyhow::Error> {
387 if self.origin.complex_symmetric {
388 Err(format_err!(
389 "`norm_preserving_scalar_map` is currently not implemented for complex-symmetric overlaps. This thus precludes the use of the Cayley table to speed up the computation of the orbit overlap matrix."
390 ))
391 } else if self
392 .group
393 .get_index(i)
394 .unwrap_or_else(|| panic!("Group operation index `{i}` not found."))
395 .contains_time_reversal()
396 {
397 Ok(ComplexFloat::conj)
398 } else {
399 Ok(|x| x)
400 }
401 }
402
403 fn integrality_threshold(&self) -> <T as ComplexFloat>::Real {
404 self.integrality_threshold
405 }
406
407 fn eigenvalue_comparison_mode(&self) -> &EigenvalueComparisonMode {
408 &self.eigenvalue_comparison_mode
409 }
410
411 fn analyse_rep(
427 &self,
428 ) -> Result<
429 <<G as CharacterProperties>::CharTab as SubspaceDecomposable<T>>::Decomposition,
430 DecompositionError,
431 > {
432 let (valid_symmetry, err_str) = match self.symmetry_transformation_kind {
434 SymmetryTransformationKind::Spatial => (true, String::new()),
435 SymmetryTransformationKind::SpatialWithSpinTimeReversal
436 | SymmetryTransformationKind::Spin
437 | SymmetryTransformationKind::SpinSpatial => {
438 match self.group().group_type() {
439 GroupType::Ordinary(_) => (true, String::new()),
440 GroupType::MagneticGrey(_) | GroupType::MagneticBlackWhite(_) => {
441 (!self.group().unitary_represented(),
442 "Unitary-represented magnetic groups cannot be used for symmetry analysis of a one-electron molecular orbital where spin is treated explicitly.".to_string())
443 }
444 }
445 }
446 };
447 if valid_symmetry {
448 log::debug!("Analysing representation symmetry for an MO...");
449 let chis = self
450 .calc_characters()
451 .map_err(|err| DecompositionError(err.to_string()))?;
452 let res = self.group().character_table().reduce_characters(
453 &chis.iter().map(|(cc, chi)| (cc, *chi)).collect::<Vec<_>>(),
454 self.integrality_threshold(),
455 );
456 log::debug!("Analysing representation symmetry for an MO... Done.");
457 res
458 } else {
459 Err(DecompositionError(err_str))
460 }
461 }
462}
463
464#[allow(clippy::too_many_arguments, clippy::type_complexity)]
497pub fn generate_det_mo_orbits<'a, G, T, SC>(
498 det: &'a SlaterDeterminant<'a, T, SC>,
499 mos: &'a [Vec<MolecularOrbital<'a, T, SC>>],
500 group: &'a G,
501 metric: &Array2<T>,
502 metric_h: Option<&Array2<T>>,
503 integrality_threshold: <T as ComplexFloat>::Real,
504 linear_independence_threshold: <T as ComplexFloat>::Real,
505 symmetry_transformation_kind: SymmetryTransformationKind,
506 eigenvalue_comparison_mode: EigenvalueComparisonMode,
507 use_cayley_table: bool,
508) -> Result<
509 (
510 SlaterDeterminantSymmetryOrbit<'a, G, T, SC>,
511 Vec<Vec<MolecularOrbitalSymmetryOrbit<'a, G, T, SC>>>,
512 ),
513 anyhow::Error,
514>
515where
516 G: SymmetryGroupProperties + Clone,
517 G::CharTab: SubspaceDecomposable<T>,
518 T: Lapack
519 + ComplexFloat<Real = <T as Scalar>::Real>
520 + fmt::Debug
521 + Mul<<T as ComplexFloat>::Real, Output = T>,
522 <T as ComplexFloat>::Real: fmt::Debug
523 + Zero
524 + From<u16>
525 + ToPrimitive
526 + approx::RelativeEq<<T as ComplexFloat>::Real>
527 + approx::AbsDiffEq<Epsilon = <T as Scalar>::Real>,
528 SC: StructureConstraint + Clone + Eq + fmt::Display,
529 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
530 MolecularOrbital<'a, T, SC>: SymmetryTransformable,
531{
532 log::debug!("Constructing determinant and MO orbits in tandem...");
533 let order = group.order();
534 let mut det_orbit = SlaterDeterminantSymmetryOrbit::<G, T, SC>::builder()
535 .group(group)
536 .origin(det)
537 .integrality_threshold(integrality_threshold)
538 .linear_independence_threshold(linear_independence_threshold)
539 .symmetry_transformation_kind(symmetry_transformation_kind.clone())
540 .eigenvalue_comparison_mode(eigenvalue_comparison_mode.clone())
541 .build()
542 .map_err(|err| format_err!(err))?;
543 let mut mo_orbitss = MolecularOrbitalSymmetryOrbit::from_orbitals(
544 group,
545 mos,
546 symmetry_transformation_kind,
547 eigenvalue_comparison_mode,
548 integrality_threshold,
549 linear_independence_threshold,
550 );
551
552 let mut det_smat = Array2::<T>::zeros((order, order));
553 let mut mo_smatss = mo_orbitss
554 .iter()
555 .map(|mo_orbits| {
556 mo_orbits
557 .iter()
558 .map(|_| Array2::<T>::zeros((order, order)))
559 .collect::<Vec<_>>()
560 })
561 .collect::<Vec<_>>();
562
563 let thresh = det.threshold();
564
565 let sao = metric;
566 let sao_h = metric_h.unwrap_or(sao);
567
568 if let (Some(ctb), true) = (group.cayley_table(), use_cayley_table) {
569 log::debug!(
570 "Cayley table available. Group closure will be used to speed up overlap matrix computation."
571 );
572 let mut det_smatw0 = Array1::<T>::zeros(order);
573 let mut mo_smatw0ss = mo_orbitss
574 .iter()
575 .map(|mo_orbits| {
576 mo_orbits
577 .iter()
578 .map(|_| Array1::<T>::zeros(order))
579 .collect::<Vec<_>>()
580 })
581 .collect::<Vec<_>>();
582 let det_0 = det_orbit.origin();
583 for (w, det_w_res) in det_orbit.iter().enumerate() {
584 let det_w = det_w_res?;
585 let w0_ov = izip!(
586 det_w.coefficients(),
587 det_w.occupations(),
588 det_0.coefficients(),
589 det_0.occupations(),
590 )
591 .enumerate()
592 .map(|(ispin, (cw, occw, c0, occ0))| {
593 let nonzero_occ_w = occw.iter().positions(|&occ| occ > thresh).collect_vec();
594 let nonzero_occ_0 = occ0.iter().positions(|&occ| occ > thresh).collect_vec();
595
596 let all_mo_w0_ov_mat = if det.complex_symmetric() {
597 match (det_w.complex_conjugated(), det_0.complex_conjugated()) {
598 (false, false) => cw.t().dot(sao_h).dot(c0),
599 (true, false) => cw.t().dot(sao).dot(c0),
600 (false, true) => c0.t().dot(sao).dot(cw),
601 (true, true) => cw.t().dot(&sao_h.t()).dot(c0),
602 }
603 } else {
604 match (det_w.complex_conjugated(), det_0.complex_conjugated()) {
605 (false, false) => cw.t().mapv(|x| x.conj()).dot(sao).dot(c0),
606 (true, false) => cw.t().mapv(|x| x.conj()).dot(sao_h).dot(c0),
607 (false, true) => c0
608 .t()
609 .mapv(|x| x.conj())
610 .dot(sao_h)
611 .dot(cw)
612 .mapv(|x| x.conj()),
613 (true, true) => cw.t().mapv(|x| x.conj()).dot(&sao.t()).dot(c0),
614 }
615 };
616
617 all_mo_w0_ov_mat
618 .diag()
619 .iter()
620 .enumerate()
621 .for_each(|(imo, mo_w0_ov)| {
622 mo_smatw0ss[ispin][imo][w] = *mo_w0_ov;
623 });
624
625 let occ_mo_w0_ov_mat = all_mo_w0_ov_mat
626 .select(Axis(0), &nonzero_occ_w)
627 .select(Axis(1), &nonzero_occ_0);
628
629 occ_mo_w0_ov_mat
630 .det()
631 .expect("The determinant of the MO overlap matrix `w0` could not be found.")
632 })
633 .fold(T::one(), |acc, x| acc * x);
634
635 let implicit_factor = det.structure_constraint().implicit_factor()?;
636 let w0_ov = if implicit_factor > 1 {
637 let p_i32 = i32::try_from(implicit_factor)?;
638 ComplexFloat::powi(w0_ov, p_i32)
639 } else {
640 w0_ov
641 };
642 det_smatw0[w] = w0_ov;
643 }
644
645 for (i, j) in (0..order).cartesian_product(0..order) {
646 let jinv = ctb
647 .slice(s![.., j])
648 .iter()
649 .position(|&x| x == 0)
650 .ok_or(format_err!(
651 "Unable to find the inverse of group element `{j}`."
652 ))?;
653 let jinv_i = ctb[(jinv, i)];
654
655 for (ispin, mo_smatw0s) in mo_smatw0ss.iter().enumerate() {
656 for (imo, mo_smat_w0) in mo_smatw0s.iter().enumerate() {
657 mo_smatss[ispin][imo][(i, j)] =
658 mo_orbitss[ispin][imo].norm_preserving_scalar_map(jinv)?(mo_smat_w0[jinv_i])
659 }
660 }
661
662 det_smat[(i, j)] = det_orbit.norm_preserving_scalar_map(jinv)?(det_smatw0[jinv_i]);
663 }
664 } else {
665 log::debug!(
666 "Cayley table not available or the use of Cayley table not requested. Overlap matrix will be constructed without group-closure speed-up."
667 );
668 let indexed_dets = det_orbit
669 .iter()
670 .map(|det_res| det_res.map_err(|err| err.to_string()))
671 .enumerate()
672 .collect::<Vec<_>>();
673 for det_pair in indexed_dets.iter().product_repeat(2) {
674 let (w, det_w_res) = &det_pair[0];
675 let (x, det_x_res) = &det_pair[1];
676 let det_w = det_w_res
677 .as_ref()
678 .map_err(|err| format_err!(err.to_owned()))
679 .with_context(|| "One of the determinants in the orbit is not available")?;
680 let det_x = det_x_res
681 .as_ref()
682 .map_err(|err| format_err!(err.to_owned()))
683 .with_context(|| "One of the determinants in the orbit is not available")?;
684
685 let wx_ov = izip!(
686 det_w.coefficients(),
687 det_w.occupations(),
688 det_x.coefficients(),
689 det_x.occupations(),
690 )
691 .enumerate()
692 .map(|(ispin, (cw, occw, cx, occx))| {
693 let nonzero_occ_w = occw.iter().positions(|&occ| occ > thresh).collect_vec();
694 let nonzero_occ_x = occx.iter().positions(|&occ| occ > thresh).collect_vec();
695
696 let all_mo_wx_ov_mat = if det.complex_symmetric() {
702 match (det_w.complex_conjugated(), det_x.complex_conjugated()) {
703 (false, false) => cw.t().dot(sao_h).dot(cx),
704 (true, false) => cw.t().dot(sao).dot(cx),
705 (false, true) => cx.t().dot(sao).dot(cw),
706 (true, true) => cw.t().dot(&sao_h.t()).dot(cx),
707 }
708 } else {
709 match (det_w.complex_conjugated(), det_x.complex_conjugated()) {
710 (false, false) => cw.t().mapv(|x| x.conj()).dot(sao).dot(cx),
711 (true, false) => cw.t().mapv(|x| x.conj()).dot(sao_h).dot(cx),
712 (false, true) => cx
713 .t()
714 .mapv(|x| x.conj())
715 .dot(sao_h)
716 .dot(cw)
717 .mapv(|x| x.conj()),
718 (true, true) => cw.t().mapv(|x| x.conj()).dot(&sao.t()).dot(cx),
719 }
720 };
721
722 all_mo_wx_ov_mat
723 .diag()
724 .iter()
725 .enumerate()
726 .for_each(|(imo, mo_wx_ov)| {
727 mo_smatss[ispin][imo][(*w, *x)] = *mo_wx_ov;
728 });
729
730 let occ_mo_wx_ov_mat = all_mo_wx_ov_mat
731 .select(Axis(0), &nonzero_occ_w)
732 .select(Axis(1), &nonzero_occ_x);
733
734 occ_mo_wx_ov_mat
735 .det()
736 .expect("The determinant of the MO overlap matrix could not be found.")
737 })
738 .fold(T::one(), |acc, x| acc * x);
739
740 let implicit_factor = det.structure_constraint().implicit_factor()?;
741 let wx_ov = if implicit_factor > 1 {
742 let p_i32 = i32::try_from(implicit_factor)?;
743 ComplexFloat::powi(wx_ov, p_i32)
744 } else {
745 wx_ov
746 };
747 det_smat[(*w, *x)] = wx_ov;
748 }
749 }
750
751 if det_orbit.origin().complex_symmetric() {
752 det_orbit.set_smat(
753 (det_smat.clone() + det_smat.t().to_owned()).mapv(|x| x / (T::one() + T::one())),
754 );
755 } else {
756 det_orbit.set_smat(
757 (det_smat.clone() + det_smat.t().to_owned().mapv(|x| x.conj()))
758 .mapv(|x| x / (T::one() + T::one())),
759 );
760 };
761
762 mo_orbitss
763 .iter_mut()
764 .enumerate()
765 .for_each(|(ispin, mo_orbits)| {
766 mo_orbits
767 .iter_mut()
768 .enumerate()
769 .for_each(|(imo, mo_orbit)| {
770 if mo_orbit.origin().complex_symmetric() {
771 mo_orbit.set_smat(
772 (mo_smatss[ispin][imo].clone() + mo_smatss[ispin][imo].t().to_owned())
773 .mapv(|x| x / (T::one() + T::one())),
774 )
775 } else {
776 mo_orbit.set_smat(
777 (mo_smatss[ispin][imo].clone()
778 + mo_smatss[ispin][imo].t().to_owned().mapv(|x| x.conj()))
779 .mapv(|x| x / (T::one() + T::one())),
780 )
781 }
782 })
783 });
784
785 log::debug!("Constructing determinant and MO orbits in tandem... Done.");
786 Ok((det_orbit, mo_orbitss))
787}