qsym2/bindings/python/representation_analysis/multideterminant/
multideterminant_fci_basis.rs1use std::path::PathBuf;
4
5use itertools::Itertools;
6use num_complex::Complex;
7use numpy::{PyArray1, PyArrayMethods};
8use pyo3::exceptions::{PyIOError, PyRuntimeError};
9use pyo3::prelude::*;
10
11use crate::analysis::EigenvalueComparisonMode;
12use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled};
13use crate::bindings::python::integrals::{PyBasisAngularOrder, PyStructureConstraint};
14use crate::bindings::python::representation_analysis::slater_determinant::PySlaterDeterminant;
15use crate::bindings::python::representation_analysis::{PyArray1RC, PyArray2RC};
16use crate::drivers::QSym2Driver;
17use crate::drivers::representation_analysis::angular_function::AngularFunctionRepAnalysisParams;
18use crate::drivers::representation_analysis::multideterminant::{
19 MultiDeterminantRepAnalysisDriver, MultiDeterminantRepAnalysisParams,
20};
21use crate::drivers::representation_analysis::{
22 CharacterTableDisplay, MagneticSymmetryAnalysisKind,
23};
24use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
25use crate::io::format::qsym2_output;
26use crate::io::{QSym2FileType, read_qsym2_binary};
27use crate::symmetry::symmetry_group::{
28 MagneticRepresentedSymmetryGroup, UnitaryRepresentedSymmetryGroup,
29};
30use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
31use crate::target::determinant::SlaterDeterminant;
32use crate::target::noci::basis::FCIBasis;
33use crate::target::noci::multideterminant::MultiDeterminant;
34use crate::target::noci::multideterminants::MultiDeterminants;
35
36type C128 = Complex<f64>;
37
38#[allow(clippy::too_many_arguments)]
102#[pyfunction]
103#[pyo3(signature = (
104 inp_sym,
105 pydet,
106 coefficients,
107 energies,
108 occupation_patterns,
109 pybaos,
110 integrality_threshold,
111 linear_independence_threshold,
112 use_magnetic_group,
113 use_double_group,
114 use_cayley_table,
115 symmetry_transformation_kind,
116 eigenvalue_comparison_mode,
117 sao,
118 sao_h=None,
119 write_overlap_eigenvalues=true,
120 write_character_table=true,
121 infinite_order_to_finite=None,
122 angular_function_integrality_threshold=1e-7,
123 angular_function_linear_independence_threshold=1e-7,
124 angular_function_max_angular_momentum=2
125))]
126pub fn rep_analyse_multideterminants_fci_basis(
127 py: Python<'_>,
128 inp_sym: PathBuf,
129 pydet: PySlaterDeterminant,
130 coefficients: PyArray2RC,
131 energies: PyArray1RC,
132 occupation_patterns: Vec<Vec<Bound<'_, PyArray1<f64>>>>,
133 pybaos: Vec<PyBasisAngularOrder>,
134 integrality_threshold: f64,
135 linear_independence_threshold: f64,
136 use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
137 use_double_group: bool,
138 use_cayley_table: bool,
139 symmetry_transformation_kind: SymmetryTransformationKind,
140 eigenvalue_comparison_mode: EigenvalueComparisonMode,
141 sao: PyArray2RC,
142 sao_h: Option<PyArray2RC>,
143 write_overlap_eigenvalues: bool,
144 write_character_table: bool,
145 infinite_order_to_finite: Option<u32>,
146 angular_function_integrality_threshold: f64,
147 angular_function_linear_independence_threshold: f64,
148 angular_function_max_angular_momentum: u32,
149) -> PyResult<()> {
150 let pd_res: SymmetryGroupDetectionResult =
152 read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
153 .map_err(|err| PyIOError::new_err(err.to_string()))?;
154
155 let mut file_name = inp_sym.to_path_buf();
156 file_name.set_extension(QSym2FileType::Sym.ext());
157 qsym2_output!(
158 "Symmetry-group detection results read in from {}.",
159 file_name.display(),
160 );
161 qsym2_output!("");
162
163 let mol = &pd_res.pre_symmetry.recentred_molecule;
165
166 let baos = pybaos
167 .iter()
168 .map(|bao| {
169 bao.to_qsym2(mol)
170 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
171 })
172 .collect::<Result<Vec<_>, _>>()?;
173 let baos_ref = baos.iter().collect::<Vec<_>>();
174 let augment_to_generalised = match symmetry_transformation_kind {
175 SymmetryTransformationKind::SpatialWithSpinTimeReversal
176 | SymmetryTransformationKind::Spin
177 | SymmetryTransformationKind::SpinSpatial => true,
178 SymmetryTransformationKind::Spatial => false,
179 };
180 let afa_params = AngularFunctionRepAnalysisParams::builder()
181 .integrality_threshold(angular_function_integrality_threshold)
182 .linear_independence_threshold(angular_function_linear_independence_threshold)
183 .max_angular_momentum(angular_function_max_angular_momentum)
184 .build()
185 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
186 let mda_params = MultiDeterminantRepAnalysisParams::<f64>::builder()
187 .integrality_threshold(integrality_threshold)
188 .linear_independence_threshold(linear_independence_threshold)
189 .use_magnetic_group(use_magnetic_group.clone())
190 .use_double_group(use_double_group)
191 .use_cayley_table(use_cayley_table)
192 .symmetry_transformation_kind(symmetry_transformation_kind.clone())
193 .eigenvalue_comparison_mode(eigenvalue_comparison_mode)
194 .write_overlap_eigenvalues(write_overlap_eigenvalues)
195 .write_character_table(if write_character_table {
196 Some(CharacterTableDisplay::Symbolic)
197 } else {
198 None
199 })
200 .infinite_order_to_finite(infinite_order_to_finite)
201 .build()
202 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
203
204 let real = matches!(pydet, PySlaterDeterminant::Real(_));
205
206 let structure_constraint = match pydet {
207 PySlaterDeterminant::Real(ref pd) => pd.structure_constraint().clone(),
208 PySlaterDeterminant::Complex(ref pd) => pd.structure_constraint().clone(),
209 };
210
211 let occupation_patterns = occupation_patterns
212 .into_iter()
213 .map(|occs| {
214 occs.into_iter()
215 .map(|occ| occ.to_owned_array())
216 .collect_vec()
217 })
218 .collect_vec();
219
220 match (real, &coefficients, &energies, &sao) {
240 (
241 true,
242 PyArray2RC::Real(pycoefficients_r),
243 PyArray1RC::Real(pyenergies_r),
244 PyArray2RC::Real(pysao_r),
245 ) => {
246 if matches!(
248 structure_constraint,
249 PyStructureConstraint::SpinOrbitCoupled(_)
250 ) {
251 return Err(PyRuntimeError::new_err(
252 "Real determinants cannot support spin--orbit-coupled structure constraint.",
253 ));
254 }
255
256 let sao_r = pysao_r.to_owned_array();
258 let coefficients_r = pycoefficients_r.to_owned_array();
259 let energies_r = pyenergies_r.to_owned_array();
260 let det_r = if augment_to_generalised {
261 if let PySlaterDeterminant::Real(ref pydet_r) = pydet {
262 pydet_r
263 .to_qsym2(&baos_ref, mol)
264 .map(|det_r_inner| det_r_inner.to_generalised())
265 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
266 } else {
267 Err(PyRuntimeError::new_err(
268 "Unexpected complex type for a Slater determinant.".to_string(),
269 ))
270 }
271 } else {
272 if let PySlaterDeterminant::Real(ref pydet_r) = pydet {
273 pydet_r
274 .to_qsym2(&baos_ref, mol)
275 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
276 } else {
277 Err(PyRuntimeError::new_err(
278 "Unexpected complex type for a Slater determinant.".to_string(),
279 ))
280 }
281 }?;
282
283 let n_energies = energies_r.len();
284 if coefficients_r.shape()[1] != n_energies {
285 return Err(PyRuntimeError::new_err(
286 "Mismatched number of FCI energies and number of FCI states.",
287 ));
288 }
289
290 let fci_basis = FCIBasis::builder()
292 .reference(det_r)
293 .occupation_patterns(occupation_patterns)
294 .build()
295 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
296
297 let multidets_vec = energies_r
299 .iter()
300 .zip(coefficients_r.columns())
301 .map(|(energy, coeffs)| {
302 MultiDeterminant::builder()
303 .basis(fci_basis.clone())
304 .coefficients(coeffs.to_owned())
305 .threshold(1e-7)
306 .energy(Ok(*energy))
307 .build()
308 })
309 .collect::<Result<Vec<_>, _>>()
310 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
311 let multidets = MultiDeterminants::from_multideterminant_vec(
312 &multidets_vec.iter().collect::<Vec<_>>()
313 )
314 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
315
316 match &use_magnetic_group {
318 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
319 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
320 MagneticRepresentedSymmetryGroup,
321 f64,
322 _,
323 SpinConstraint,
324 >::builder()
325 .parameters(&mda_params)
326 .angular_function_parameters(&afa_params)
327 .multidets(&multidets)
328 .sao(&sao_r)
329 .sao_h(None) .symmetry_group(&pd_res)
331 .build()
332 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
333
334 py.detach(|| {
336 mda_driver
337 .run()
338 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
339 })?
340 }
341 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
342 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
343 UnitaryRepresentedSymmetryGroup,
344 f64,
345 _,
346 SpinConstraint,
347 >::builder()
348 .parameters(&mda_params)
349 .angular_function_parameters(&afa_params)
350 .multidets(&multidets)
351 .sao(&sao_r)
352 .sao_h(None) .symmetry_group(&pd_res)
354 .build()
355 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
356
357 py.detach(|| {
359 mda_driver
360 .run()
361 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
362 })?
363 }
364 }
365 }
366 (_, _, _, _) => {
367 let sao_c = match sao {
371 PyArray2RC::Real(pysao_r) => pysao_r.to_owned_array().mapv(Complex::from),
372 PyArray2RC::Complex(pysao_c) => pysao_c.to_owned_array(),
373 };
374 let sao_h_c = sao_h.map(|pysao_h| match pysao_h {
375 PyArray2RC::Real(pysao_h_r) => pysao_h_r.to_owned_array().mapv(Complex::from),
377 PyArray2RC::Complex(pysao_h_c) => pysao_h_c.to_owned_array(),
378 });
379 let coefficients_c = match coefficients {
380 PyArray2RC::Real(pycoefficients_r) => {
381 pycoefficients_r.to_owned_array().mapv(Complex::from)
382 }
383 PyArray2RC::Complex(pycoefficients_c) => pycoefficients_c.to_owned_array(),
384 };
385 let energies_c = match energies {
386 PyArray1RC::Real(pyenergies_r) => pyenergies_r.to_owned_array().mapv(Complex::from),
387 PyArray1RC::Complex(pyenergies_c) => pyenergies_c.to_owned_array(),
388 };
389
390 match structure_constraint {
391 PyStructureConstraint::SpinConstraint(_) => {
392 let det_c = if augment_to_generalised {
393 match pydet {
394 PySlaterDeterminant::Real(ref pydet_r) => pydet_r
395 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
396 .map(|det_r| {
397 SlaterDeterminant::<C128, SpinConstraint>::from(det_r)
398 .to_generalised()
399 }),
400 PySlaterDeterminant::Complex(ref pydet_c) => pydet_c
401 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
402 .map(|det_c| det_c.to_generalised()),
403 }
404 } else {
405 match pydet {
406 PySlaterDeterminant::Real(ref pydet_r) => pydet_r
407 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
408 .map(|det_r| {
409 SlaterDeterminant::<C128, SpinConstraint>::from(det_r)
410 }),
411 PySlaterDeterminant::Complex(ref pydet_c) => {
412 pydet_c.to_qsym2::<SpinConstraint>(&baos_ref, mol)
413 }
414 }
415 }
416 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
417
418 let n_energies = energies_c.len();
419 if coefficients_c.shape()[1] != n_energies {
420 return Err(PyRuntimeError::new_err(
421 "Mismatched number of FCI energies and number of FCI states.",
422 ));
423 }
424
425 let fci_basis = FCIBasis::builder()
427 .reference(det_c)
428 .occupation_patterns(occupation_patterns)
429 .build()
430 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
431
432 let multidets_vec = energies_c
434 .iter()
435 .zip(coefficients_c.columns())
436 .map(|(energy, coeffs)| {
437 MultiDeterminant::builder()
438 .basis(fci_basis.clone())
439 .coefficients(coeffs.to_owned())
440 .threshold(1e-7)
441 .energy(Ok(*energy))
442 .build()
443 })
444 .collect::<Result<Vec<_>, _>>()
445 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
446 let multidets = MultiDeterminants::from_multideterminant_vec(
447 &multidets_vec.iter().collect::<Vec<_>>()
448 )
449 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
450
451 match &use_magnetic_group {
453 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
454 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
455 MagneticRepresentedSymmetryGroup,
456 C128,
457 _,
458 SpinConstraint,
459 >::builder()
460 .parameters(&mda_params)
461 .angular_function_parameters(&afa_params)
462 .multidets(&multidets)
463 .sao(&sao_c)
464 .sao_h(sao_h_c.as_ref())
465 .symmetry_group(&pd_res)
466 .build()
467 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
468
469 py.detach(|| {
471 mda_driver
472 .run()
473 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
474 })?
475 }
476 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
477 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
478 UnitaryRepresentedSymmetryGroup,
479 C128,
480 _,
481 SpinConstraint,
482 >::builder()
483 .parameters(&mda_params)
484 .angular_function_parameters(&afa_params)
485 .multidets(&multidets)
486 .sao(&sao_c)
487 .sao_h(sao_h_c.as_ref())
488 .symmetry_group(&pd_res)
489 .build()
490 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
491
492 py.detach(|| {
494 mda_driver
495 .run()
496 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
497 })?
498 }
499 }
500 }
501 PyStructureConstraint::SpinOrbitCoupled(_) => {
502 let det_c = match pydet {
503 PySlaterDeterminant::Real(ref pydet_r) => pydet_r
504 .to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
505 .map(SlaterDeterminant::<C128, SpinOrbitCoupled>::from),
506 PySlaterDeterminant::Complex(ref pydet_c) => {
507 pydet_c.to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
508 }
509 }
510 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
511
512 let n_energies = energies_c.len();
513 if coefficients_c.shape()[1] != n_energies {
514 return Err(PyRuntimeError::new_err(
515 "Mismatched number of FCI energies and number of FCI states.",
516 ));
517 }
518
519 let fci_basis = FCIBasis::builder()
521 .reference(det_c)
522 .occupation_patterns(occupation_patterns)
523 .build()
524 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
525
526 let multidets_vec = energies_c
528 .iter()
529 .zip(coefficients_c.columns())
530 .map(|(energy, coeffs)| {
531 MultiDeterminant::builder()
532 .basis(fci_basis.clone())
533 .coefficients(coeffs.to_owned())
534 .threshold(1e-7)
535 .energy(Ok(*energy))
536 .build()
537 })
538 .collect::<Result<Vec<_>, _>>()
539 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
540 let multidets = MultiDeterminants::from_multideterminant_vec(
541 &multidets_vec.iter().collect::<Vec<_>>()
542 )
543 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
544
545 match &use_magnetic_group {
547 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
548 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
549 MagneticRepresentedSymmetryGroup,
550 C128,
551 _,
552 SpinOrbitCoupled,
553 >::builder()
554 .parameters(&mda_params)
555 .angular_function_parameters(&afa_params)
556 .multidets(&multidets)
557 .sao(&sao_c)
558 .sao_h(sao_h_c.as_ref())
559 .symmetry_group(&pd_res)
560 .build()
561 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
562
563 py.detach(|| {
565 mda_driver
566 .run()
567 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
568 })?
569 }
570 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
571 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
572 UnitaryRepresentedSymmetryGroup,
573 C128,
574 _,
575 SpinOrbitCoupled,
576 >::builder()
577 .parameters(&mda_params)
578 .angular_function_parameters(&afa_params)
579 .multidets(&multidets)
580 .sao(&sao_c)
581 .sao_h(sao_h_c.as_ref())
582 .symmetry_group(&pd_res)
583 .build()
584 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
585
586 py.detach(|| {
588 mda_driver
589 .run()
590 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
591 })?
592 }
593 }
594 }
595 }
596 }
597 }
598 Ok(())
599}