Skip to main content

qsym2/target/noci/
basis_transformation.rs

1//! Implementation of symmetry transformations for bases for configuration interaction of Slater
2//! determinants.
3
4use std::collections::{HashSet, VecDeque};
5use std::fmt;
6
7use anyhow::format_err;
8use nalgebra::Vector3;
9use ndarray::Array2;
10use ndarray_linalg::types::Lapack;
11use num_complex::{Complex, ComplexFloat};
12
13use crate::angmom::spinor_rotation_3d::{SpinOrbitCoupled, StructureConstraint};
14use crate::group::GroupProperties;
15use crate::permutation::Permutation;
16use crate::symmetry::symmetry_element::{RotationGroup, SymmetryElement, SymmetryOperation, TRROT};
17use crate::symmetry::symmetry_element_order::ElementOrder;
18use crate::symmetry::symmetry_transformation::{
19    ComplexConjugationTransformable, DefaultTimeReversalTransformable, SpatialUnitaryTransformable,
20    SpinUnitaryTransformable, SymmetryTransformable, TimeReversalTransformable,
21    TransformationError,
22};
23use crate::target::determinant::SlaterDeterminant;
24use crate::target::noci::basis::{EagerBasis, FCIBasis, OrbitBasis};
25
26// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28// OrbitBasis: Lazy basis from orbits
29// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31
32// ---------------------------
33// SpatialUnitaryTransformable
34// ---------------------------
35impl<'g, G, I> SpatialUnitaryTransformable for OrbitBasis<'g, G, I>
36where
37    G: GroupProperties + Clone,
38    I: SpatialUnitaryTransformable,
39{
40    fn transform_spatial_mut(
41        &mut self,
42        _: &Array2<f64>,
43        _: Option<&Permutation<usize>>,
44    ) -> Result<&mut Self, TransformationError> {
45        Err(TransformationError("Transforming an orbit basis by an arbitrary spatial transformation matrix is not supported.".to_string()))
46    }
47}
48
49// ------------------------
50// SpinUnitaryTransformable
51// ------------------------
52impl<'g, G, I> SpinUnitaryTransformable for OrbitBasis<'g, G, I>
53where
54    G: GroupProperties + Clone,
55    I: SpinUnitaryTransformable,
56{
57    fn transform_spin_mut(
58        &mut self,
59        _: &Array2<Complex<f64>>,
60    ) -> Result<&mut Self, TransformationError> {
61        Err(TransformationError("Transforming an orbit basis by an arbitrary spin transformation matrix is not supported.".to_string()))
62    }
63}
64
65// -------------------------------
66// ComplexConjugationTransformable
67// -------------------------------
68
69impl<'g, G, I> ComplexConjugationTransformable for OrbitBasis<'g, G, I>
70where
71    G: GroupProperties + Clone,
72    I: ComplexConjugationTransformable,
73{
74    /// Performs a complex conjugation in-place.
75    fn transform_cc_mut(&mut self) -> Result<&mut Self, TransformationError> {
76        Err(TransformationError(
77            "Transforming an orbit basis by an explicit complex conjugation is not supported."
78                .to_string(),
79        ))
80    }
81}
82
83// --------------------------------
84// DefaultTimeReversalTransformable
85// --------------------------------
86impl<'g, G, I> DefaultTimeReversalTransformable for OrbitBasis<'g, G, I>
87where
88    G: GroupProperties + Clone,
89    I: DefaultTimeReversalTransformable,
90{
91}
92
93// ---------------------------------------
94// TimeReversalTransformable (non-default)
95// ---------------------------------------
96// `SlaterDeterminant<_, _, SpinOrbitCoupled>` does not implement
97// `DefaultTimeReversalTransformable` and so the surrounding `OrbitBasis` does not get a blanket
98// implementation of `TimeReversalTransformable`.
99impl<'a, 'g, G> TimeReversalTransformable
100    for OrbitBasis<'g, G, SlaterDeterminant<'a, Complex<f64>, SpinOrbitCoupled>>
101where
102    G: GroupProperties<GroupElement = SymmetryOperation> + Clone,
103{
104    fn transform_timerev_mut(&mut self) -> Result<&mut Self, TransformationError> {
105        let t_element = SymmetryElement::builder()
106            .threshold(1e-14)
107            .proper_order(ElementOrder::Int(1))
108            .proper_power(1)
109            .raw_axis(Vector3::new(0.0, 0.0, 1.0))
110            .kind(TRROT)
111            .rotation_group(RotationGroup::SU2(true))
112            .build()
113            .map_err(|err| TransformationError(err.to_string()))?;
114        let t = SymmetryOperation::builder()
115            .generating_element(t_element)
116            .power(1)
117            .build()
118            .map_err(|err| TransformationError(err.to_string()))?;
119        if let Some(prefactors) = self.prefactors.as_mut() {
120            prefactors.push_front((t.clone(), |g, item| {
121                item.sym_transform_spin_spatial(g)
122                    .map_err(|err| format_err!(err))
123            }));
124        } else {
125            let mut v = VecDeque::<(
126                G::GroupElement,
127                fn(
128                    &G::GroupElement,
129                    &SlaterDeterminant<'a, Complex<f64>, SpinOrbitCoupled>,
130                )
131                    -> Result<SlaterDeterminant<'a, Complex<f64>, SpinOrbitCoupled>, anyhow::Error>,
132            )>::new();
133            v.push_front((t.clone(), |g, item| {
134                item.sym_transform_spin_spatial(g)
135                    .map_err(|err| format_err!(err))
136            }));
137            self.prefactors = Some(v);
138        }
139        Ok(self)
140    }
141}
142
143// ---------------------
144// SymmetryTransformable
145// ---------------------
146impl<'g, G, I> SymmetryTransformable for OrbitBasis<'g, G, I>
147where
148    G: GroupProperties<GroupElement = SymmetryOperation> + Clone,
149    I: SymmetryTransformable,
150    OrbitBasis<'g, G, I>: TimeReversalTransformable,
151{
152    fn sym_permute_sites_spatial(
153        &self,
154        symop: &SymmetryOperation,
155    ) -> Result<Permutation<usize>, TransformationError> {
156        let mut perms = self
157            .origins
158            .iter()
159            .map(|origin| origin.sym_permute_sites_spatial(symop))
160            .collect::<Result<HashSet<_>, _>>()?;
161        if perms.len() == 1 {
162            perms.drain().next().ok_or(TransformationError(
163                "Unable to retrieve the site permutation.".to_string(),
164            ))
165        } else {
166            Err(TransformationError(
167                "Mismatched site permutations across the origins.".to_string(),
168            ))
169        }
170    }
171
172    // --------------------------
173    // Overriden provided methods
174    // --------------------------
175    fn sym_transform_spatial_mut(
176        &mut self,
177        symop: &SymmetryOperation,
178    ) -> Result<&mut Self, TransformationError> {
179        if let Some(prefactors) = self.prefactors.as_mut() {
180            prefactors.push_front((symop.clone(), |g, item| {
181                item.sym_transform_spatial(g)
182                    .map_err(|err| format_err!(err))
183            }));
184        } else {
185            let mut v = VecDeque::<(
186                G::GroupElement,
187                fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
188            )>::new();
189            v.push_front((symop.clone(), |g, item| {
190                item.sym_transform_spatial(g)
191                    .map_err(|err| format_err!(err))
192            }));
193            self.prefactors = Some(v);
194        }
195        Ok(self)
196    }
197
198    fn sym_transform_spatial_with_spintimerev_mut(
199        &mut self,
200        symop: &SymmetryOperation,
201    ) -> Result<&mut Self, TransformationError> {
202        if let Some(prefactors) = self.prefactors.as_mut() {
203            prefactors.push_front((symop.clone(), |g, item| {
204                item.sym_transform_spatial_with_spintimerev(g)
205                    .map_err(|err| format_err!(err))
206            }));
207        } else {
208            let mut v = VecDeque::<(
209                G::GroupElement,
210                fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
211            )>::new();
212            v.push_front((symop.clone(), |g, item| {
213                item.sym_transform_spatial_with_spintimerev(g)
214                    .map_err(|err| format_err!(err))
215            }));
216            self.prefactors = Some(v);
217        }
218        Ok(self)
219    }
220
221    fn sym_transform_spin_mut(
222        &mut self,
223        symop: &SymmetryOperation,
224    ) -> Result<&mut Self, TransformationError> {
225        if let Some(prefactors) = self.prefactors.as_mut() {
226            prefactors.push_front((symop.clone(), |g, item| {
227                item.sym_transform_spin(g).map_err(|err| format_err!(err))
228            }));
229        } else {
230            let mut v = VecDeque::<(
231                G::GroupElement,
232                fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
233            )>::new();
234            v.push_front((symop.clone(), |g, item| {
235                item.sym_transform_spin(g).map_err(|err| format_err!(err))
236            }));
237            self.prefactors = Some(v);
238        }
239        Ok(self)
240    }
241
242    fn sym_transform_spin_spatial_mut(
243        &mut self,
244        symop: &SymmetryOperation,
245    ) -> Result<&mut Self, TransformationError> {
246        if let Some(prefactors) = self.prefactors.as_mut() {
247            prefactors.push_front((symop.clone(), |g, item| {
248                item.sym_transform_spin_spatial(g)
249                    .map_err(|err| format_err!(err))
250            }));
251        } else {
252            let mut v = VecDeque::<(
253                G::GroupElement,
254                fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
255            )>::new();
256            v.push_front((symop.clone(), |g, item| {
257                item.sym_transform_spin_spatial(g)
258                    .map_err(|err| format_err!(err))
259            }));
260            self.prefactors = Some(v);
261        }
262        Ok(self)
263    }
264}
265
266// ~~~~~~~~~~
267// ~~~~~~~~~~
268// EagerBasis
269// ~~~~~~~~~~
270// ~~~~~~~~~~
271
272// ---------------------------
273// SpatialUnitaryTransformable
274// ---------------------------
275impl<I> SpatialUnitaryTransformable for EagerBasis<I>
276where
277    I: SpatialUnitaryTransformable,
278{
279    fn transform_spatial_mut(
280        &mut self,
281        rmat: &Array2<f64>,
282        perm: Option<&Permutation<usize>>,
283    ) -> Result<&mut Self, TransformationError> {
284        self.elements
285            .iter_mut()
286            .map(|origin| origin.transform_spatial_mut(rmat, perm))
287            .collect::<Result<Vec<_>, _>>()?;
288        Ok(self)
289    }
290}
291
292// ------------------------
293// SpinUnitaryTransformable
294// ------------------------
295impl<I> SpinUnitaryTransformable for EagerBasis<I>
296where
297    I: SpinUnitaryTransformable,
298{
299    fn transform_spin_mut(
300        &mut self,
301        dmat: &Array2<Complex<f64>>,
302    ) -> Result<&mut Self, TransformationError> {
303        self.elements
304            .iter_mut()
305            .map(|origin| origin.transform_spin_mut(dmat))
306            .collect::<Result<Vec<_>, _>>()?;
307        Ok(self)
308    }
309}
310
311// -------------------------------
312// ComplexConjugationTransformable
313// -------------------------------
314
315impl<I> ComplexConjugationTransformable for EagerBasis<I>
316where
317    I: ComplexConjugationTransformable,
318{
319    /// Performs a complex conjugation in-place.
320    fn transform_cc_mut(&mut self) -> Result<&mut Self, TransformationError> {
321        for origin in self.elements.iter_mut() {
322            origin.transform_cc_mut()?;
323        }
324        Ok(self)
325    }
326}
327
328// --------------------------------
329// DefaultTimeReversalTransformable
330// --------------------------------
331impl<I> DefaultTimeReversalTransformable for EagerBasis<I> where
332    I: DefaultTimeReversalTransformable + Clone
333{
334}
335
336// ---------------------------------------
337// TimeReversalTransformable (non-default)
338// ---------------------------------------
339// `SlaterDeterminant<_, _, SpinOrbitCoupled>` does not implement
340// `DefaultTimeReversalTransformable` and so the surrounding `EagerBasis` does not get a blanket
341// implementation of `TimeReversalTransformable`.
342impl<'a> TimeReversalTransformable
343    for EagerBasis<SlaterDeterminant<'a, Complex<f64>, SpinOrbitCoupled>>
344{
345    fn transform_timerev_mut(&mut self) -> Result<&mut Self, TransformationError> {
346        for det in self.elements.iter_mut() {
347            det.transform_timerev_mut()?;
348        }
349        Ok(self)
350    }
351}
352
353// ---------------------
354// SymmetryTransformable
355// ---------------------
356impl<I> SymmetryTransformable for EagerBasis<I>
357where
358    I: SymmetryTransformable,
359    EagerBasis<I>: TimeReversalTransformable,
360{
361    // ----------------
362    // Required methods
363    // ----------------
364    fn sym_permute_sites_spatial(
365        &self,
366        symop: &SymmetryOperation,
367    ) -> Result<Permutation<usize>, TransformationError> {
368        let mut perms = self
369            .elements
370            .iter()
371            .map(|origin| origin.sym_permute_sites_spatial(symop))
372            .collect::<Result<HashSet<_>, _>>()?;
373        if perms.len() == 1 {
374            perms.drain().next().ok_or(TransformationError(
375                "Unable to retrieve the site permutation.".to_string(),
376            ))
377        } else {
378            Err(TransformationError(
379                "Mismatched site permutations across the elements in the basis.".to_string(),
380            ))
381        }
382    }
383
384    // ----------------------------
385    // Overwritten provided methods
386    // ----------------------------
387    // We need to override this to make sure that we use the correct
388    // `sym_transform_spin_spatial_mut` for the items and not the default
389    // `sym_transform_spin_spatial_mut` which simply combines `transform_spatial_mut`,
390    // `transform_spin_mut`, and `transform_timerev_mut` which is not right if spin--orbit coupling
391    // is at work.
392    fn sym_transform_spin_spatial_mut(
393        &mut self,
394        symop: &SymmetryOperation,
395    ) -> Result<&mut Self, TransformationError> {
396        self.elements
397            .iter_mut()
398            .map(|item| item.sym_transform_spin_spatial_mut(symop))
399            .collect::<Result<Vec<_>, _>>()?;
400        Ok(self)
401    }
402}
403
404// ~~~~~~~~
405// ~~~~~~~~
406// FCIBasis
407// ~~~~~~~~
408// ~~~~~~~~
409
410// ---------------------------
411// SpatialUnitaryTransformable
412// ---------------------------
413impl<'a, T, SC> SpatialUnitaryTransformable for FCIBasis<'a, T, SC>
414where
415    T: ComplexFloat + Lapack,
416    SC: StructureConstraint + fmt::Display + Clone,
417    SlaterDeterminant<'a, T, SC>: SpatialUnitaryTransformable,
418{
419    fn transform_spatial_mut(
420        &mut self,
421        rmat: &Array2<f64>,
422        perm: Option<&Permutation<usize>>,
423    ) -> Result<&mut Self, TransformationError> {
424        self.reference.transform_spatial_mut(rmat, perm)?;
425        Ok(self)
426    }
427}
428
429// ------------------------
430// SpinUnitaryTransformable
431// ------------------------
432impl<'a, T, SC> SpinUnitaryTransformable for FCIBasis<'a, T, SC>
433where
434    T: ComplexFloat + Lapack,
435    SC: StructureConstraint + fmt::Display + Clone,
436    SlaterDeterminant<'a, T, SC>: SpinUnitaryTransformable,
437{
438    fn transform_spin_mut(
439        &mut self,
440        rmat: &Array2<Complex<f64>>,
441    ) -> Result<&mut Self, TransformationError> {
442        self.reference.transform_spin_mut(rmat)?;
443        Ok(self)
444    }
445}
446
447// -------------------------------
448// ComplexConjugationTransformable
449// -------------------------------
450
451impl<'a, T, SC> ComplexConjugationTransformable for FCIBasis<'a, T, SC>
452where
453    T: ComplexFloat + Lapack,
454    SC: StructureConstraint + fmt::Display + Clone,
455    SlaterDeterminant<'a, T, SC>: ComplexConjugationTransformable,
456{
457    /// Performs a complex conjugation in-place.
458    fn transform_cc_mut(&mut self) -> Result<&mut Self, TransformationError> {
459        self.reference.transform_cc_mut()?;
460        Ok(self)
461    }
462}
463
464// --------------------------------
465// DefaultTimeReversalTransformable
466// --------------------------------
467impl<'a, T, SC> DefaultTimeReversalTransformable for FCIBasis<'a, T, SC>
468where
469    T: ComplexFloat + Lapack,
470    SC: StructureConstraint + fmt::Display + Clone,
471    SlaterDeterminant<'a, T, SC>: DefaultTimeReversalTransformable,
472{
473}
474
475// ---------------------------------------
476// TimeReversalTransformable (non-default)
477// ---------------------------------------
478// `SlaterDeterminant<_, _, SpinOrbitCoupled>` does not implement `DefaultTimeReversalTransformable`
479// and so the surrounding `FCIBasis` does not get a blanket implementation of
480// `TimeReversalTransformable`.
481impl<'a> TimeReversalTransformable for FCIBasis<'a, Complex<f64>, SpinOrbitCoupled> {
482    fn transform_timerev_mut(&mut self) -> Result<&mut Self, TransformationError> {
483        self.reference.transform_timerev_mut()?;
484        Ok(self)
485    }
486}
487
488// ---------------------
489// SymmetryTransformable
490// ---------------------
491impl<'a, T, SC> SymmetryTransformable for FCIBasis<'a, T, SC>
492where
493    T: ComplexFloat + Lapack,
494    SC: StructureConstraint + fmt::Display + Clone,
495    SlaterDeterminant<'a, T, SC>: SymmetryTransformable,
496    FCIBasis<'a, T, SC>: TimeReversalTransformable,
497{
498    fn sym_permute_sites_spatial(
499        &self,
500        symop: &SymmetryOperation,
501    ) -> Result<Permutation<usize>, TransformationError> {
502        self.reference.sym_permute_sites_spatial(symop)
503    }
504
505    // ----------------------------
506    // Overwritten provided methods
507    // ----------------------------
508    // We need to override this to make sure that we use the correct
509    // `sym_transform_spin_spatial_mut` for the reference and not the default
510    // `sym_transform_spin_spatial_mut` which simply combines `transform_spatial_mut`,
511    // `transform_spin_mut`, and `transform_timerev_mut` which is not right if spin--orbit coupling
512    // is at work.
513    fn sym_transform_spin_spatial_mut(
514        &mut self,
515        symop: &SymmetryOperation,
516    ) -> Result<&mut Self, TransformationError> {
517        self.reference.sym_transform_spin_spatial_mut(symop)?;
518        Ok(self)
519    }
520}