qsym2/target/noci/
basis.rs

1//! Basis for non-orthogonal configuration interaction of Slater determinants.
2
3use std::collections::VecDeque;
4
5use anyhow::{self, format_err};
6use derive_builder::Builder;
7use itertools::Itertools;
8use itertools::structs::Product;
9
10use crate::group::GroupProperties;
11
12#[path = "basis_transformation.rs"]
13mod basis_transformation;
14
15#[cfg(test)]
16#[path = "basis_tests.rs"]
17mod basis_tests;
18
19// =====
20// Basis
21// =====
22
23// -----------------
24// Trait definitions
25// -----------------
26
27/// Trait defining behaviours of a basis consisting of linear-space items.
28pub trait Basis<I> {
29    /// Type of the iterator over items in the basis.
30    type BasisIter: Iterator<Item = Result<I, anyhow::Error>>;
31
32    /// Returns the number of items in the basis.
33    fn n_items(&self) -> usize;
34
35    /// An iterator over items in the basis.
36    fn iter(&self) -> Self::BasisIter;
37
38    /// Shared reference to the first item in the basis.
39    fn first(&self) -> Option<I>;
40}
41
42// --------------------------------------
43// Struct definitions and implementations
44// --------------------------------------
45
46// ~~~~~~~~~~~~~~~~~~~~~~
47// Lazy basis from orbits
48// ~~~~~~~~~~~~~~~~~~~~~~
49
50#[derive(Builder, Clone)]
51pub struct OrbitBasis<'g, G, I>
52where
53    G: GroupProperties,
54{
55    /// The origins from which orbits are generated.
56    origins: Vec<I>,
57
58    /// The group acting on the origins to generate orbits, the concatenation of which forms the
59    /// basis.
60    group: &'g G,
61
62    /// Additional operators acting on the entire orbit basis (right-most operator acts first). Each
63    /// operator has an associated action that defines how it operatres on the elements in the
64    /// orbit basis.
65    #[builder(default = "None")]
66    #[allow(clippy::type_complexity)]
67    prefactors: Option<
68        VecDeque<(
69            G::GroupElement,
70            fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
71        )>,
72    >,
73
74    /// A function defining the action of each group element on the origin.
75    action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
76}
77
78impl<'g, G, I> OrbitBasis<'g, G, I>
79where
80    G: GroupProperties + Clone,
81    I: Clone,
82{
83    pub fn builder() -> OrbitBasisBuilder<'g, G, I> {
84        OrbitBasisBuilder::<'g, G, I>::default()
85    }
86
87    /// The origins from which orbits are generated.
88    pub fn origins(&self) -> &Vec<I> {
89        &self.origins
90    }
91
92    /// The group acting on the origins to generate orbits, the concatenation of which forms the
93    /// basis.
94    pub fn group(&self) -> &G {
95        self.group
96    }
97
98    /// Additional operators acting on the entire orbit basis (right-most operator acts first). Each
99    /// operator has an associated action that defines how it operatres on the elements in the
100    /// orbit basis.
101    #[allow(clippy::type_complexity)]
102    pub fn prefactors(
103        &self,
104    ) -> Option<
105        &VecDeque<(
106            G::GroupElement,
107            fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
108        )>,
109    > {
110        self.prefactors.as_ref()
111    }
112}
113
114impl<'g, G, I> OrbitBasis<'g, G, I>
115where
116    G: GroupProperties,
117    I: Clone,
118{
119    /// Converts this orbit basis into the equivalent eager basis.
120    pub fn to_eager(&self) -> Result<EagerBasis<I>, anyhow::Error> {
121        let elements = self.iter().collect::<Result<Vec<_>, _>>()?;
122        EagerBasis::builder()
123            .elements(elements)
124            .build()
125            .map_err(|err| format_err!(err))
126    }
127}
128
129impl<'g, G, I> Basis<I> for OrbitBasis<'g, G, I>
130where
131    G: GroupProperties,
132    I: Clone,
133{
134    type BasisIter = OrbitBasisIterator<G, I>;
135
136    fn n_items(&self) -> usize {
137        self.origins.len() * self.group.order()
138    }
139
140    /// Iterates over the elements of the [`OrbitBasis`]. Each element is indexed by `iI` where `i`
141    /// enumerates the group elements and `I` enumerates the origins. `I` is the fast index and `i`
142    /// the slow one.
143    fn iter(&self) -> Self::BasisIter {
144        OrbitBasisIterator::new(
145            self.prefactors.clone(),
146            self.group,
147            self.origins.clone(),
148            self.action,
149        )
150    }
151
152    fn first(&self) -> Option<I> {
153        if let Some(prefactors) = self.prefactors.as_ref() {
154            prefactors
155                .iter()
156                .rev()
157                .try_fold(self.origins.first()?.clone(), |acc, (symop, action)| {
158                    (action)(symop, &acc).ok()
159                })
160        } else {
161            self.origins.first().cloned()
162        }
163    }
164}
165
166/// Lazy iterator for basis constructed from the concatenation of orbits generated from multiple
167/// origins.
168pub struct OrbitBasisIterator<G, I>
169where
170    G: GroupProperties,
171{
172    #[allow(clippy::type_complexity)]
173    prefactors: Option<
174        VecDeque<(
175            G::GroupElement,
176            fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
177        )>,
178    >,
179
180    /// A mutable iterator over the Cartesian product between the group elements and the origins.
181    group_origin_iter: Product<
182        <<G as GroupProperties>::ElementCollection as IntoIterator>::IntoIter,
183        std::vec::IntoIter<I>,
184    >,
185
186    /// A function defining the action of each group element on the origin.
187    action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
188}
189
190impl<G, I> OrbitBasisIterator<G, I>
191where
192    G: GroupProperties,
193    I: Clone,
194{
195    /// Creates and returns a new orbit basis iterator.
196    ///
197    /// Each element returned by the iterator is indexed by `iI` where `i` enumerates the group
198    /// elements and `I` enumerates the origins. `I` is the fast index and `i` the slow one.
199    ///
200    /// # Arguments
201    ///
202    /// * `group` - A group.
203    /// * `origins` - A slice of origins.
204    /// * `action` - A function or closure defining the action of each group element on the origins.
205    ///
206    /// # Returns
207    ///
208    /// An orbit basis iterator.
209    #[allow(clippy::type_complexity)]
210    fn new(
211        prefactors: Option<
212            VecDeque<(
213                G::GroupElement,
214                fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
215            )>,
216        >,
217        group: &G,
218        origins: Vec<I>,
219        action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
220    ) -> Self {
221        Self {
222            prefactors,
223            group_origin_iter: group
224                .elements()
225                .clone()
226                .into_iter()
227                .cartesian_product(origins),
228            action,
229        }
230    }
231}
232
233impl<G, I> Iterator for OrbitBasisIterator<G, I>
234where
235    G: GroupProperties,
236    I: Clone,
237{
238    type Item = Result<I, anyhow::Error>;
239
240    fn next(&mut self) -> Option<Self::Item> {
241        if let Some(prefactors) = self.prefactors.as_ref() {
242            // let group_action_result = self
243            //     .group_origin_iter
244            //     .next()
245            //     .map(|(op, origin)| (self.action)(&op, &origin))?;
246            // Some((self.action)(prefactor, group_action_result.as_ref().ok()?))
247            self.group_origin_iter.next().and_then(|(op, origin)| {
248                prefactors
249                    .iter()
250                    .rev()
251                    .try_fold((self.action)(&op, &origin), |acc_res, (symop, action)| {
252                        acc_res.map(|acc| (action)(symop, &acc))
253                    })
254                    .ok()
255            })
256        } else {
257            self.group_origin_iter
258                .next()
259                .map(|(op, origin)| (self.action)(&op, &origin))
260        }
261    }
262}
263
264// ~~~~~~~~~~~
265// Eager basis
266// ~~~~~~~~~~~
267
268#[derive(Builder, Clone)]
269pub struct EagerBasis<I: Clone> {
270    /// The elements in the basis.
271    elements: Vec<I>,
272}
273
274impl<I: Clone> EagerBasis<I> {
275    pub fn builder() -> EagerBasisBuilder<I> {
276        EagerBasisBuilder::default()
277    }
278}
279
280impl<I: Clone> Basis<I> for EagerBasis<I> {
281    type BasisIter = std::vec::IntoIter<Result<I, anyhow::Error>>;
282
283    fn n_items(&self) -> usize {
284        self.elements.len()
285    }
286
287    fn iter(&self) -> Self::BasisIter {
288        self.elements
289            .iter()
290            .cloned()
291            .map(Ok)
292            .collect_vec()
293            .into_iter()
294    }
295
296    fn first(&self) -> Option<I> {
297        self.elements.first().cloned()
298    }
299}