1use std::collections::VecDeque;
4use std::fmt;
5
6use anyhow::{self, format_err};
7use derive_builder::Builder;
8use itertools::Itertools;
9use itertools::structs::Product;
10use ndarray::Array1;
11use ndarray_linalg::types::Lapack;
12use num_complex::ComplexFloat;
13
14use crate::angmom::spinor_rotation_3d::StructureConstraint;
15use crate::group::GroupProperties;
16use crate::target::determinant::SlaterDeterminant;
17
18#[path = "basis_transformation.rs"]
19mod basis_transformation;
20
21#[path = "basis_metric.rs"]
22pub mod basis_metric;
23
24#[path = "basis_orbit.rs"]
25pub mod basis_orbit;
26
27#[cfg(test)]
28#[path = "basis_tests.rs"]
29mod basis_tests;
30
31pub trait Basis {
41 type Item;
43
44 type BasisIter<'b>: Iterator<Item = Result<Self::Item, anyhow::Error>>
46 where
47 Self: 'b;
48
49 fn n_items(&self) -> usize;
51
52 fn iter(&self) -> Self::BasisIter<'_>;
54
55 fn first(&self) -> Option<Self::Item>;
57}
58
59#[derive(Builder, Clone)]
72pub struct OrbitBasis<'g, G, I>
73where
74 G: GroupProperties,
75{
76 origins: Vec<I>,
78
79 group: &'g G,
82
83 #[builder(default = "None")]
87 #[allow(clippy::type_complexity)]
88 prefactors: Option<
89 VecDeque<(
90 G::GroupElement,
91 fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
92 )>,
93 >,
94
95 action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
97}
98
99impl<'g, G, I> OrbitBasis<'g, G, I>
100where
101 G: GroupProperties + Clone,
102 I: Clone,
103{
104 pub fn builder() -> OrbitBasisBuilder<'g, G, I> {
105 OrbitBasisBuilder::<'g, G, I>::default()
106 }
107
108 pub fn origins(&self) -> &Vec<I> {
110 &self.origins
111 }
112
113 pub fn group(&self) -> &G {
116 self.group
117 }
118
119 #[allow(clippy::type_complexity)]
123 pub fn prefactors(
124 &self,
125 ) -> Option<
126 &VecDeque<(
127 G::GroupElement,
128 fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
129 )>,
130 > {
131 self.prefactors.as_ref()
132 }
133}
134
135impl<'g, G, I> OrbitBasis<'g, G, I>
136where
137 G: GroupProperties,
138 I: Clone,
139{
140 pub fn to_eager(&self) -> Result<EagerBasis<I>, anyhow::Error> {
142 let elements = self.iter().collect::<Result<Vec<_>, _>>()?;
143 EagerBasis::builder()
144 .elements(elements)
145 .build()
146 .map_err(|err| format_err!(err))
147 }
148}
149
150impl<'g, G, I> Basis for OrbitBasis<'g, G, I>
151where
152 G: GroupProperties,
153 I: Clone,
154{
155 type Item = I;
156
157 type BasisIter<'b>
158 = OrbitBasisIterator<G, I>
159 where
160 Self: 'b;
161
162 fn n_items(&self) -> usize {
163 self.origins.len() * self.group.order()
164 }
165
166 fn iter(&self) -> Self::BasisIter<'_> {
170 OrbitBasisIterator::new(
171 self.prefactors.clone(),
172 self.group,
173 self.origins.clone(),
174 self.action,
175 )
176 }
177
178 fn first(&self) -> Option<Self::Item> {
179 if let Some(prefactors) = self.prefactors.as_ref() {
180 prefactors
181 .iter()
182 .rev()
183 .try_fold(self.origins.first()?.clone(), |acc, (symop, action)| {
184 (action)(symop, &acc).ok()
185 })
186 } else {
187 self.origins.first().cloned()
188 }
189 }
190}
191
192pub struct OrbitBasisIterator<G, I>
199where
200 G: GroupProperties,
201{
202 #[allow(clippy::type_complexity)]
203 prefactors: Option<
204 VecDeque<(
205 G::GroupElement,
206 fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
207 )>,
208 >,
209
210 group_origin_iter: Product<
212 <<G as GroupProperties>::ElementCollection as IntoIterator>::IntoIter,
213 std::vec::IntoIter<I>,
214 >,
215
216 action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
218}
219
220impl<G, I> OrbitBasisIterator<G, I>
221where
222 G: GroupProperties,
223 I: Clone,
224{
225 #[allow(clippy::type_complexity)]
240 fn new(
241 prefactors: Option<
242 VecDeque<(
243 G::GroupElement,
244 fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
245 )>,
246 >,
247 group: &G,
248 origins: Vec<I>,
249 action: fn(&G::GroupElement, &I) -> Result<I, anyhow::Error>,
250 ) -> Self {
251 Self {
252 prefactors,
253 group_origin_iter: group
254 .elements()
255 .clone()
256 .into_iter()
257 .cartesian_product(origins),
258 action,
259 }
260 }
261}
262
263impl<G, I> Iterator for OrbitBasisIterator<G, I>
264where
265 G: GroupProperties,
266 I: Clone,
267{
268 type Item = Result<I, anyhow::Error>;
269
270 fn next(&mut self) -> Option<Self::Item> {
271 if let Some(prefactors) = self.prefactors.as_ref() {
272 self.group_origin_iter.next().and_then(|(op, origin)| {
273 prefactors
274 .iter()
275 .rev()
276 .try_fold((self.action)(&op, &origin), |acc_res, (symop, action)| {
277 acc_res.map(|acc| (action)(symop, &acc))
278 })
279 .ok()
280 })
281 } else {
282 self.group_origin_iter
283 .next()
284 .map(|(op, origin)| (self.action)(&op, &origin))
285 }
286 }
287}
288
289#[derive(Builder, Clone)]
295pub struct EagerBasis<I: Clone> {
296 elements: Vec<I>,
298}
299
300impl<I: Clone> EagerBasis<I> {
301 pub fn builder() -> EagerBasisBuilder<I> {
302 EagerBasisBuilder::default()
303 }
304}
305
306impl<I: Clone> Basis for EagerBasis<I> {
307 type Item = I;
308
309 type BasisIter<'b>
310 = std::vec::IntoIter<Result<I, anyhow::Error>>
311 where
312 Self: 'b;
313
314 fn n_items(&self) -> usize {
315 self.elements.len()
316 }
317
318 fn iter(&self) -> Self::BasisIter<'_> {
319 self.elements
320 .iter()
321 .cloned()
322 .map(Ok)
323 .collect_vec()
324 .into_iter()
325 }
326
327 fn first(&self) -> Option<Self::Item> {
328 self.elements.first().cloned()
329 }
330}
331
332#[derive(Builder, Clone)]
339#[builder(build_fn(validate = "Self::validate"))]
340pub struct FCIBasis<'a, T, SC>
341where
342 T: ComplexFloat + Lapack,
343 SC: StructureConstraint + fmt::Display,
344{
345 reference: SlaterDeterminant<'a, T, SC>,
348
349 occupation_patterns: Vec<Vec<Array1<<T as ComplexFloat>::Real>>>,
351}
352
353impl<'a, T, SC> FCIBasisBuilder<'a, T, SC>
354where
355 T: ComplexFloat + Lapack,
356 SC: StructureConstraint + fmt::Display,
357{
358 fn validate(&self) -> Result<(), String> {
359 if let Some(ref reference) = self.reference
360 && let Some(ref occupation_patterns) = self.occupation_patterns
361 {
362 let reference_occs = reference.occupations();
363 if occupation_patterns.iter().all(|occs| {
364 occs.len() == reference_occs.len()
365 && occs
366 .iter()
367 .zip(reference_occs)
368 .all(|(occ, reference_occ)| occ.shape() == reference_occ.shape())
369 }) {
370 Ok(())
371 } else {
372 Err("Inconsistent occupation patterns.".to_string())
373 }
374 } else {
375 Err("Missing `reference` or `occupation_patterns`.".to_string())
376 }
377 }
378}
379
380impl<'a, T, SC> FCIBasis<'a, T, SC>
381where
382 T: ComplexFloat + Lapack,
383 SC: StructureConstraint + fmt::Display + Clone,
384{
385 pub fn builder() -> FCIBasisBuilder<'a, T, SC> {
386 FCIBasisBuilder::<T, SC>::default()
387 }
388
389 pub fn reference(&self) -> &SlaterDeterminant<'a, T, SC> {
391 &self.reference
392 }
393
394 pub fn occupation_patterns(&self) -> &Vec<Vec<Array1<<T as ComplexFloat>::Real>>> {
396 &self.occupation_patterns
397 }
398}
399
400impl<'a, T, SC> Basis for FCIBasis<'a, T, SC>
401where
402 T: ComplexFloat + Lapack,
403 SC: 'a + StructureConstraint + fmt::Display + Clone,
404{
405 type Item = SlaterDeterminant<'a, T, SC>;
406
407 type BasisIter<'b>
408 = FCIBasisIterator<'b, 'a, T, SC>
409 where
410 Self: 'b;
411
412 fn n_items(&self) -> usize {
413 self.occupation_patterns.len()
414 }
415
416 fn iter(&self) -> Self::BasisIter<'_> {
420 FCIBasisIterator::new(self)
421 }
422
423 fn first(&self) -> Option<SlaterDeterminant<'a, T, SC>> {
424 Some(self.reference.clone())
425 }
426}
427
428#[derive(Builder, Clone)]
434pub struct FCIBasisIterator<'b, 'a, T, SC>
435where
436 T: ComplexFloat + Lapack,
437 SC: StructureConstraint + fmt::Display,
438{
439 fci_basis: &'b FCIBasis<'a, T, SC>,
441
442 current_occupation_index: usize,
444}
445
446impl<'b, 'a, T, SC> FCIBasisIterator<'b, 'a, T, SC>
447where
448 T: ComplexFloat + Lapack,
449 SC: StructureConstraint + fmt::Display,
450{
451 fn new(fci_basis: &'b FCIBasis<'a, T, SC>) -> Self {
461 Self {
462 fci_basis,
463 current_occupation_index: 0,
464 }
465 }
466}
467
468impl<'b, 'a, T, SC> Iterator for FCIBasisIterator<'b, 'a, T, SC>
469where
470 T: ComplexFloat + Lapack,
471 SC: StructureConstraint + fmt::Display + Clone,
472{
473 type Item = Result<SlaterDeterminant<'a, T, SC>, anyhow::Error>;
474
475 fn next(&mut self) -> Option<Self::Item> {
476 let reference = &self.fci_basis.reference;
477 let index = self.current_occupation_index;
478 self.current_occupation_index += 1;
479 if index >= self.fci_basis.occupation_patterns.len() {
480 None
481 } else {
482 let det = SlaterDeterminant::builder()
483 .structure_constraint(reference.structure_constraint().clone())
484 .baos(reference.baos().clone())
485 .complex_symmetric(reference.complex_symmetric())
486 .complex_conjugated(reference.complex_conjugated())
487 .mol(reference.mol())
488 .coefficients(reference.coefficients())
489 .occupations(self.fci_basis.occupation_patterns.get(index)?)
490 .mo_energies(reference.mo_energies().cloned())
491 .threshold(reference.threshold())
492 .build()
493 .map_err(|err| format_err!(err));
494 Some(det)
495 }
496 }
497}