1use std::collections::HashSet;
4use std::fmt;
5use std::iter::Sum;
6
7use anyhow::{self, format_err};
8use approx;
9use derive_builder::Builder;
10use itertools::Itertools;
11use log;
12use ndarray::{Array1, Array2, Ix2, s};
13use ndarray_einsum::*;
14use ndarray_linalg::types::Lapack;
15use num::ToPrimitive;
16use num_complex::{Complex, ComplexFloat};
17use num_traits::float::{Float, FloatConst};
18
19use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled, StructureConstraint};
20use crate::auxiliary::molecule::Molecule;
21use crate::basis::ao::BasisAngularOrder;
22use crate::target::density::{DensitiesOwned, Density};
23use crate::target::orbital::MolecularOrbital;
24
25#[cfg(test)]
26mod determinant_tests;
27
28#[cfg(test)]
29mod determinant_jadapted_tests;
30
31pub mod determinant_analysis;
32pub mod determinant_projection;
33mod determinant_transformation;
34
35#[derive(Builder, Clone)]
41#[builder(build_fn(validate = "Self::validate"))]
42pub struct SlaterDeterminant<'a, T, SC>
43where
44 T: ComplexFloat + Lapack,
45 SC: StructureConstraint + fmt::Display,
46{
47 structure_constraint: SC,
49
50 baos: Vec<&'a BasisAngularOrder<'a>>,
54
55 complex_symmetric: bool,
58
59 #[builder(default = "false")]
62 complex_conjugated: bool,
63
64 mol: &'a Molecule,
66
67 #[builder(setter(custom))]
69 coefficients: Vec<Array2<T>>,
70
71 #[builder(setter(custom))]
73 occupations: Vec<Array1<<T as ComplexFloat>::Real>>,
74
75 #[builder(default = "None")]
77 mo_energies: Option<Vec<Array1<T>>>,
78
79 #[builder(default = "Err(\"Determinant energy not yet set.\".to_string())")]
81 energy: Result<T, String>,
82
83 threshold: <T as ComplexFloat>::Real,
85}
86
87impl<'a, T, SC> SlaterDeterminantBuilder<'a, T, SC>
88where
89 T: ComplexFloat + Lapack,
90 SC: StructureConstraint + fmt::Display,
91{
92 pub fn coefficients(&mut self, cs: &[Array2<T>]) -> &mut Self {
93 self.coefficients = Some(cs.to_vec());
94 self
95 }
96
97 pub fn occupations(&mut self, occs: &[Array1<<T as ComplexFloat>::Real>]) -> &mut Self {
98 self.occupations = Some(occs.to_vec());
99 self
100 }
101
102 fn validate(&self) -> Result<(), String> {
103 let structcons = self
104 .structure_constraint
105 .as_ref()
106 .ok_or("No structure constraints found.".to_string())?;
107 let baos = self
108 .baos
109 .as_ref()
110 .ok_or("No `BasisAngularOrder`s found.".to_string())?;
111 let baos_length_check = baos.len() == structcons.n_explicit_comps_per_coefficient_matrix();
112 if !baos_length_check {
113 log::error!(
114 "The number of `BasisAngularOrder`s provided does not match the number of explicit components per coefficient matrix."
115 );
116 }
117
118 let nbas_tot = baos.iter().map(|bao| bao.n_funcs()).sum::<usize>();
119 let coefficients = self
120 .coefficients
121 .as_ref()
122 .ok_or("No coefficients found.".to_string())?;
123 let coefficients_length_check = if coefficients.len() != structcons.n_coefficient_matrices()
124 {
125 log::error!(
126 "Unexpected number of coefficient matrices: {} found, but {} expected for the structure constraint {}.",
127 coefficients.len(),
128 structcons.n_coefficient_matrices(),
129 structcons
130 );
131 false
132 } else {
133 true
134 };
135 let coefficients_shape_check = {
136 let nrows = nbas_tot;
137 if !coefficients.iter().all(|c| c.shape()[0] == nrows) {
138 log::error!(
139 "Unexpected shapes of coefficient matrices: {} {} expected for all coefficient matrices, but {} found.",
140 nrows,
141 if nrows == 1 { "row" } else { "rows" },
142 coefficients
143 .iter()
144 .map(|c| c.shape()[0].to_string())
145 .join(", ")
146 );
147 false
148 } else {
149 true
150 }
151 };
152
153 let occupations = self
154 .occupations
155 .as_ref()
156 .ok_or("No occupations found.".to_string())?;
157 let occupations_length_check = if occupations.len() != structcons.n_coefficient_matrices() {
158 log::error!(
159 "Unexpected number of occupation vectors: {} found, but {} expected for the structure constraint {}.",
160 occupations.len(),
161 structcons.n_coefficient_matrices(),
162 structcons
163 );
164 false
165 } else {
166 true
167 };
168 let occupations_shape_check = if !occupations
169 .iter()
170 .zip(coefficients.iter())
171 .all(|(o, c)| o.len() == c.shape()[1])
172 {
173 log::error!(
174 "Mismatched occupations and numbers of orbitals: {}",
175 occupations
176 .iter()
177 .zip(coefficients.iter())
178 .map(|(o, c)| format!("{} vs. {}", o.len(), c.shape()[1]))
179 .join(", ")
180 );
181 false
182 } else {
183 true
184 };
185
186 let mol = self.mol.ok_or("No molecule found.".to_string())?;
187 let natoms_set = baos.iter().map(|bao| bao.n_atoms()).collect::<HashSet<_>>();
188 if natoms_set.len() != 1 {
189 return Err("Inconsistent numbers of atoms between `BasisAngularOrder`s of different explicit components.".to_string());
190 };
191 let n_atoms = natoms_set.iter().next().ok_or_else(|| {
192 "Unable to retrieve the number of atoms from the `BasisAngularOrder`s.".to_string()
193 })?;
194 let natoms_check = mol.atoms.len() == *n_atoms;
195 if !natoms_check {
196 log::error!(
197 "The number of atoms in the molecule does not match the number of local sites in the basis."
198 );
199 }
200
201 if baos_length_check
202 && coefficients_length_check
203 && coefficients_shape_check
204 && occupations_length_check
205 && occupations_shape_check
206 && natoms_check
207 {
208 Ok(())
209 } else {
210 Err(format!(
211 "Slater determinant validation failed:
212 baos_length ({baos_length_check}),
213 coefficients_length ({coefficients_length_check}),
214 coefficients_shape ({coefficients_shape_check}),
215 occupations_length ({occupations_length_check}),
216 occupations_shape ({occupations_shape_check}),
217 natoms ({natoms_check})."
218 ))
219 }
220 }
221}
222
223impl<'a, T, SC> SlaterDeterminant<'a, T, SC>
224where
225 T: ComplexFloat + Clone + Lapack,
226 SC: StructureConstraint + Clone + fmt::Display,
227{
228 pub fn builder() -> SlaterDeterminantBuilder<'a, T, SC> {
230 SlaterDeterminantBuilder::default()
231 }
232}
233
234impl<'a, T, SC> SlaterDeterminant<'a, T, SC>
235where
236 T: ComplexFloat + Clone + Lapack,
237 SC: StructureConstraint + Clone + fmt::Display,
238{
239 pub fn to_orbitals(&self) -> Vec<Vec<MolecularOrbital<'a, T, SC>>> {
247 self.coefficients
248 .iter()
249 .enumerate()
250 .map(|(spini, cs_spini)| {
251 cs_spini
252 .columns()
253 .into_iter()
254 .enumerate()
255 .map(move |(i, c)| {
256 MolecularOrbital::builder()
257 .coefficients(c.to_owned())
258 .energy(self.mo_energies.as_ref().map(|moes| moes[spini][i]))
259 .baos(self.baos.clone())
260 .mol(self.mol)
261 .structure_constraint(self.structure_constraint.clone())
262 .component_index(spini)
263 .complex_symmetric(self.complex_symmetric)
264 .threshold(self.threshold)
265 .build()
266 .expect("Unable to construct a molecular orbital.")
267 })
268 .collect::<Vec<_>>()
269 })
270 .collect::<Vec<_>>()
271 }
272}
273
274impl<'a, T, SC> SlaterDeterminant<'a, T, SC>
275where
276 T: ComplexFloat + Clone + Lapack,
277 SC: StructureConstraint + fmt::Display,
278{
279 pub fn complex_symmetric(&self) -> bool {
281 self.complex_symmetric
282 }
283
284 pub(crate) fn complex_conjugated(&self) -> bool {
286 self.complex_conjugated
287 }
288
289 pub fn structure_constraint(&self) -> &SC {
291 &self.structure_constraint
292 }
293
294 pub fn baos(&'_ self) -> &Vec<&'_ BasisAngularOrder<'_>> {
297 &self.baos
298 }
299
300 pub fn mol(&self) -> &Molecule {
302 self.mol
303 }
304
305 pub fn energy(&self) -> Result<&T, &String> {
307 self.energy.as_ref()
308 }
309
310 pub fn mo_energies(&self) -> Option<&Vec<Array1<T>>> {
312 self.mo_energies.as_ref()
313 }
314
315 pub fn occupations(&self) -> &Vec<Array1<<T as ComplexFloat>::Real>> {
317 &self.occupations
318 }
319
320 pub fn coefficients(&self) -> &Vec<Array2<T>> {
322 &self.coefficients
323 }
324
325 pub fn threshold(&self) -> <T as ComplexFloat>::Real {
327 self.threshold
328 }
329
330 pub fn nelectrons(&self) -> <T as ComplexFloat>::Real
332 where
333 <T as ComplexFloat>::Real: Sum + From<u16>,
334 {
335 let implicit_factor = self
336 .structure_constraint
337 .implicit_factor()
338 .expect("Unable to retrieve the implicit factor from the structure constraint.");
339 <T as ComplexFloat>::Real::from(
340 implicit_factor
341 .to_u16()
342 .expect("Unable to convert the implicit factor to `u16`."),
343 ) * self
344 .occupations
345 .iter()
346 .map(|occ| occ.iter().copied().sum())
347 .sum()
348 }
349}
350
351impl<'a, T> SlaterDeterminant<'a, T, SpinConstraint>
352where
353 T: ComplexFloat + Clone + Lapack,
354{
355 pub fn to_generalised(&self) -> Self {
363 match self.structure_constraint {
364 SpinConstraint::Restricted(n) => {
365 log::debug!(
366 "Restricted Slater determinant will be augmented to generalised Slater determinant."
367 );
368 let bao = self.baos[0];
369 let nbas = bao.n_funcs();
370
371 let cr = &self.coefficients[0];
372 let occr = &self.occupations[0];
373 let norb = cr.ncols();
374 let mut cg = Array2::<T>::zeros((nbas * usize::from(n), norb * usize::from(n)));
375 let mut occg = Array1::<<T as ComplexFloat>::Real>::zeros((norb * usize::from(n),));
376 (0..usize::from(n)).for_each(|i| {
377 let row_start = nbas * i;
378 let row_end = nbas * (i + 1);
379 let col_start = norb * i;
380 let col_end = norb * (i + 1);
381 cg.slice_mut(s![row_start..row_end, col_start..col_end])
382 .assign(cr);
383 occg.slice_mut(s![col_start..col_end]).assign(occr);
384 });
385 let moeg_opt = self.mo_energies.as_ref().map(|moer| {
386 let mut moeg = Array1::<T>::zeros((norb * usize::from(n),));
387 (0..usize::from(n)).for_each(|i| {
388 let col_start = norb * i;
389 let col_end = norb * (i + 1);
390 moeg.slice_mut(s![col_start..col_end]).assign(&moer[0]);
391 });
392 vec![moeg]
393 });
394 Self::builder()
395 .coefficients(&[cg])
396 .occupations(&[occg])
397 .mo_energies(moeg_opt)
398 .energy(self.energy.clone())
399 .baos((0..n).map(|_| bao).collect::<Vec<_>>())
400 .mol(self.mol)
401 .structure_constraint(SpinConstraint::Generalised(n, false))
402 .complex_symmetric(self.complex_symmetric)
403 .threshold(self.threshold)
404 .build()
405 .expect("Unable to spin-generalise a `SlaterDeterminant`.")
406 }
407 SpinConstraint::Unrestricted(n, increasingm) => {
408 log::debug!(
409 "Unrestricted Slater determinant will be augmented to generalised Slater determinant."
410 );
411 let bao = self.baos[0];
412 let nbas = bao.n_funcs();
413 let norb_tot = self.coefficients.iter().map(|c| c.ncols()).sum();
414 let mut cg = Array2::<T>::zeros((nbas * usize::from(n), norb_tot));
415 let mut occg = Array1::<<T as ComplexFloat>::Real>::zeros((norb_tot,));
416
417 let col_boundary_indices = (0..usize::from(n))
418 .scan(0, |acc, ispin| {
419 let start_index = *acc;
420 *acc += self.coefficients[ispin].shape()[1];
421 Some((start_index, *acc))
422 })
423 .collect::<Vec<_>>();
424 (0..usize::from(n)).for_each(|i| {
425 let row_start = nbas * i;
426 let row_end = nbas * (i + 1);
427 let (col_start, col_end) = col_boundary_indices[i];
428 cg.slice_mut(s![row_start..row_end, col_start..col_end])
429 .assign(&self.coefficients[i]);
430 occg.slice_mut(s![col_start..col_end])
431 .assign(&self.occupations[i]);
432 });
433
434 let moeg_opt = self.mo_energies.as_ref().map(|moer| {
435 let mut moeg = Array1::<T>::zeros((norb_tot,));
436 (0..usize::from(n)).for_each(|i| {
437 let (col_start, col_end) = col_boundary_indices[i];
438 moeg.slice_mut(s![col_start..col_end]).assign(&moer[i]);
439 });
440 vec![moeg]
441 });
442
443 Self::builder()
444 .coefficients(&[cg])
445 .occupations(&[occg])
446 .mo_energies(moeg_opt)
447 .energy(self.energy.clone())
448 .baos((0..n).map(|_| bao).collect::<Vec<_>>())
449 .mol(self.mol)
450 .structure_constraint(SpinConstraint::Generalised(n, increasingm))
451 .complex_symmetric(self.complex_symmetric)
452 .threshold(self.threshold)
453 .build()
454 .expect("Unable to spin-generalise a `SlaterDeterminant`.")
455 }
456 SpinConstraint::Generalised(_, _) => self.clone(),
457 }
458 }
459}
460
461impl<'a> SlaterDeterminant<'a, f64, SpinConstraint> {
462 pub fn to_densities(
474 &'a self,
475 ) -> Result<DensitiesOwned<'a, f64, SpinConstraint>, anyhow::Error> {
476 let densities = match self.structure_constraint {
477 SpinConstraint::Restricted(_) | SpinConstraint::Unrestricted(_, _) => {
478 self.coefficients().iter().zip(self.occupations().iter()).map(|(c, o)| {
479 let denmat = einsum(
480 "i,mi,ni->mn",
481 &[&o.view(), &c.view(), &c.view()]
482 )
483 .expect("Unable to construct a density matrix from a determinant coefficient matrix.")
484 .into_dimensionality::<Ix2>()
485 .expect("Unable to convert the resultant density matrix to two dimensions.");
486 Density::<f64>::builder()
487 .density_matrix(denmat)
488 .bao(self.baos()[0])
489 .mol(self.mol())
490 .complex_symmetric(self.complex_symmetric())
491 .threshold(self.threshold())
492 .build()
493 })
494 .collect::<Result<Vec<_>, _>>()?
495 }
496 SpinConstraint::Generalised(nspins, _) => {
497 let denmat = einsum(
498 "i,mi,ni->mn",
499 &[&self.occupations[0].view(), &self.coefficients[0].view(), &self.coefficients[0].view()]
500 )
501 .expect("Unable to construct a density matrix from a determinant coefficient matrix.")
502 .into_dimensionality::<Ix2>()
503 .expect("Unable to convert the resultant density matrix to two dimensions.");
504 let component_boundary_indices = self.baos
505 .iter()
506 .scan(0, |acc, bao| {
507 let start_index = *acc;
508 *acc += bao.n_funcs();
509 Some((start_index, *acc))
510 }).collect::<Vec<_>>();
511 (0..usize::from(nspins)).map(|ispin| {
512 let (start, end) = component_boundary_indices[ispin];
513 let ispin_denmat = denmat.slice(s![start..end, start..end]).to_owned();
514 Density::<f64>::builder()
515 .density_matrix(ispin_denmat)
516 .bao(self.baos()[ispin])
517 .mol(self.mol())
518 .complex_symmetric(self.complex_symmetric())
519 .threshold(self.threshold())
520 .build()
521 }).collect::<Result<Vec<_>, _>>()?
522 }
523 };
524 DensitiesOwned::builder()
525 .structure_constraint(self.structure_constraint.clone())
526 .densities(densities)
527 .build()
528 .map_err(|err| format_err!(err))
529 }
530}
531
532impl<'a, T> SlaterDeterminant<'a, Complex<T>, SpinConstraint>
533where
534 T: Float + FloatConst + Lapack,
535 Complex<T>: Lapack,
536{
537 pub fn to_densities(
554 &'a self,
555 ) -> Result<DensitiesOwned<'a, Complex<T>, SpinConstraint>, anyhow::Error> {
556 let densities = match self.structure_constraint {
557 SpinConstraint::Restricted(_) | SpinConstraint::Unrestricted(_, _) => {
558 self.coefficients().iter().zip(self.occupations().iter()).map(|(c, o)| {
559 let denmat = einsum(
560 "i,mi,ni->mn",
561 &[&o.map(Complex::<T>::from).view(), &c.view(), &c.map(Complex::conj).view()]
562 )
563 .expect("Unable to construct a density matrix from a determinant coefficient matrix.")
564 .into_dimensionality::<Ix2>()
565 .expect("Unable to convert the resultant density matrix to two dimensions.");
566 Density::<Complex<T>>::builder()
567 .density_matrix(denmat)
568 .bao(self.baos()[0])
569 .mol(self.mol())
570 .complex_symmetric(self.complex_symmetric())
571 .threshold(self.threshold())
572 .build()
573 })
574 .collect::<Result<Vec<_>, _>>()?
575 }
576 SpinConstraint::Generalised(nspins, _) => {
577 let denmat = einsum(
578 "i,mi,ni->mn",
579 &[
580 &self.occupations[0].map(Complex::<T>::from).view(),
581 &self.coefficients[0].view(),
582 &self.coefficients[0].map(Complex::conj).view()
583 ]
584 )
585 .expect("Unable to construct a density matrix from a determinant coefficient matrix.")
586 .into_dimensionality::<Ix2>()
587 .expect("Unable to convert the resultant density matrix to two dimensions.");
588 let component_boundary_indices = self.baos
589 .iter()
590 .scan(0, |acc, bao| {
591 let start_index = *acc;
592 *acc += bao.n_funcs();
593 Some((start_index, *acc))
594 }).collect::<Vec<_>>();
595 (0..usize::from(nspins)).map(|ispin| {
596 let (start, end) = component_boundary_indices[ispin];
597 let ispin_denmat = denmat.slice(s![start..end, start..end]).to_owned();
598 Density::<Complex<T>>::builder()
599 .density_matrix(ispin_denmat)
600 .bao(self.baos()[ispin])
601 .mol(self.mol())
602 .complex_symmetric(self.complex_symmetric())
603 .threshold(self.threshold())
604 .build()
605 }).collect::<Result<Vec<_>, _>>()?
606 }
607 };
608 DensitiesOwned::builder()
609 .structure_constraint(self.structure_constraint.clone())
610 .densities(densities)
611 .build()
612 .map_err(|err| format_err!(err))
613 }
614}
615
616impl<'a, T> SlaterDeterminant<'a, Complex<T>, SpinOrbitCoupled>
617where
618 T: Float + FloatConst + Lapack,
619 Complex<T>: Lapack,
620{
621 pub fn to_densities(
634 &'a self,
635 ) -> Result<DensitiesOwned<'a, Complex<T>, SpinOrbitCoupled>, anyhow::Error> {
636 let densities = match self.structure_constraint {
637 SpinOrbitCoupled::JAdapted(ncomps) => {
638 let denmat = einsum(
639 "i,mi,ni->mn",
640 &[
641 &self.occupations[0].map(Complex::<T>::from).view(),
642 &self.coefficients[0].view(),
643 &self.coefficients[0].map(Complex::conj).view(),
644 ],
645 )
646 .expect(
647 "Unable to construct a density matrix from a determinant coefficient matrix.",
648 )
649 .into_dimensionality::<Ix2>()
650 .expect("Unable to convert the resultant density matrix to two dimensions.");
651 let component_boundary_indices = self
652 .baos
653 .iter()
654 .scan(0, |acc, bao| {
655 let start_index = *acc;
656 *acc += bao.n_funcs();
657 Some((start_index, *acc))
658 })
659 .collect::<Vec<_>>();
660 (0..usize::from(ncomps))
661 .map(|icomp| {
662 let (start, end) = component_boundary_indices[icomp];
663 let icomp_denmat = denmat.slice(s![start..end, start..end]).to_owned();
664 Density::<Complex<T>>::builder()
665 .density_matrix(icomp_denmat)
666 .bao(self.baos()[icomp])
667 .mol(self.mol())
668 .complex_symmetric(self.complex_symmetric())
669 .threshold(self.threshold())
670 .build()
671 })
672 .collect::<Result<Vec<_>, _>>()?
673 }
674 };
675 DensitiesOwned::builder()
676 .structure_constraint(self.structure_constraint.clone())
677 .densities(densities)
678 .build()
679 .map_err(|err| format_err!(err))
680 }
681}
682
683impl<'a, T, SC> From<SlaterDeterminant<'a, T, SC>> for SlaterDeterminant<'a, Complex<T>, SC>
691where
692 T: Float + FloatConst + Lapack,
693 Complex<T>: Lapack,
694 SC: StructureConstraint + Clone + fmt::Display,
695{
696 fn from(value: SlaterDeterminant<'a, T, SC>) -> Self {
697 SlaterDeterminant::<'a, Complex<T>, SC>::builder()
698 .coefficients(
699 &value
700 .coefficients
701 .into_iter()
702 .map(|coeffs| coeffs.map(Complex::from))
703 .collect::<Vec<_>>(),
704 )
705 .occupations(&value.occupations)
706 .mo_energies(value.mo_energies.map(|moes| {
707 moes.iter()
708 .map(|moe| moe.map(Complex::from))
709 .collect::<Vec<_>>()
710 }))
711 .baos(value.baos.clone())
712 .mol(value.mol)
713 .structure_constraint(value.structure_constraint)
714 .complex_symmetric(value.complex_symmetric)
715 .threshold(value.threshold)
716 .build()
717 .expect("Unable to complexify a `SlaterDeterminant`.")
718 }
719}
720
721impl<'a, T, SC> PartialEq for SlaterDeterminant<'a, T, SC>
725where
726 T: ComplexFloat<Real = f64> + Lapack,
727 SC: StructureConstraint + PartialEq + fmt::Display,
728{
729 fn eq(&self, other: &Self) -> bool {
730 let thresh = (self.threshold * other.threshold).sqrt();
731 let coefficients_eq =
732 self.coefficients.len() == other.coefficients.len()
733 && self.coefficients.iter().zip(other.coefficients.iter()).all(
734 |(scoeffs, ocoeffs)| {
735 approx::relative_eq!(
736 (scoeffs - ocoeffs)
737 .map(|x| ComplexFloat::abs(*x).powi(2))
738 .sum()
739 .sqrt(),
740 0.0,
741 epsilon = thresh,
742 max_relative = thresh,
743 )
744 },
745 );
746 let occs_eq = self.occupations.len() == other.occupations.len()
747 && self
748 .occupations
749 .iter()
750 .zip(other.occupations.iter())
751 .all(|(soccs, ooccs)| {
752 approx::relative_eq!(
753 (soccs - ooccs).map(|x| x.abs().powi(2)).sum().sqrt(),
754 0.0,
755 epsilon = thresh,
756 max_relative = thresh,
757 )
758 });
759 self.structure_constraint == other.structure_constraint
760 && self.baos == other.baos
761 && self.mol == other.mol
762 && coefficients_eq
763 && occs_eq
764 }
765}
766
767impl<'a, T, SC> Eq for SlaterDeterminant<'a, T, SC>
771where
772 T: ComplexFloat<Real = f64> + Lapack,
773 SC: StructureConstraint + Eq + fmt::Display,
774{
775}
776
777impl<'a, T, SC> fmt::Debug for SlaterDeterminant<'a, T, SC>
781where
782 T: fmt::Debug + ComplexFloat + Lapack,
783 <T as ComplexFloat>::Real: Sum + From<u16> + fmt::Debug,
784 SC: StructureConstraint + fmt::Debug + fmt::Display,
785{
786 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
787 write!(
788 f,
789 "SlaterDeterminant[{:?}: {:?} electrons, {} coefficient {} of dimensions {}]",
790 self.structure_constraint,
791 self.nelectrons(),
792 self.coefficients.len(),
793 if self.coefficients.len() != 1 {
794 "matrices"
795 } else {
796 "matrix"
797 },
798 self.coefficients
799 .iter()
800 .map(|coeff| coeff
801 .shape()
802 .iter()
803 .map(|x| x.to_string())
804 .collect::<Vec<_>>()
805 .join("×"))
806 .collect::<Vec<_>>()
807 .join(", ")
808 )?;
809 Ok(())
810 }
811}
812
813impl<'a, T, SC> fmt::Display for SlaterDeterminant<'a, T, SC>
817where
818 T: fmt::Display + ComplexFloat + Lapack,
819 <T as ComplexFloat>::Real: Sum + From<u16> + fmt::Display,
820 SC: StructureConstraint + fmt::Display,
821{
822 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823 write!(
824 f,
825 "SlaterDeterminant[{}: {} electrons, {} coefficient {} of dimensions {}]",
826 self.structure_constraint,
827 self.nelectrons(),
828 self.coefficients.len(),
829 if self.coefficients.len() != 1 {
830 "matrices"
831 } else {
832 "matrix"
833 },
834 self.coefficients
835 .iter()
836 .map(|coeff| coeff
837 .shape()
838 .iter()
839 .map(|x| x.to_string())
840 .collect::<Vec<_>>()
841 .join("×"))
842 .collect::<Vec<_>>()
843 .join(", ")
844 )?;
845 Ok(())
846 }
847}