1use std::collections::HashSet;
4use std::fmt;
5use std::hash::Hash;
6use std::str::FromStr;
7
8use anyhow::{bail, ensure, format_err};
9use derive_builder::Builder;
10use duplicate::duplicate_item;
11use indexmap::IndexMap;
12use ndarray::{Array1, Array2, Array4, Ix2, s};
13use ndarray_linalg::Lapack;
14use num::Complex;
15use num_complex::ComplexFloat;
16use num_traits::ToPrimitive;
17use num_traits::real::Real;
18use serde::{Deserialize, Serialize, de::DeserializeOwned};
19
20use crate::analysis::{EigenvalueComparisonMode, Overlap};
21use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled, StructureConstraint};
22use crate::chartab::CharacterTable;
23use crate::chartab::chartab_group::CharacterProperties;
24use crate::drivers::QSym2Driver;
25use crate::drivers::representation_analysis::{
26 CharacterTableDisplay, MagneticSymmetryAnalysisKind, fn_construct_unitary_group, log_bao,
27 log_cc_transversal,
28};
29use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
30use crate::group::{GroupProperties, UnitaryRepresentedGroup};
31use crate::io::format::{
32 QSym2Output, log_subtitle, nice_bool, qsym2_output, write_subtitle, write_title,
33};
34use crate::projection::Projectable;
35use crate::symmetry::symmetry_group::{SymmetryGroupProperties, UnitaryRepresentedSymmetryGroup};
36use crate::symmetry::symmetry_symbols::MullikenIrrepSymbol;
37use crate::symmetry::symmetry_transformation::{SymmetryTransformable, SymmetryTransformationKind};
38use crate::target::determinant::SlaterDeterminant;
39use crate::target::determinant::determinant_analysis::SlaterDeterminantSymmetryOrbit;
40use crate::target::noci::basis::OrbitBasis;
41use crate::target::noci::multideterminant::MultiDeterminant;
42use crate::target::noci::multideterminants::MultiDeterminants;
43
44#[cfg(test)]
45#[path = "slater_determinant_tests.rs"]
46mod slater_determinant_tests;
47
48const fn default_symbolic() -> Option<CharacterTableDisplay> {
53 Some(CharacterTableDisplay::Symbolic)
54}
55
56#[derive(Clone, Builder, Debug, Serialize, Deserialize)]
58pub struct SlaterDeterminantProjectionParams {
59 #[builder(default = "None")]
62 #[serde(default)]
63 pub use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
64
65 #[builder(default = "false")]
67 #[serde(default)]
68 pub use_double_group: bool,
69
70 #[builder(default = "Some(CharacterTableDisplay::Symbolic)")]
73 #[serde(default = "default_symbolic")]
74 pub write_character_table: Option<CharacterTableDisplay>,
75
76 #[builder(default = "SymmetryTransformationKind::Spatial")]
79 #[serde(default)]
80 pub symmetry_transformation_kind: SymmetryTransformationKind,
81
82 #[builder(default = "None")]
85 #[serde(default)]
86 pub infinite_order_to_finite: Option<u32>,
87
88 #[builder(default = "None")]
90 #[serde(default)]
91 pub symbolic_projection_targets: Option<Vec<String>>,
92
93 #[builder(default = "None")]
96 #[serde(default)]
97 pub numeric_projection_targets: Option<Vec<usize>>,
98}
99
100impl SlaterDeterminantProjectionParams {
101 pub fn builder() -> SlaterDeterminantProjectionParamsBuilder {
103 SlaterDeterminantProjectionParamsBuilder::default()
104 }
105}
106
107impl fmt::Display for SlaterDeterminantProjectionParams {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 if let Some(symbolic_projection_targets) = self.symbolic_projection_targets.as_ref() {
110 writeln!(f, "Projection subspaces (symbolic):")?;
111 for projection_target in symbolic_projection_targets.iter() {
112 writeln!(f, " {projection_target}")?;
113 }
114 writeln!(f)?;
115 }
116 if let Some(numeric_projection_targets) = self.numeric_projection_targets.as_ref() {
117 writeln!(f, "Projection subspaces (numeric):")?;
118 for projection_target in numeric_projection_targets.iter() {
119 writeln!(f, " {projection_target}")?;
120 }
121 writeln!(f)?;
122 }
123 writeln!(
124 f,
125 "Use magnetic group for projection: {}",
126 match self.use_magnetic_group {
127 None => "no",
128 Some(MagneticSymmetryAnalysisKind::Representation) =>
129 "yes, using unitary representations",
130 Some(MagneticSymmetryAnalysisKind::Corepresentation) =>
131 "yes, using magnetic corepresentations",
132 }
133 )?;
134 writeln!(
135 f,
136 "Use double group for projection: {}",
137 nice_bool(self.use_double_group)
138 )?;
139 if let Some(finite_order) = self.infinite_order_to_finite {
140 writeln!(f, "Infinite order to finite: {finite_order}")?;
141 }
142 writeln!(
143 f,
144 "Symmetry transformation kind: {}",
145 self.symmetry_transformation_kind
146 )?;
147 writeln!(f)?;
148 writeln!(
149 f,
150 "Write character table: {}",
151 if let Some(chartab_display) = self.write_character_table.as_ref() {
152 format!("yes, {}", chartab_display.to_string().to_lowercase())
153 } else {
154 "no".to_string()
155 }
156 )?;
157
158 Ok(())
159 }
160}
161
162#[derive(Clone, Builder)]
168pub struct SlaterDeterminantProjectionResult<'a, G, T, SC>
169where
170 G: SymmetryGroupProperties + Clone,
171 T: ComplexFloat + Lapack,
172 SC: StructureConstraint + Hash + Eq + fmt::Display + 'a,
173 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
174 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
175 Projectable<G, SlaterDeterminant<'a, T, SC>>,
176{
177 parameters: &'a SlaterDeterminantProjectionParams,
179
180 determinant: &'a SlaterDeterminant<'a, T, SC>,
182
183 group: G,
185
186 #[allow(clippy::type_complexity)]
189 projected_determinants: IndexMap<
190 G::RowSymbol,
191 Result<MultiDeterminant<'a, T, OrbitBasis<G, SlaterDeterminant<'a, T, SC>>, SC>, String>,
192 >,
193
194 #[builder(default = "None")]
201 sao: Option<Array2<T>>,
202
203 #[builder(default = "None")]
210 sao_h: Option<Array2<T>>,
211}
212
213impl<'a, G, T, SC> SlaterDeterminantProjectionResult<'a, G, T, SC>
214where
215 G: SymmetryGroupProperties + Clone,
216 G::RowSymbol: Serialize + DeserializeOwned,
217 T: ComplexFloat + Lapack,
218 SC: StructureConstraint + Hash + Eq + Clone + fmt::Display,
219 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
220 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
221 Projectable<G, SlaterDeterminant<'a, T, SC>>,
222{
223 pub fn builder() -> SlaterDeterminantProjectionResultBuilder<'a, G, T, SC> {
224 SlaterDeterminantProjectionResultBuilder::default()
225 }
226
227 pub fn parameters(&self) -> &SlaterDeterminantProjectionParams {
229 self.parameters
230 }
231
232 pub fn determinant(&self) -> &SlaterDeterminant<'a, T, SC> {
234 self.determinant
235 }
236
237 #[allow(clippy::type_complexity)]
239 pub fn projected_determinants(
240 &self,
241 ) -> &IndexMap<
242 G::RowSymbol,
243 Result<MultiDeterminant<'a, T, OrbitBasis<G, SlaterDeterminant<'a, T, SC>>, SC>, String>,
244 > {
245 &self.projected_determinants
246 }
247}
248
249impl<'a, G, T, SC> fmt::Display for SlaterDeterminantProjectionResult<'a, G, T, SC>
250where
251 G: SymmetryGroupProperties + Clone,
252 T: ComplexFloat + Lapack,
253 SC: StructureConstraint + Hash + Eq + fmt::Display,
254 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
255 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
256 Projectable<G, SlaterDeterminant<'a, T, SC>>,
257 MultiDeterminant<'a, T, OrbitBasis<G, SlaterDeterminant<'a, T, SC>>, SC>: Overlap<T, Ix2>,
258{
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 write_subtitle(f, "Orbit-based symmetry projection summary")?;
261 writeln!(f)?;
262 writeln!(
263 f,
264 "> Group: {} ({})",
265 self.group
266 .finite_subgroup_name()
267 .map(|subgroup_name| format!("{} > {}", self.group.name(), subgroup_name))
268 .unwrap_or(self.group.name()),
269 self.group.group_type().to_string().to_lowercase()
270 )?;
271 writeln!(f)?;
272
273 let energy_precision = 7 + Real::ceil(ComplexFloat::abs(ComplexFloat::log10(
274 self.determinant.threshold(),
275 )))
276 .to_usize()
277 .expect("Unable to convert the linear independence threshold exponent to `usize`.");
278
279 let (rows, (sq_norms, energies)): (Vec<_>, (Vec<_>, Vec<_>)) = self
280 .projected_determinants
281 .iter()
282 .map(|(row, psd_res)| {
283 let sq_norm = psd_res
284 .as_ref()
285 .map_err(|err| err.to_string())
286 .and_then(|psd| {
287 psd.overlap(psd, self.sao.as_ref(), self.sao_h.as_ref())
288 .map(|norm_sq| format!("{norm_sq:+.3e}"))
289 .map_err(|err| err.to_string())
290 })
291 .unwrap_or_else(|err| err);
292 let energy = psd_res
293 .as_ref()
294 .map_err(|err| err.to_string())
295 .and_then(|psd| {
296 psd.energy()
297 .map(|energy| format!("{energy:+.energy_precision$}"))
298 .map_err(|err| err.to_string())
299 })
300 .unwrap_or_else(|err| err);
301 (row.to_string(), (sq_norm, energy))
302 })
303 .unzip();
304
305 let overlap_definition = self
306 .projected_determinants
307 .iter()
308 .next()
309 .and_then(|(_, psd_res)| psd_res.as_ref().ok().map(|psd| psd.overlap_definition()))
310 .unwrap_or("--".to_string());
311
312 writeln!(
313 f,
314 " Squared norms are computed w.r.t. the following inner product:"
315 )?;
316 writeln!(f, " {overlap_definition}")?;
317 writeln!(f)?;
318
319 let row_length = rows
320 .iter()
321 .map(|row| row.chars().count())
322 .max()
323 .unwrap_or(8)
324 .max(8);
325 let sq_norm_length = sq_norms
326 .iter()
327 .map(|sq_norm| sq_norm.chars().count())
328 .max()
329 .unwrap_or(12)
330 .max(12);
331 let energy_length = energies
332 .iter()
333 .map(|energy| energy.chars().count())
334 .max()
335 .unwrap_or(6)
336 .max(6);
337 let table_width = 6 + row_length + sq_norm_length + energy_length;
338 writeln!(f, "{}", "┈".repeat(table_width))?;
339 writeln!(
340 f,
341 " {:<row_length$} {:<sq_norm_length$} Energy",
342 "Subspace", "Squared norm"
343 )?;
344 writeln!(f, "{}", "┈".repeat(table_width))?;
345 for (row, (sq_norm, energy)) in rows.iter().zip(sq_norms.iter().zip(energies)) {
346 writeln!(
347 f,
348 " {:<row_length$} {:<sq_norm_length$} {:<}",
349 row, sq_norm, energy
350 )?;
351 }
352 writeln!(f, "{}", "┈".repeat(table_width))?;
353
354 writeln!(f)?;
355 Ok(())
356 }
357}
358
359impl<'a, G, T, SC> fmt::Debug for SlaterDeterminantProjectionResult<'a, G, T, SC>
360where
361 G: SymmetryGroupProperties + Clone,
362 T: ComplexFloat + Lapack,
363 SC: StructureConstraint + Hash + Eq + fmt::Display,
364 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
365 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
366 Projectable<G, SlaterDeterminant<'a, T, SC>>,
367 MultiDeterminant<'a, T, OrbitBasis<G, SlaterDeterminant<'a, T, SC>>, SC>: Overlap<T, Ix2>,
368{
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 writeln!(f, "{self}")
371 }
372}
373
374#[derive(Clone, Builder)]
384#[builder(build_fn(validate = "Self::validate"))]
385pub struct SlaterDeterminantProjectionDriver<'a, G, T, SC, F>
386where
387 G: SymmetryGroupProperties + Clone,
388 G::RowSymbol: Serialize + DeserializeOwned,
389 T: ComplexFloat + Lapack,
390 SC: StructureConstraint + Hash + Eq + fmt::Display,
391 F: Fn(&Array2<T>) -> Result<(Array2<T>, Array2<T>), anyhow::Error> + Clone + Sync,
392 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
393 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
394 Projectable<G, SlaterDeterminant<'a, T, SC>>,
395{
396 parameters: &'a SlaterDeterminantProjectionParams,
398
399 determinant: &'a SlaterDeterminant<'a, T, SC>,
401
402 symmetry_group: &'a SymmetryGroupDetectionResult,
406
407 #[builder(setter(skip), default = "None")]
409 result: Option<SlaterDeterminantProjectionResult<'a, G, T, SC>>,
410
411 #[builder(default = "None")]
418 sao: Option<&'a Array2<T>>,
419
420 #[builder(default = "None")]
427 sao_h: Option<&'a Array2<T>>,
428
429 #[builder(default = "None")]
432 enuc: Option<T>,
433
434 #[builder(default = "None")]
437 onee: Option<&'a Array2<T>>,
438
439 #[builder(default = "None")]
443 twoe: Option<&'a Array4<T>>,
444
445 #[builder(default = "None")]
450 get_jk: Option<F>,
451
452 #[builder(default = "None")]
456 thresh_offdiag: Option<<T as ComplexFloat>::Real>,
457
458 #[builder(default = "None")]
461 thresh_zeroov: Option<<T as ComplexFloat>::Real>,
462}
463
464impl<'a, G, T, SC, F> SlaterDeterminantProjectionDriverBuilder<'a, G, T, SC, F>
465where
466 G: SymmetryGroupProperties + Clone,
467 G::RowSymbol: Serialize + DeserializeOwned,
468 T: ComplexFloat + Lapack,
469 SC: StructureConstraint + Hash + Eq + fmt::Display,
470 F: Fn(&Array2<T>) -> Result<(Array2<T>, Array2<T>), anyhow::Error> + Clone + Sync,
471 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
472 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
473 Projectable<G, SlaterDeterminant<'a, T, SC>>,
474{
475 fn validate(&self) -> Result<(), String> {
476 let params = self
477 .parameters
478 .ok_or("No Slater determinant projection parameters found.".to_string())?;
479
480 let sym_res = self
481 .symmetry_group
482 .ok_or("No symmetry group information found.".to_string())?;
483
484 let _sd = self
485 .determinant
486 .as_ref()
487 .ok_or("No Slater determinant found.".to_string())?;
488
489 let sym = if params.use_magnetic_group.is_some() {
490 sym_res
491 .magnetic_symmetry
492 .as_ref()
493 .ok_or("Magnetic symmetry requested for symmetry projection, but no magnetic symmetry found.")?
494 } else {
495 &sym_res.unitary_symmetry
496 };
497
498 if sym.is_infinite() && params.infinite_order_to_finite.is_none() {
499 Err(format!(
500 "Projection cannot be performed using the entirety of the infinite group `{}`. \
501 Consider setting the parameter `infinite_order_to_finite` to restrict to a finite subgroup instead.",
502 sym.group_name
503 .as_ref()
504 .expect("No symmetry group name found.")
505 ))
506 } else {
507 Ok(())
508 }
509 }
510}
511
512impl<'a, G, T, SC, F> SlaterDeterminantProjectionDriver<'a, G, T, SC, F>
520where
521 G: SymmetryGroupProperties + Clone,
522 G::RowSymbol: Serialize + DeserializeOwned,
523 T: ComplexFloat + Lapack,
524 SC: StructureConstraint + Hash + Eq + Clone + fmt::Display,
525 F: Fn(&Array2<T>) -> Result<(Array2<T>, Array2<T>), anyhow::Error> + Clone + Sync,
526 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
527 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
528 Projectable<G, SlaterDeterminant<'a, T, SC>>,
529{
530 pub fn builder() -> SlaterDeterminantProjectionDriverBuilder<'a, G, T, SC, F> {
532 SlaterDeterminantProjectionDriverBuilder::default()
533 }
534
535 #[allow(clippy::type_complexity)]
538 fn construct_sao(&self) -> Result<(Option<Array2<T>>, Option<Array2<T>>), anyhow::Error> {
539 if let Some(provided_sao) = self.sao {
540 let nbas_set = self
541 .determinant
542 .baos()
543 .iter()
544 .map(|bao| bao.n_funcs())
545 .collect::<HashSet<_>>();
546 let uniform_component = nbas_set.len() == 1;
547 let ncomps = self
548 .determinant
549 .structure_constraint()
550 .n_explicit_comps_per_coefficient_matrix();
551 let provided_dim = provided_sao.nrows();
552
553 if uniform_component {
554 let nbas = nbas_set.iter().next().ok_or_else(|| format_err!("Unable to extract the uniform number of basis functions per explicit component."))?;
555 if provided_dim == *nbas {
556 let sao = {
557 let mut sao_mut = Array2::zeros((ncomps * nbas, ncomps * nbas));
558 (0..ncomps).for_each(|icomp| {
559 let start = icomp * nbas;
560 let end = (icomp + 1) * nbas;
561 sao_mut
562 .slice_mut(s![start..end, start..end])
563 .assign(provided_sao);
564 });
565 sao_mut
566 };
567
568 let sao_h_opt = self.sao_h.map(|sao_h| {
569 let mut sao_h_mut = Array2::zeros((ncomps * nbas, ncomps * nbas));
570 (0..ncomps).for_each(|icomp| {
571 let start = icomp * nbas;
572 let end = (icomp + 1) * nbas;
573 sao_h_mut
574 .slice_mut(s![start..end, start..end])
575 .assign(sao_h);
576 });
577 sao_h_mut
578 });
579
580 Ok((Some(sao), sao_h_opt))
581 } else {
582 ensure!(provided_dim == nbas * ncomps);
583 Ok((self.sao.cloned(), self.sao_h.cloned()))
584 }
585 } else {
586 let nbas_tot = self
587 .determinant
588 .baos()
589 .iter()
590 .map(|bao| bao.n_funcs())
591 .sum::<usize>();
592 ensure!(provided_dim == nbas_tot);
593 Ok((self.sao.cloned(), self.sao_h.cloned()))
594 }
595 } else {
596 Ok((None, None))
597 }
598 }
599}
600
601impl<'a, T, SC, F> SlaterDeterminantProjectionDriver<'a, UnitaryRepresentedSymmetryGroup, T, SC, F>
605where
606 T: ComplexFloat + Lapack,
607 SC: StructureConstraint + Hash + Eq + Clone + fmt::Display,
608 F: Fn(&Array2<T>) -> Result<(Array2<T>, Array2<T>), anyhow::Error> + Clone + Sync,
609 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
610 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, UnitaryRepresentedSymmetryGroup, T, SC>:
611 Projectable<UnitaryRepresentedSymmetryGroup, SlaterDeterminant<'a, T, SC>>,
612{
613 fn_construct_unitary_group!(
614 construct_unitary_group
617 );
618}
619
620#[duplicate_item(
624 duplicate!{
625 [
626 dtype_nested sctype_nested;
627 [ f64 ] [ SpinConstraint ];
628 [ Complex<f64> ] [ SpinConstraint ];
629 [ Complex<f64> ] [ SpinOrbitCoupled ];
630 ]
631 [
632 gtype_ [ UnitaryRepresentedSymmetryGroup ]
633 dtype_ [ dtype_nested ]
634 sctype_ [ sctype_nested ]
635 doc_sub_ [ "Performs projection using a unitary-represented group and stores the result." ]
636 projection_fn_ [ projection_representation ]
637 construct_group_ [ self.construct_unitary_group()? ]
638 ]
639 }
640)]
641impl<'a, F> SlaterDeterminantProjectionDriver<'a, gtype_, dtype_, sctype_, F>
642where
643 F: Fn(&Array2<dtype_>) -> Result<(Array2<dtype_>, Array2<dtype_>), anyhow::Error>
644 + Clone
645 + Sync,
646{
647 #[doc = doc_sub_]
648 fn projection_fn_(&mut self) -> Result<(), anyhow::Error> {
649 let params = self.parameters;
650 let group = construct_group_;
651 let original_sd = self.determinant;
652 log_cc_transversal(&group);
653 let baos = original_sd.baos();
654 for (bao_i, bao) in baos.iter().enumerate() {
655 log_bao(bao, Some(bao_i));
656 }
657
658 let all_rows = group.character_table().get_all_rows();
659 let rows = params
660 .symbolic_projection_targets
661 .as_ref()
662 .unwrap_or(&vec![])
663 .iter()
664 .map(|row_str| MullikenIrrepSymbol::from_str(row_str).map_err(|err| format_err!(err)))
665 .chain(
666 params
667 .numeric_projection_targets
668 .as_ref()
669 .unwrap_or(&vec![])
670 .iter()
671 .map(|row_index| {
672 all_rows.get_index(*row_index).cloned().ok_or_else(|| {
673 format_err!(
674 "Unable to retrieve the subspace label with index {row_index}."
675 )
676 })
677 }),
678 )
679 .collect::<Result<Vec<_>, _>>()?;
680
681 let mut projected_determinants = SlaterDeterminantSymmetryOrbit::builder()
682 .group(&group)
683 .origin(original_sd)
684 .symmetry_transformation_kind(params.symmetry_transformation_kind.clone())
685 .integrality_threshold(1e-14)
686 .linear_independence_threshold(1e-14)
687 .orbit_overlap_symmetry_threshold(1e-14)
688 .eigenvalue_comparison_mode(EigenvalueComparisonMode::Modulus)
689 .build()
690 .map_err(|err| format_err!(err))
691 .and_then(|sd_orbit| {
692 rows.iter()
693 .map(move |row| {
694 sd_orbit.project_onto(row).map(move |temp_projected_sd| {
695 let basis = temp_projected_sd.basis().clone();
696 (
697 row.clone(),
698 MultiDeterminant::builder()
699 .basis(basis)
700 .complex_conjugated(temp_projected_sd.complex_conjugated())
701 .coefficients(temp_projected_sd.coefficients().clone())
702 .threshold(temp_projected_sd.threshold())
703 .build()
704 .map_err(|err| err.to_string()),
705 )
706 })
707 })
708 .collect::<Result<IndexMap<_, _>, _>>()
709 })?;
710
711 let multidets_vec = projected_determinants
712 .values()
713 .cloned()
714 .collect::<Result<Vec<_>, _>>()
715 .map_err(|err| format_err!(err))?;
716 let multidets = MultiDeterminants::from_multideterminant_vec(
717 &multidets_vec.iter().collect::<Vec<_>>(),
718 )?;
719
720 let energies = match (
721 self.enuc,
722 self.onee,
723 self.thresh_offdiag,
724 self.thresh_zeroov,
725 self.sao,
726 ) {
727 (Some(enuc), Some(onee), Some(thresh_offdiag), Some(thresh_zeroov), Some(sao)) => {
728 multidets.compute_energies(
729 sao.into(),
730 enuc,
731 onee.into(),
732 self.twoe.map(|twoe| twoe.into()),
733 self.get_jk.as_ref(),
734 thresh_offdiag,
735 thresh_zeroov,
736 true,
737 )?
738 }
739 _ => (0..multidets.n_states())
740 .map(|_| Err("No multi-determinantal energies calculated.".to_string()))
741 .collect::<Array1<_>>(),
742 };
743 projected_determinants
744 .values_mut()
745 .zip(energies)
746 .for_each(|(det_res, energy)| {
747 let _ = det_res.as_mut().map(|det| det.set_energy(energy));
748 });
749
750 let (sao_opt, sao_h_opt) = self.construct_sao()?;
751 let result = SlaterDeterminantProjectionResult::builder()
752 .parameters(params)
753 .determinant(self.determinant)
754 .group(group.clone())
755 .projected_determinants(projected_determinants)
756 .sao(sao_opt)
757 .sao_h(sao_h_opt)
758 .build()?;
759 self.result = Some(result);
760
761 Ok(())
762 }
763}
764
765impl<'a, G, T, SC, F> fmt::Display for SlaterDeterminantProjectionDriver<'a, G, T, SC, F>
773where
774 G: SymmetryGroupProperties + Clone,
775 G::RowSymbol: Serialize + DeserializeOwned,
776 T: ComplexFloat + Lapack,
777 SC: StructureConstraint + Hash + Eq + fmt::Display,
778 F: Fn(&Array2<T>) -> Result<(Array2<T>, Array2<T>), anyhow::Error> + Clone + Sync,
779 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
780 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
781 Projectable<G, SlaterDeterminant<'a, T, SC>>,
782{
783 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
784 write_title(f, "Slater Determinant Symmetry Projection")?;
785 writeln!(f)?;
786 writeln!(f, "{}", self.parameters)?;
787 Ok(())
788 }
789}
790
791impl<'a, G, T, SC, F> fmt::Debug for SlaterDeterminantProjectionDriver<'a, G, T, SC, F>
792where
793 G: SymmetryGroupProperties + Clone,
794 G::RowSymbol: Serialize + DeserializeOwned,
795 T: ComplexFloat + Lapack,
796 SC: StructureConstraint + Hash + Eq + fmt::Display,
797 F: Fn(&Array2<T>) -> Result<(Array2<T>, Array2<T>), anyhow::Error> + Clone + Sync,
798 SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
799 for<'g> SlaterDeterminantSymmetryOrbit<'g, 'a, G, T, SC>:
800 Projectable<G, SlaterDeterminant<'a, T, SC>>,
801{
802 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
803 writeln!(f, "{self}")
804 }
805}
806
807#[duplicate_item(
811 duplicate!{
812 [
813 dtype_nested sctype_nested;
814 [ f64 ] [ SpinConstraint ];
815 [ Complex<f64> ] [ SpinConstraint ];
816 [ Complex<f64> ] [ SpinOrbitCoupled ];
817 ]
818 [
819 gtype_ [ UnitaryRepresentedSymmetryGroup ]
820 dtype_ [ dtype_nested ]
821 sctype_ [ sctype_nested ]
822 doc_sub_ [ "Performs projection using a unitary-represented group and stores the result." ]
823 projection_fn_ [ projection_representation ]
824 construct_group_ [ self.construct_unitary_group()? ]
825 ]
826 }
827)]
828impl<'a, F> QSym2Driver for SlaterDeterminantProjectionDriver<'a, gtype_, dtype_, sctype_, F>
829where
830 F: Fn(&Array2<dtype_>) -> Result<(Array2<dtype_>, Array2<dtype_>), anyhow::Error>
831 + Clone
832 + Sync,
833{
834 type Params = SlaterDeterminantProjectionParams;
835
836 type Outcome = SlaterDeterminantProjectionResult<'a, gtype_, dtype_, sctype_>;
837
838 fn result(&self) -> Result<&Self::Outcome, anyhow::Error> {
839 self.result
840 .as_ref()
841 .ok_or_else(|| format_err!("No Slater determinant projection results found."))
842 }
843
844 fn run(&mut self) -> Result<(), anyhow::Error> {
845 self.log_output_display();
846 self.projection_fn_()?;
847 self.result()?.log_output_display();
848 Ok(())
849 }
850}