1use std::collections::HashSet;
5use std::path::PathBuf;
6
7use anyhow::{bail, format_err};
8use itertools::Itertools;
9use log;
10use ndarray::{Array1, Array2, Axis, ShapeBuilder};
11use num_complex::Complex;
12use numpy::{PyArrayMethods, ToPyArray};
13use pyo3::exceptions::{PyIOError, PyRuntimeError, PyTypeError};
14use pyo3::types::PyFunction;
15use pyo3::{IntoPyObjectExt, prelude::*};
16
17use crate::analysis::EigenvalueComparisonMode;
18use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled};
19use crate::bindings::python::integrals::{PyBasisAngularOrder, PyStructureConstraint};
20use crate::bindings::python::representation_analysis::multideterminant::{
21 PyMultiDeterminantsComplex, PyMultiDeterminantsReal,
22};
23use crate::bindings::python::representation_analysis::slater_determinant::PySlaterDeterminant;
24use crate::bindings::python::representation_analysis::{PyArray2RC, PyArray4RC, PyScalarRC};
25use crate::drivers::QSym2Driver;
26use crate::drivers::representation_analysis::angular_function::AngularFunctionRepAnalysisParams;
27use crate::drivers::representation_analysis::multideterminant::{
28 MultiDeterminantRepAnalysisDriver, MultiDeterminantRepAnalysisParams,
29};
30use crate::drivers::representation_analysis::{
31 CharacterTableDisplay, MagneticSymmetryAnalysisKind,
32};
33use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
34use crate::io::format::qsym2_output;
35use crate::io::{QSym2FileType, read_qsym2_binary};
36use crate::symmetry::symmetry_group::{
37 MagneticRepresentedSymmetryGroup, SymmetryGroupProperties, UnitaryRepresentedSymmetryGroup,
38};
39use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
40use crate::target::noci::backend::matelem::hamiltonian::HamiltonianAO;
41use crate::target::noci::backend::matelem::overlap::OverlapAO;
42use crate::target::noci::backend::solver::noci::SymmetryOrbitNOCISolvable;
43use crate::target::noci::basis::Basis;
44use crate::target::noci::multideterminants::MultiDeterminants;
45
46#[macro_export]
50macro_rules! generate_get_jk {
51 ($get_jk_name:ident, $py_get_jk_func:ident, $t:ty) => {
52 let $get_jk_name = move |dm: &Array2<$t>| {
53 Python::attach(|py_inner| {
54 let py_dm = dm.to_pyarray(py_inner);
55 $py_get_jk_func
56 .call1(py_inner, (py_dm,))
57 .and_then(|res| res.extract::<(Vec<Vec<$t>>, Vec<Vec<$t>>)>(py_inner))
58 .and_then(|(jss, kss)| {
59 let nrows = jss.len();
60 let ncols_set = jss.iter().map(|js| js.len()).collect::<HashSet<usize>>();
61 if ncols_set.len() != 1 {
62 return Err(PyRuntimeError::new_err(
63 "Inconsistent numbers of columns.".to_string(),
64 ));
65 }
66 let ncols = *ncols_set
67 .iter()
68 .next()
69 .unwrap_or_else(|| panic!("Unable to extract the number of columns."));
70
71 let j = Array2::<$t>::from_shape_vec(
72 (nrows, ncols),
73 jss.into_iter().flatten().collect::<Vec<_>>(),
74 )
75 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
76 let k = Array2::<$t>::from_shape_vec(
77 (nrows, ncols),
78 kss.into_iter().flatten().collect::<Vec<_>>(),
79 )
80 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
81 Ok((j, k))
82 })
83 .map_err(|err| format_err!(err))
84 })
85 };
86 };
87}
88
89#[allow(clippy::too_many_arguments)]
171#[pyfunction]
172#[pyo3(signature = (
173 inp_sym,
174 pyorigins,
175 pybaos,
176 sao,
177 enuc,
178 onee,
179 twoe,
180 py_get_jk,
181 thresh_offdiag,
182 thresh_zeroov,
183 calculate_density_matrices,
184 integrality_threshold,
185 linear_independence_threshold,
186 orbit_overlap_symmetry_threshold,
187 use_magnetic_group,
188 use_double_group,
189 use_cayley_table,
190 symmetry_transformation_kind,
191 eigenvalue_comparison_mode,
192 sao_h=None,
193 write_overlap_eigenvalues=true,
194 write_character_table=true,
195 infinite_order_to_finite=None,
196 angular_function_integrality_threshold=1e-7,
197 angular_function_linear_independence_threshold=1e-7,
198 angular_function_orbit_overlap_symmetry_threshold=1e-7,
199 angular_function_max_angular_momentum=2
200))]
201pub fn rep_analyse_multideterminants_orbit_basis_internal_solver(
202 py: Python<'_>,
203 inp_sym: PathBuf,
204 pyorigins: Vec<PySlaterDeterminant>,
205 pybaos: Vec<PyBasisAngularOrder>,
206 sao: PyArray2RC,
207 enuc: PyScalarRC,
208 onee: PyArray2RC,
209 twoe: Option<PyArray4RC>,
210 py_get_jk: Option<Py<PyFunction>>,
211 thresh_offdiag: f64,
212 thresh_zeroov: f64,
213 calculate_density_matrices: bool,
214 integrality_threshold: f64,
215 linear_independence_threshold: f64,
216 orbit_overlap_symmetry_threshold: f64,
217 use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
218 use_double_group: bool,
219 use_cayley_table: bool,
220 symmetry_transformation_kind: SymmetryTransformationKind,
221 eigenvalue_comparison_mode: EigenvalueComparisonMode,
222 sao_h: Option<PyArray2RC>,
223 write_overlap_eigenvalues: bool,
224 write_character_table: bool,
225 infinite_order_to_finite: Option<u32>,
226 angular_function_integrality_threshold: f64,
227 angular_function_linear_independence_threshold: f64,
228 angular_function_orbit_overlap_symmetry_threshold: f64,
229 angular_function_max_angular_momentum: u32,
230) -> PyResult<Py<PyAny>> {
231 let pd_res: SymmetryGroupDetectionResult =
233 read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
234 .map_err(|err| PyIOError::new_err(err.to_string()))?;
235
236 let mut file_name = inp_sym.to_path_buf();
237 file_name.set_extension(QSym2FileType::Sym.ext());
238 qsym2_output!(
239 "Symmetry-group detection results read in from {}.",
240 file_name.display(),
241 );
242 qsym2_output!("");
243
244 let mol = &pd_res.pre_symmetry.recentred_molecule;
246
247 let baos = pybaos
248 .iter()
249 .map(|bao| {
250 bao.to_qsym2(mol)
251 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
252 })
253 .collect::<Result<Vec<_>, _>>()?;
254 let baos_ref = baos.iter().collect::<Vec<_>>();
255 let augment_to_generalised = match symmetry_transformation_kind {
256 SymmetryTransformationKind::SpatialWithSpinTimeReversal
257 | SymmetryTransformationKind::Spin
258 | SymmetryTransformationKind::SpinSpatial => true,
259 SymmetryTransformationKind::Spatial => false,
260 };
261 let afa_params = AngularFunctionRepAnalysisParams::builder()
262 .integrality_threshold(angular_function_integrality_threshold)
263 .linear_independence_threshold(angular_function_linear_independence_threshold)
264 .orbit_overlap_symmetry_threshold(angular_function_orbit_overlap_symmetry_threshold)
265 .max_angular_momentum(angular_function_max_angular_momentum)
266 .build()
267 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
268 let mda_params = MultiDeterminantRepAnalysisParams::<f64>::builder()
269 .integrality_threshold(integrality_threshold)
270 .linear_independence_threshold(linear_independence_threshold)
271 .orbit_overlap_symmetry_threshold(orbit_overlap_symmetry_threshold)
272 .use_magnetic_group(use_magnetic_group.clone())
273 .use_double_group(use_double_group)
274 .use_cayley_table(use_cayley_table)
275 .symmetry_transformation_kind(symmetry_transformation_kind.clone())
276 .eigenvalue_comparison_mode(eigenvalue_comparison_mode)
277 .write_overlap_eigenvalues(write_overlap_eigenvalues)
278 .write_character_table(if write_character_table {
279 Some(CharacterTableDisplay::Symbolic)
280 } else {
281 None
282 })
283 .infinite_order_to_finite(infinite_order_to_finite)
284 .build()
285 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
286
287 let all_real_origins = pyorigins
288 .iter()
289 .all(|pyorigin| matches!(pyorigin, PySlaterDeterminant::Real(_)));
290 let all_real_integrals = matches!(sao, PyArray2RC::Real(_))
291 && matches!(enuc, PyScalarRC::Real(_))
292 && matches!(onee, PyArray2RC::Real(_))
293 && matches!(twoe, Some(PyArray4RC::Real(_)) | None);
294 let all_real = all_real_origins && all_real_integrals;
295
296 let structure_constraints_set = pyorigins
297 .iter()
298 .map(|pyorigin| match pyorigin {
299 PySlaterDeterminant::Real(pydet) => pydet.structure_constraint().clone(),
300 PySlaterDeterminant::Complex(pydet) => pydet.structure_constraint().clone(),
301 })
302 .collect::<HashSet<_>>();
303 if structure_constraints_set.len() != 1 {
304 return Err(PyRuntimeError::new_err(
305 "Inconsistent structure constraints across origin determinants.`",
306 ));
307 };
308 let structure_constraint = structure_constraints_set
309 .iter()
310 .next()
311 .ok_or_else(|| PyRuntimeError::new_err("Unable to retrieve the structure constraint."))?;
312
313 if all_real {
333 if matches!(
337 structure_constraint,
338 PyStructureConstraint::SpinOrbitCoupled(_)
339 ) {
340 return Err(PyRuntimeError::new_err(
341 "Real determinants cannot support spin--orbit-coupled structure constraint.",
342 ));
343 }
344
345 let sao_r = match sao {
347 PyArray2RC::Real(pysao_r) => Ok(pysao_r.to_owned_array()),
348 PyArray2RC::Complex(_) => Err(PyTypeError::new_err(
349 "Unexpected complex type for the SAO matrix.",
350 )),
351 }?;
352 let enuc_r = match enuc {
353 PyScalarRC::Real(enuc_r) => Ok(enuc_r),
354 PyScalarRC::Complex(_) => Err(PyTypeError::new_err(
355 "Unexpected complex type for the nuclear repulsion energy.",
356 )),
357 }?;
358 let onee_r = match onee {
359 PyArray2RC::Real(pyonee_r) => Ok(pyonee_r.to_owned_array()),
360 PyArray2RC::Complex(_) => Err(PyTypeError::new_err(
361 "Unexpected complex type for the one-electron integral matrix.",
362 )),
363 }?;
364 let twoe_r = match twoe {
365 Some(PyArray4RC::Real(pytwoe_r)) => Ok(Some(pytwoe_r.to_owned_array())),
366 Some(PyArray4RC::Complex(_)) => Err(PyTypeError::new_err(
367 "Unexpected complex type for the two-electron integral tensor.",
368 )),
369 None => Ok(None),
370 }?;
371 let get_jk_r = py_get_jk.map(|py_get_jk_inner| {
372 generate_get_jk!(get_jk_r, py_get_jk_inner, f64);
373 get_jk_r
374 });
375 let overlap_ao = OverlapAO::<f64, SpinConstraint>::builder()
376 .sao(sao_r.view())
377 .build()
378 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
379 let hamiltonian_ao = HamiltonianAO::<f64, SpinConstraint, _>::builder()
380 .enuc(enuc_r)
381 .onee(onee_r.view())
382 .twoe(twoe_r.as_ref().map(|m| m.view()))
383 .get_jk(get_jk_r)
384 .build()
385 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
386
387 let origins_r = if augment_to_generalised {
388 pyorigins
389 .iter()
390 .map(|pydet| {
391 if let PySlaterDeterminant::Real(pydet_r) = pydet {
392 pydet_r
393 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
394 .map(|det_r| det_r.to_generalised())
395 } else {
396 bail!("Unexpected complex type for an origin Slater determinant.")
397 }
398 })
399 .collect::<Result<Vec<_>, _>>()
400 } else {
401 pyorigins
402 .iter()
403 .map(|pydet| {
404 if let PySlaterDeterminant::Real(pydet_r) = pydet {
405 pydet_r.to_qsym2::<SpinConstraint>(&baos_ref, mol)
406 } else {
407 bail!("Unexpected complex type for an origin Slater determinant.")
408 }
409 })
410 .collect::<Result<Vec<_>, _>>()
411 }
412 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
413
414 match &use_magnetic_group {
415 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
416 let group = py
418 .detach(|| {
419 let magsym = pd_res
420 .magnetic_symmetry
421 .as_ref()
422 .ok_or(format_err!("Magnetic group required for orbit construction, but no magnetic symmetry found."))?;
423 if use_double_group {
424 MagneticRepresentedSymmetryGroup::from_molecular_symmetry(
425 magsym,
426 infinite_order_to_finite,
427 )
428 .and_then(|grp| grp.to_double_group())
429 } else {
430 MagneticRepresentedSymmetryGroup::from_molecular_symmetry(
431 magsym,
432 infinite_order_to_finite,
433 )
434 }
435 })
436 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
437
438 let system = (&hamiltonian_ao, &overlap_ao);
440 let multidets_vec = system
441 .solve_symmetry_orbit_noci(
442 &origins_r.iter().collect_vec(),
443 &group,
444 symmetry_transformation_kind,
445 use_cayley_table,
446 thresh_offdiag,
447 thresh_zeroov,
448 )
449 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
450 let multidets = MultiDeterminants::from_multideterminant_vec(
451 &multidets_vec.iter().collect::<Vec<_>>(),
452 )
453 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
454
455 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
457 MagneticRepresentedSymmetryGroup,
458 f64,
459 _,
460 SpinConstraint,
461 >::builder()
462 .parameters(&mda_params)
463 .angular_function_parameters(&afa_params)
464 .multidets(&multidets)
465 .sao(&sao_r)
466 .sao_h(None) .symmetry_group(&pd_res)
468 .build()
469 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
470 py.detach(|| {
471 mda_driver
472 .run()
473 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
474 })?;
475
476 let basis = multidets
478 .basis()
479 .iter()
480 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
481 .collect::<Result<Vec<_>, _>>()
482 .map_err(|_| {
483 PyRuntimeError::new_err(
484 "Unable to obtain the basis of Slater determinants.".to_string(),
485 )
486 })?;
487 let (coefficientss, energies): (Vec<_>, Vec<_>) = multidets_vec
488 .iter()
489 .map(|multidet| {
490 let coefficients = multidet.coefficients().iter().cloned().collect_vec();
491 let energy = *multidet.energy().unwrap_or(&f64::NAN);
492 Ok::<_, PyErr>((coefficients, energy))
493 })
494 .collect::<Result<Vec<_>, _>>()?
495 .into_iter()
496 .unzip();
497 let coefficientss_arr = Array2::from_shape_vec(
498 (basis.len(), coefficientss.len()).f(),
499 coefficientss.into_iter().flatten().collect_vec(),
500 )
501 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
502 .to_pyarray(py);
503 let energies_arr = Array1::from_vec(energies).to_pyarray(py);
504 let density_matrices = if calculate_density_matrices {
505 log::debug!("Calculating density matrices...");
506 let denmats_opt = multidets
507 .density_matrices(&sao_r.view(), thresh_offdiag, thresh_zeroov, true)
508 .map(|denmats| {
509 denmats
510 .axis_iter(Axis(0))
511 .map(|denmat| denmat.to_pyarray(py))
512 .collect_vec()
513 })
514 .ok();
515 log::debug!("Calculating density matrices... Done.");
516 denmats_opt
517 } else {
518 None
519 };
520 let pymultidet = PyMultiDeterminantsReal::new(
521 basis,
522 coefficientss_arr,
523 energies_arr,
524 density_matrices,
525 multidets.threshold(),
526 )
527 .into_py_any(py)?;
528 Ok(pymultidet)
529 }
530 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
531 let group = py
533 .detach(|| {
534 let sym = if use_magnetic_group.is_some() {
535 pd_res
536 .magnetic_symmetry
537 .as_ref()
538 .ok_or(format_err!("Magnetic group required for orbit construction, but no magnetic symmetry found."))?
539 } else {
540 &pd_res.unitary_symmetry
541 };
542 if use_double_group {
543 UnitaryRepresentedSymmetryGroup::from_molecular_symmetry(
544 sym,
545 infinite_order_to_finite,
546 )
547 .and_then(|grp| grp.to_double_group())
548 } else {
549 UnitaryRepresentedSymmetryGroup::from_molecular_symmetry(
550 sym,
551 infinite_order_to_finite,
552 )
553 }
554 })
555 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
556
557 let system = (&hamiltonian_ao, &overlap_ao);
559 let multidets_vec = system
560 .solve_symmetry_orbit_noci(
561 &origins_r.iter().collect_vec(),
562 &group,
563 symmetry_transformation_kind,
564 use_cayley_table,
565 thresh_offdiag,
566 thresh_zeroov,
567 )
568 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
569 let multidets = MultiDeterminants::from_multideterminant_vec(
570 &multidets_vec.iter().collect::<Vec<_>>(),
571 )
572 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
573
574 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
576 UnitaryRepresentedSymmetryGroup,
577 f64,
578 _,
579 SpinConstraint,
580 >::builder()
581 .parameters(&mda_params)
582 .angular_function_parameters(&afa_params)
583 .multidets(&multidets)
584 .sao(&sao_r)
585 .sao_h(None) .symmetry_group(&pd_res)
587 .build()
588 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
589 py.detach(|| {
590 mda_driver
591 .run()
592 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
593 })?;
594
595 let basis = multidets
597 .basis()
598 .iter()
599 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
600 .collect::<Result<Vec<_>, _>>()
601 .map_err(|_| {
602 PyRuntimeError::new_err(
603 "Unable to obtain the basis of Slater determinants.".to_string(),
604 )
605 })?;
606 let (coefficientss, energies): (Vec<_>, Vec<_>) = multidets_vec
607 .iter()
608 .map(|multidet| {
609 let coefficients = multidet.coefficients().iter().cloned().collect_vec();
610 let energy = *multidet.energy().unwrap_or(&f64::NAN);
611 Ok::<_, PyErr>((coefficients, energy))
612 })
613 .collect::<Result<Vec<_>, _>>()?
614 .into_iter()
615 .unzip();
616 let coefficientss_arr = Array2::from_shape_vec(
617 (basis.len(), coefficientss.len()).f(),
618 coefficientss.into_iter().flatten().collect_vec(),
619 )
620 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
621 .to_pyarray(py);
622 let energies_arr = Array1::from_vec(energies).to_pyarray(py);
623 let density_matrices = if calculate_density_matrices {
624 log::debug!("Calculating density matrices...");
625 let denmats_opt = multidets
626 .density_matrices(&sao_r.view(), thresh_offdiag, thresh_zeroov, true)
627 .map(|denmats| {
628 denmats
629 .axis_iter(Axis(0))
630 .map(|denmat| denmat.to_pyarray(py))
631 .collect_vec()
632 })
633 .ok();
634 log::debug!("Calculating density matrices... Done.");
635 denmats_opt
636 } else {
637 None
638 };
639 let pymultidet = PyMultiDeterminantsReal::new(
640 basis,
641 coefficientss_arr,
642 energies_arr,
643 density_matrices,
644 multidets.threshold(),
645 )
646 .into_py_any(py)?;
647 Ok(pymultidet)
648 }
649 }
650 } else {
651 let sao_c = match sao {
655 PyArray2RC::Real(pysao_r) => pysao_r.to_owned_array().mapv(Complex::from),
656 PyArray2RC::Complex(pysao_c) => pysao_c.to_owned_array(),
657 };
658 let sao_h_c = match sao_h {
659 Some(PyArray2RC::Real(pysao_r)) => Some(pysao_r.to_owned_array().mapv(Complex::from)),
660 Some(PyArray2RC::Complex(pysao_c)) => Some(pysao_c.to_owned_array()),
661 None => None,
662 };
663 let enuc_c = match enuc {
664 PyScalarRC::Real(enuc_r) => Complex::from(enuc_r),
665 PyScalarRC::Complex(enuc_c) => enuc_c,
666 };
667 let onee_c = match onee {
668 PyArray2RC::Real(pyonee_r) => pyonee_r.to_owned_array().mapv(Complex::from),
669 PyArray2RC::Complex(pyonee_c) => pyonee_c.to_owned_array(),
670 };
671 let twoe_c = match twoe {
672 Some(PyArray4RC::Real(pytwoe_r)) => Some(pytwoe_r.to_owned_array().mapv(Complex::from)),
673 Some(PyArray4RC::Complex(pytwoe_c)) => Some(pytwoe_c.to_owned_array()),
674 None => None,
675 };
676 let get_jk_c = py_get_jk.map(|py_get_jk_inner| {
677 generate_get_jk!(get_jk_c, py_get_jk_inner, Complex<f64>);
678 get_jk_c
679 });
680
681 match structure_constraint {
682 PyStructureConstraint::SpinConstraint(_) => {
683 let overlap_ao = OverlapAO::<Complex<f64>, SpinConstraint>::builder()
684 .sao(sao_c.view())
685 .build()
686 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
687 let hamiltonian_ao = HamiltonianAO::<Complex<f64>, SpinConstraint, _>::builder()
688 .enuc(enuc_c)
689 .onee(onee_c.view())
690 .twoe(twoe_c.as_ref().map(|m| m.view()))
691 .get_jk(get_jk_c.as_ref())
692 .build()
693 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
694
695 let origins_c = if augment_to_generalised {
696 pyorigins
697 .iter()
698 .map(|pydet| match pydet {
699 PySlaterDeterminant::Real(pydet_r) => pydet_r
700 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
701 .map(|det_r| det_r.to_generalised().into()),
702 PySlaterDeterminant::Complex(pydet_c) => pydet_c
703 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
704 .map(|det_c| det_c.to_generalised()),
705 })
706 .collect::<Result<Vec<_>, _>>()
707 } else {
708 pyorigins
709 .iter()
710 .map(|pydet| match pydet {
711 PySlaterDeterminant::Real(pydet_r) => pydet_r
712 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
713 .map(|det_r| det_r.into()),
714 PySlaterDeterminant::Complex(pydet_c) => {
715 pydet_c.to_qsym2::<SpinConstraint>(&baos_ref, mol)
716 }
717 })
718 .collect::<Result<Vec<_>, _>>()
719 }
720 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
721
722 match &use_magnetic_group {
723 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
724 let group = py
726 .detach(|| {
727 let magsym = pd_res
728 .magnetic_symmetry
729 .as_ref()
730 .ok_or(format_err!("Magnetic group required for orbit construction, but no magnetic symmetry found."))?;
731 if use_double_group {
732 MagneticRepresentedSymmetryGroup::from_molecular_symmetry(
733 magsym,
734 infinite_order_to_finite,
735 )
736 .and_then(|grp| grp.to_double_group())
737 } else {
738 MagneticRepresentedSymmetryGroup::from_molecular_symmetry(
739 magsym,
740 infinite_order_to_finite,
741 )
742 }
743 })
744 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
745
746 let system = (&hamiltonian_ao, &overlap_ao);
748 let multidets_vec = system
749 .solve_symmetry_orbit_noci(
750 &origins_c.iter().collect_vec(),
751 &group,
752 symmetry_transformation_kind,
753 use_cayley_table,
754 thresh_offdiag,
755 thresh_zeroov,
756 )
757 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
758 let multidets = MultiDeterminants::from_multideterminant_vec(
759 &multidets_vec.iter().collect::<Vec<_>>(),
760 )
761 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
762
763 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
765 MagneticRepresentedSymmetryGroup,
766 Complex<f64>,
767 _,
768 SpinConstraint,
769 >::builder()
770 .parameters(&mda_params)
771 .angular_function_parameters(&afa_params)
772 .multidets(&multidets)
773 .sao(&sao_c)
774 .sao_h(sao_h_c.as_ref())
775 .symmetry_group(&pd_res)
776 .build()
777 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
778 py.detach(|| {
779 mda_driver
780 .run()
781 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
782 })?;
783
784 let basis = multidets
786 .basis()
787 .iter()
788 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
789 .collect::<Result<Vec<_>, _>>()
790 .map_err(|_| {
791 PyRuntimeError::new_err(
792 "Unable to obtain the basis of Slater determinants."
793 .to_string(),
794 )
795 })?;
796 let (coefficientss, energies): (Vec<_>, Vec<_>) = multidets_vec
797 .iter()
798 .map(|multidet| {
799 let coefficients =
800 multidet.coefficients().iter().cloned().collect_vec();
801 let energy = *multidet.energy().unwrap_or(&Complex::from(f64::NAN));
802 Ok::<_, PyErr>((coefficients, energy))
803 })
804 .collect::<Result<Vec<_>, _>>()?
805 .into_iter()
806 .unzip();
807 let coefficientss_arr = Array2::from_shape_vec(
808 (basis.len(), coefficientss.len()).f(),
809 coefficientss.into_iter().flatten().collect_vec(),
810 )
811 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
812 .to_pyarray(py);
813 let energies_arr = Array1::from_vec(energies).to_pyarray(py);
814 let density_matrices = if calculate_density_matrices {
815 log::debug!("Calculating density matrices...");
816 let denmats_opt = multidets
817 .density_matrices(
818 &sao_c.view(),
819 thresh_offdiag,
820 thresh_zeroov,
821 true,
822 )
823 .map(|denmats| {
824 denmats
825 .axis_iter(Axis(0))
826 .map(|denmat| denmat.to_pyarray(py))
827 .collect_vec()
828 })
829 .ok();
830 log::debug!("Calculating density matrices... Done.");
831 denmats_opt
832 } else {
833 None
834 };
835 let pymultidet = PyMultiDeterminantsComplex::new(
836 basis,
837 coefficientss_arr,
838 energies_arr,
839 density_matrices,
840 multidets.threshold(),
841 )
842 .into_py_any(py)?;
843 Ok(pymultidet)
844 }
845 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
846 let group = py
848 .detach(|| {
849 let sym = if use_magnetic_group.is_some() {
850 pd_res
851 .magnetic_symmetry
852 .as_ref()
853 .ok_or(format_err!("Magnetic group required for orbit construction, but no magnetic symmetry found."))?
854 } else {
855 &pd_res.unitary_symmetry
856 };
857 if use_double_group {
858 UnitaryRepresentedSymmetryGroup::from_molecular_symmetry(
859 sym,
860 infinite_order_to_finite,
861 )
862 .and_then(|grp| grp.to_double_group())
863 } else {
864 UnitaryRepresentedSymmetryGroup::from_molecular_symmetry(
865 sym,
866 infinite_order_to_finite,
867 )
868 }
869 })
870 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
871
872 let system = (&hamiltonian_ao, &overlap_ao);
874 let multidets_vec = system
875 .solve_symmetry_orbit_noci(
876 &origins_c.iter().collect_vec(),
877 &group,
878 symmetry_transformation_kind,
879 use_cayley_table,
880 thresh_offdiag,
881 thresh_zeroov,
882 )
883 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
884 let multidets = MultiDeterminants::from_multideterminant_vec(
885 &multidets_vec.iter().collect::<Vec<_>>(),
886 )
887 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
888
889 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
891 UnitaryRepresentedSymmetryGroup,
892 Complex<f64>,
893 _,
894 SpinConstraint,
895 >::builder()
896 .parameters(&mda_params)
897 .angular_function_parameters(&afa_params)
898 .multidets(&multidets)
899 .sao(&sao_c)
900 .sao_h(sao_h_c.as_ref())
901 .symmetry_group(&pd_res)
902 .build()
903 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
904 py.detach(|| {
905 mda_driver
906 .run()
907 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
908 })?;
909
910 let basis = multidets
912 .basis()
913 .iter()
914 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
915 .collect::<Result<Vec<_>, _>>()
916 .map_err(|_| {
917 PyRuntimeError::new_err(
918 "Unable to obtain the basis of Slater determinants."
919 .to_string(),
920 )
921 })?;
922 let (coefficientss, energies): (Vec<_>, Vec<_>) = multidets_vec
923 .iter()
924 .map(|multidet| {
925 let coefficients =
926 multidet.coefficients().iter().cloned().collect_vec();
927 let energy = *multidet.energy().unwrap_or(&Complex::from(f64::NAN));
928 Ok::<_, PyErr>((coefficients, energy))
929 })
930 .collect::<Result<Vec<_>, _>>()?
931 .into_iter()
932 .unzip();
933 let coefficientss_arr = Array2::from_shape_vec(
934 (basis.len(), coefficientss.len()).f(),
935 coefficientss.into_iter().flatten().collect_vec(),
936 )
937 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
938 .to_pyarray(py);
939 let energies_arr = Array1::from_vec(energies).to_pyarray(py);
940 let density_matrices = if calculate_density_matrices {
941 log::debug!("Calculating density matrices...");
942 let denmats_opt = multidets
943 .density_matrices(
944 &sao_c.view(),
945 thresh_offdiag,
946 thresh_zeroov,
947 true,
948 )
949 .map(|denmats| {
950 denmats
951 .axis_iter(Axis(0))
952 .map(|denmat| denmat.to_pyarray(py))
953 .collect_vec()
954 })
955 .ok();
956 log::debug!("Calculating density matrices... Done.");
957 denmats_opt
958 } else {
959 None
960 };
961 let pymultidet = PyMultiDeterminantsComplex::new(
962 basis,
963 coefficientss_arr,
964 energies_arr,
965 density_matrices,
966 multidets.threshold(),
967 )
968 .into_py_any(py)?;
969 Ok(pymultidet)
970 }
971 }
972 }
973 PyStructureConstraint::SpinOrbitCoupled(_) => {
974 let overlap_ao = OverlapAO::<Complex<f64>, SpinOrbitCoupled>::builder()
975 .sao(sao_c.view())
976 .build()
977 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
978 let hamiltonian_ao = HamiltonianAO::<Complex<f64>, SpinOrbitCoupled, _>::builder()
979 .enuc(enuc_c)
980 .onee(onee_c.view())
981 .twoe(twoe_c.as_ref().map(|m| m.view()))
982 .get_jk(get_jk_c.as_ref())
983 .build()
984 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
985
986 let origins_c = pyorigins
987 .iter()
988 .map(|pydet| match pydet {
989 PySlaterDeterminant::Real(pydet_r) => pydet_r
990 .to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
991 .map(|det_r| det_r.into()),
992 PySlaterDeterminant::Complex(pydet_c) => {
993 pydet_c.to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
994 }
995 })
996 .collect::<Result<Vec<_>, _>>()
997 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
998
999 match &use_magnetic_group {
1000 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
1001 let group = py
1003 .detach(|| {
1004 let magsym = pd_res
1005 .magnetic_symmetry
1006 .as_ref()
1007 .ok_or(format_err!("Magnetic group required for orbit construction, but no magnetic symmetry found."))?;
1008 if use_double_group {
1009 MagneticRepresentedSymmetryGroup::from_molecular_symmetry(
1010 magsym,
1011 infinite_order_to_finite,
1012 )
1013 .and_then(|grp| grp.to_double_group())
1014 } else {
1015 MagneticRepresentedSymmetryGroup::from_molecular_symmetry(
1016 magsym,
1017 infinite_order_to_finite,
1018 )
1019 }
1020 })
1021 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1022
1023 let system = (&hamiltonian_ao, &overlap_ao);
1025 let multidets_vec = system
1026 .solve_symmetry_orbit_noci(
1027 &origins_c.iter().collect_vec(),
1028 &group,
1029 symmetry_transformation_kind,
1030 use_cayley_table,
1031 thresh_offdiag,
1032 thresh_zeroov,
1033 )
1034 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1035 let multidets = MultiDeterminants::from_multideterminant_vec(
1036 &multidets_vec.iter().collect::<Vec<_>>(),
1037 )
1038 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1039
1040 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
1042 MagneticRepresentedSymmetryGroup,
1043 Complex<f64>,
1044 _,
1045 SpinOrbitCoupled,
1046 >::builder()
1047 .parameters(&mda_params)
1048 .angular_function_parameters(&afa_params)
1049 .multidets(&multidets)
1050 .sao(&sao_c)
1051 .sao_h(sao_h_c.as_ref())
1052 .symmetry_group(&pd_res)
1053 .build()
1054 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1055 py.detach(|| {
1056 mda_driver
1057 .run()
1058 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
1059 })?;
1060
1061 let basis = multidets
1063 .basis()
1064 .iter()
1065 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
1066 .collect::<Result<Vec<_>, _>>()
1067 .map_err(|_| {
1068 PyRuntimeError::new_err(
1069 "Unable to obtain the basis of Slater determinants."
1070 .to_string(),
1071 )
1072 })?;
1073 let (coefficientss, energies): (Vec<_>, Vec<_>) = multidets_vec
1074 .iter()
1075 .map(|multidet| {
1076 let coefficients =
1077 multidet.coefficients().iter().cloned().collect_vec();
1078 let energy = *multidet.energy().unwrap_or(&Complex::from(f64::NAN));
1079 Ok::<_, PyErr>((coefficients, energy))
1080 })
1081 .collect::<Result<Vec<_>, _>>()?
1082 .into_iter()
1083 .unzip();
1084 let coefficientss_arr = Array2::from_shape_vec(
1085 (basis.len(), coefficientss.len()).f(),
1086 coefficientss.into_iter().flatten().collect_vec(),
1087 )
1088 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
1089 .to_pyarray(py);
1090 let energies_arr = Array1::from_vec(energies).to_pyarray(py);
1091 let density_matrices = if calculate_density_matrices {
1092 log::debug!("Calculating density matrices...");
1093 let denmats_opt = multidets
1094 .density_matrices(
1095 &sao_c.view(),
1096 thresh_offdiag,
1097 thresh_zeroov,
1098 true,
1099 )
1100 .map(|denmats| {
1101 denmats
1102 .axis_iter(Axis(0))
1103 .map(|denmat| denmat.to_pyarray(py))
1104 .collect_vec()
1105 })
1106 .ok();
1107 log::debug!("Calculating density matrices... Done.");
1108 denmats_opt
1109 } else {
1110 None
1111 };
1112 let pymultidet = PyMultiDeterminantsComplex::new(
1113 basis,
1114 coefficientss_arr,
1115 energies_arr,
1116 density_matrices,
1117 multidets.threshold(),
1118 )
1119 .into_py_any(py)?;
1120 Ok(pymultidet)
1121 }
1122 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
1123 let group = py
1125 .detach(|| {
1126 let sym = if use_magnetic_group.is_some() {
1127 pd_res
1128 .magnetic_symmetry
1129 .as_ref()
1130 .ok_or(format_err!("Magnetic group required for orbit construction, but no magnetic symmetry found."))?
1131 } else {
1132 &pd_res.unitary_symmetry
1133 };
1134 if use_double_group {
1135 UnitaryRepresentedSymmetryGroup::from_molecular_symmetry(
1136 sym,
1137 infinite_order_to_finite,
1138 )
1139 .and_then(|grp| grp.to_double_group())
1140 } else {
1141 UnitaryRepresentedSymmetryGroup::from_molecular_symmetry(
1142 sym,
1143 infinite_order_to_finite,
1144 )
1145 }
1146 })
1147 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1148
1149 let system = (&hamiltonian_ao, &overlap_ao);
1151 let multidets_vec = system
1152 .solve_symmetry_orbit_noci(
1153 &origins_c.iter().collect_vec(),
1154 &group,
1155 symmetry_transformation_kind,
1156 use_cayley_table,
1157 thresh_offdiag,
1158 thresh_zeroov,
1159 )
1160 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1161 let multidets = MultiDeterminants::from_multideterminant_vec(
1162 &multidets_vec.iter().collect::<Vec<_>>(),
1163 )
1164 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1165
1166 let mut mda_driver = MultiDeterminantRepAnalysisDriver::<
1168 UnitaryRepresentedSymmetryGroup,
1169 Complex<f64>,
1170 _,
1171 SpinOrbitCoupled,
1172 >::builder()
1173 .parameters(&mda_params)
1174 .angular_function_parameters(&afa_params)
1175 .multidets(&multidets)
1176 .sao(&sao_c)
1177 .sao_h(sao_h_c.as_ref())
1178 .symmetry_group(&pd_res)
1179 .build()
1180 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
1181 py.detach(|| {
1182 mda_driver
1183 .run()
1184 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
1185 })?;
1186
1187 let basis = multidets
1189 .basis()
1190 .iter()
1191 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
1192 .collect::<Result<Vec<_>, _>>()
1193 .map_err(|_| {
1194 PyRuntimeError::new_err(
1195 "Unable to obtain the basis of Slater determinants."
1196 .to_string(),
1197 )
1198 })?;
1199 let (coefficientss, energies): (Vec<_>, Vec<_>) = multidets_vec
1200 .iter()
1201 .map(|multidet| {
1202 let coefficients =
1203 multidet.coefficients().iter().cloned().collect_vec();
1204 let energy = *multidet.energy().unwrap_or(&Complex::from(f64::NAN));
1205 Ok::<_, PyErr>((coefficients, energy))
1206 })
1207 .collect::<Result<Vec<_>, _>>()?
1208 .into_iter()
1209 .unzip();
1210 let coefficientss_arr = Array2::from_shape_vec(
1211 (basis.len(), coefficientss.len()).f(),
1212 coefficientss.into_iter().flatten().collect_vec(),
1213 )
1214 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
1215 .to_pyarray(py);
1216 let energies_arr = Array1::from_vec(energies).to_pyarray(py);
1217 let density_matrices = if calculate_density_matrices {
1218 log::debug!("Calculating density matrices...");
1219 let denmats_opt = multidets
1220 .density_matrices(
1221 &sao_c.view(),
1222 thresh_offdiag,
1223 thresh_zeroov,
1224 true,
1225 )
1226 .map(|denmats| {
1227 denmats
1228 .axis_iter(Axis(0))
1229 .map(|denmat| denmat.to_pyarray(py))
1230 .collect_vec()
1231 })
1232 .ok();
1233 log::debug!("Calculating density matrices... Done.");
1234 denmats_opt
1235 } else {
1236 None
1237 };
1238 let pymultidet = PyMultiDeterminantsComplex::new(
1239 basis,
1240 coefficientss_arr,
1241 energies_arr,
1242 density_matrices,
1243 multidets.threshold(),
1244 )
1245 .into_py_any(py)?;
1246 Ok(pymultidet)
1247 }
1248 }
1249 }
1250 }
1251 }
1252}