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    fn iter(&self) -> Self::BasisIter {
141        OrbitBasisIterator::new(
142            self.prefactors.clone(),
143            self.group,
144            self.origins.clone(),
145            self.action,
146        )
147    }
148
149    fn first(&self) -> Option<I> {
150        if let Some(prefactors) = self.prefactors.as_ref() {
151            prefactors
152                .iter()
153                .rev()
154                .try_fold(self.origins.first()?.clone(), |acc, (symop, action)| {
155                    (action)(symop, &acc).ok()
156                })
157        } else {
158            self.origins.first().cloned()
159        }
160    }
161}
162
163/// Lazy iterator for basis constructed from the concatenation of orbits generated from multiple
164/// origins.
165pub struct OrbitBasisIterator<G, I>
166where
167    G: GroupProperties,
168{
169    #[allow(clippy::type_complexity)]
170    prefactors: Option<
171        VecDeque<(
172            G::GroupElement,
173            fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
174        )>,
175    >,
176
177    /// A mutable iterator over the Cartesian product between the group elements and the origins.
178    group_origin_iter: Product<
179        <<G as GroupProperties>::ElementCollection as IntoIterator>::IntoIter,
180        std::vec::IntoIter<I>,
181    >,
182
183    /// A function defining the action of each group element on the origin.
184    action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
185}
186
187impl<G, I> OrbitBasisIterator<G, I>
188where
189    G: GroupProperties,
190    I: Clone,
191{
192    /// Creates and returns a new orbit basis iterator.
193    ///
194    /// # Arguments
195    ///
196    /// * `group` - A group.
197    /// * `origins` - A slice of origins.
198    /// * `action` - A function or closure defining the action of each group element on the origins.
199    ///
200    /// # Returns
201    ///
202    /// An orbit basis iterator.
203    #[allow(clippy::type_complexity)]
204    fn new(
205        prefactors: Option<
206            VecDeque<(
207                G::GroupElement,
208                fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
209            )>,
210        >,
211        group: &G,
212        origins: Vec<I>,
213        action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
214    ) -> Self {
215        Self {
216            prefactors,
217            group_origin_iter: group
218                .elements()
219                .clone()
220                .into_iter()
221                .cartesian_product(origins),
222            action,
223        }
224    }
225}
226
227impl<G, I> Iterator for OrbitBasisIterator<G, I>
228where
229    G: GroupProperties,
230    I: Clone,
231{
232    type Item = Result<I, anyhow::Error>;
233
234    fn next(&mut self) -> Option<Self::Item> {
235        if let Some(prefactors) = self.prefactors.as_ref() {
236            // let group_action_result = self
237            //     .group_origin_iter
238            //     .next()
239            //     .map(|(op, origin)| (self.action)(&op, &origin))?;
240            // Some((self.action)(prefactor, group_action_result.as_ref().ok()?))
241            self.group_origin_iter.next().and_then(|(op, origin)| {
242                prefactors
243                    .iter()
244                    .rev()
245                    .try_fold((self.action)(&op, &origin), |acc_res, (symop, action)| {
246                        acc_res.map(|acc| (action)(symop, &acc))
247                    })
248                    .ok()
249            })
250        } else {
251            self.group_origin_iter
252                .next()
253                .map(|(op, origin)| (self.action)(&op, &origin))
254        }
255    }
256}
257
258// ~~~~~~~~~~~
259// Eager basis
260// ~~~~~~~~~~~
261
262#[derive(Builder, Clone)]
263pub struct EagerBasis<I: Clone> {
264    /// The elements in the basis.
265    elements: Vec<I>,
266}
267
268impl<I: Clone> EagerBasis<I> {
269    pub fn builder() -> EagerBasisBuilder<I> {
270        EagerBasisBuilder::default()
271    }
272}
273
274impl<I: Clone> Basis<I> for EagerBasis<I> {
275    type BasisIter = std::vec::IntoIter<Result<I, anyhow::Error>>;
276
277    fn n_items(&self) -> usize {
278        self.elements.len()
279    }
280
281    fn iter(&self) -> Self::BasisIter {
282        self.elements
283            .iter()
284            .cloned()
285            .map(Ok)
286            .collect_vec()
287            .into_iter()
288    }
289
290    fn first(&self) -> Option<I> {
291        self.elements.first().cloned()
292    }
293}