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