1use std::collections::HashSet;
4use std::path::PathBuf;
5
6use anyhow::format_err;
7use itertools::Itertools;
8use ndarray::{Array2, Axis, ShapeBuilder};
9use num_complex::Complex;
10use numpy::{PyArrayMethods, ToPyArray};
11use pyo3::exceptions::{PyIOError, PyRuntimeError};
12use pyo3::types::PyFunction;
13use pyo3::{IntoPyObjectExt, prelude::*};
14
15use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled};
16use crate::bindings::python::integrals::{PyBasisAngularOrder, PyStructureConstraint};
17use crate::bindings::python::projection::PyProjectionTarget;
18use crate::bindings::python::representation_analysis::multideterminant::{
19 PyMultiDeterminantsComplex, PyMultiDeterminantsReal,
20};
21use crate::bindings::python::representation_analysis::slater_determinant::PySlaterDeterminant;
22use crate::bindings::python::representation_analysis::{PyArray2RC, PyArray4RC, PyScalarRC};
23use crate::drivers::QSym2Driver;
24use crate::drivers::projection::slater_determinant::{
25 SlaterDeterminantProjectionDriver, SlaterDeterminantProjectionParams,
26};
27use crate::drivers::representation_analysis::{
28 CharacterTableDisplay, MagneticSymmetryAnalysisKind,
29};
30use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
31use crate::io::format::qsym2_output;
32use crate::io::{QSym2FileType, read_qsym2_binary};
33use crate::symmetry::symmetry_group::UnitaryRepresentedSymmetryGroup;
34use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
35use crate::target::noci::basis::Basis;
36use crate::target::noci::multideterminants::MultiDeterminants;
37
38type C128 = Complex<f64>;
39
40#[allow(clippy::too_many_arguments)]
108#[pyfunction]
109#[pyo3(signature = (
110 inp_sym,
111 pydet,
112 projection_targets,
113 calculate_energies,
114 calculate_density_matrices,
115 pybaos,
116 use_magnetic_group,
117 use_double_group,
118 symmetry_transformation_kind,
119 write_character_table=true,
120 infinite_order_to_finite=None,
121 sao=None,
122 sao_h=None,
123 enuc=None,
124 onee=None,
125 twoe=None,
126 py_get_jk=None,
127 thresh_offdiag=None,
128 thresh_zeroov=None,
129))]
130pub fn project_slater_determinant(
131 py: Python<'_>,
132 inp_sym: PathBuf,
133 pydet: PySlaterDeterminant,
134 projection_targets: Vec<PyProjectionTarget>,
135 calculate_energies: bool,
136 calculate_density_matrices: bool,
137 pybaos: Vec<PyBasisAngularOrder>,
138 use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
139 use_double_group: bool,
140 symmetry_transformation_kind: SymmetryTransformationKind,
141 write_character_table: bool,
142 infinite_order_to_finite: Option<u32>,
143 sao: Option<PyArray2RC>,
144 sao_h: Option<PyArray2RC>,
145 enuc: Option<PyScalarRC>,
146 onee: Option<PyArray2RC>,
147 twoe: Option<PyArray4RC>,
148 py_get_jk: Option<Py<PyFunction>>,
149 thresh_offdiag: Option<f64>,
150 thresh_zeroov: Option<f64>,
151) -> PyResult<(Vec<String>, Py<PyAny>)> {
152 let pd_res: SymmetryGroupDetectionResult =
153 read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
154 .map_err(|err| PyIOError::new_err(err.to_string()))?;
155
156 let mut file_name = inp_sym.to_path_buf();
157 file_name.set_extension(QSym2FileType::Sym.ext());
158 qsym2_output!(
159 "Symmetry-group detection results read in from {}.",
160 file_name.display(),
161 );
162 qsym2_output!("");
163
164 let mol = &pd_res.pre_symmetry.recentred_molecule;
165 let baos = pybaos
166 .iter()
167 .map(|bao| {
168 bao.to_qsym2(mol)
169 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
170 })
171 .collect::<Result<Vec<_>, _>>()?;
172 let baos_ref = baos.iter().collect::<Vec<_>>();
173 let augment_to_generalised = match symmetry_transformation_kind {
174 SymmetryTransformationKind::SpatialWithSpinTimeReversal
175 | SymmetryTransformationKind::Spin
176 | SymmetryTransformationKind::SpinSpatial => true,
177 SymmetryTransformationKind::Spatial => false,
178 };
179
180 let symbolic_projection_targets = projection_targets
181 .iter()
182 .filter_map(|pt| match pt {
183 PyProjectionTarget::Symbolic(symbolic_pt) => Some(symbolic_pt.clone()),
184 PyProjectionTarget::Numeric(_) => None,
185 })
186 .collect_vec();
187 let numeric_projection_targets = projection_targets
188 .iter()
189 .filter_map(|pt| match pt {
190 PyProjectionTarget::Symbolic(_) => None,
191 PyProjectionTarget::Numeric(numeric_pt) => Some(*numeric_pt),
192 })
193 .collect_vec();
194
195 let sdp_params = SlaterDeterminantProjectionParams::builder()
196 .symbolic_projection_targets(Some(symbolic_projection_targets))
197 .numeric_projection_targets(Some(numeric_projection_targets))
198 .use_magnetic_group(use_magnetic_group.clone())
199 .use_double_group(use_double_group)
200 .symmetry_transformation_kind(symmetry_transformation_kind)
201 .write_character_table(if write_character_table {
202 Some(CharacterTableDisplay::Symbolic)
203 } else {
204 None
205 })
206 .infinite_order_to_finite(infinite_order_to_finite)
207 .build()
208 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
209
210 match pydet {
230 PySlaterDeterminant::Real(pydet_r) => {
231 if matches!(
232 pydet_r.structure_constraint,
233 PyStructureConstraint::SpinOrbitCoupled(_)
234 ) {
235 return Err(PyRuntimeError::new_err("Real determinants are not compatible with spin--orbit-coupled structure constraint.".to_string()));
236 }
237
238 let sao_opt = sao.and_then(|pysao| match pysao {
239 PyArray2RC::Real(pysao_r) => Some(pysao_r.to_owned_array()),
240 PyArray2RC::Complex(_) => None,
241 });
242 let sao_h_opt = sao_h.and_then(|pysao_h| match pysao_h {
243 PyArray2RC::Real(pysao_h_r) => Some(pysao_h_r.to_owned_array()),
244 PyArray2RC::Complex(_) => None,
245 });
246 let enuc_opt = enuc.and_then(|pyenuc| match pyenuc {
247 PyScalarRC::Real(pyenuc_r) => Some(pyenuc_r),
248 PyScalarRC::Complex(_) => None,
249 });
250 let onee_opt = onee.and_then(|pyonee| match pyonee {
251 PyArray2RC::Real(pyonee_r) => Some(pyonee_r.to_owned_array()),
252 PyArray2RC::Complex(_) => None,
253 });
254 let twoe_opt = twoe.and_then(|pytwoe| match pytwoe {
255 PyArray4RC::Real(pytwoe_r) => Some(pytwoe_r.to_owned_array()),
256 PyArray4RC::Complex(_) => None,
257 });
258 let get_jk_opt = py_get_jk.map(|py_get_jk_inner| {
259 crate::generate_get_jk!(get_jk_r, py_get_jk_inner, f64);
260 get_jk_r
261 });
262
263 let det_r = if augment_to_generalised {
264 pydet_r
265 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
266 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
267 .to_generalised()
268 } else {
269 pydet_r
270 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
271 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
272 };
273
274 let projected_sds = match &use_magnetic_group {
275 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
276 return Err(PyRuntimeError::new_err(
277 "Projection using corepresentations is not yet supported.",
278 ));
279 }
280 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
281 let mut sdp_driver = if calculate_energies {
282 SlaterDeterminantProjectionDriver::<
283 UnitaryRepresentedSymmetryGroup,
284 f64,
285 SpinConstraint,
286 _,
287 >::builder()
288 .parameters(&sdp_params)
289 .determinant(&det_r)
290 .symmetry_group(&pd_res)
291 .sao(sao_opt.as_ref())
292 .sao_h(sao_h_opt.as_ref())
293 .enuc(enuc_opt)
294 .onee(onee_opt.as_ref())
295 .twoe(twoe_opt.as_ref())
296 .get_jk(get_jk_opt)
297 .thresh_offdiag(thresh_offdiag)
298 .thresh_zeroov(thresh_zeroov)
299 .build()
300 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
301 } else {
302 SlaterDeterminantProjectionDriver::<
303 UnitaryRepresentedSymmetryGroup,
304 f64,
305 SpinConstraint,
306 _,
307 >::builder()
308 .parameters(&sdp_params)
309 .determinant(&det_r)
310 .symmetry_group(&pd_res)
311 .sao(sao_opt.as_ref())
312 .sao_h(sao_h_opt.as_ref())
313 .build()
314 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
315 };
316 py.detach(|| {
317 sdp_driver
318 .run()
319 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
320 })?;
321 sdp_driver
322 .result()
323 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
324 .projected_determinants()
325 .clone()
326 }
327 };
328 let multidets_vec = projected_sds
329 .values()
330 .map(|v| v.as_ref())
331 .collect::<Result<Vec<_>, _>>()
332 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
333 let multidets = MultiDeterminants::from_multideterminant_vec(
334 &multidets_vec.into_iter().collect::<Vec<_>>(),
335 )
336 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
337
338 let energies = multidets.energies();
339
340 let (rows, coefficientss): (Vec<_>, Vec<_>) = projected_sds
341 .iter()
342 .map(|(row, multidet_res)| {
343 let coefficients = multidet_res
344 .as_ref()
345 .map(|multidet| multidet.coefficients().iter().cloned().collect_vec())
346 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
347 Ok::<_, PyErr>((row.to_string(), coefficients))
348 })
349 .collect::<Result<Vec<_>, _>>()?
350 .into_iter()
351 .unzip();
352 let basis = projected_sds
353 .iter()
354 .next()
355 .and_then(|(_, multidet_res)| {
356 multidet_res.as_ref().ok().and_then(|multidet| {
357 multidet
358 .basis()
359 .iter()
360 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
361 .collect::<Result<Vec<_>, _>>()
362 .ok()
363 })
364 })
365 .ok_or_else(|| {
366 PyRuntimeError::new_err(
367 "Unable to obtain the basis of Slater determinants.".to_string(),
368 )
369 })?;
370 let coefficientss_arr = Array2::from_shape_vec(
371 (basis.len(), coefficientss.len()).f(),
372 coefficientss.into_iter().flatten().collect_vec(),
373 )
374 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
375 .to_pyarray(py);
376 let energies_arr = energies.mapv(|v| v.unwrap_or(f64::NAN)).to_pyarray(py);
377
378 let density_matrices = match (
379 calculate_density_matrices,
380 thresh_offdiag,
381 thresh_zeroov,
382 sao_opt.as_ref(),
383 ) {
384 (true, Some(thresh_offdiag), Some(thresh_zeroov), Some(sao)) => {
385 log::debug!("Calculating density matrices...");
386 let denmats_opt = multidets
387 .density_matrices(&sao.view(), thresh_offdiag, thresh_zeroov, true)
388 .map(|denmats| {
389 denmats
390 .axis_iter(Axis(0))
391 .map(|denmat| denmat.to_pyarray(py))
392 .collect_vec()
393 })
394 .ok();
395 log::debug!("Calculating density matrices... Done.");
396 denmats_opt
397 }
398 _ => None,
399 };
400
401 let pymultidet = PyMultiDeterminantsReal::new(
402 basis,
403 coefficientss_arr,
404 energies_arr,
405 density_matrices,
406 pydet_r.threshold,
407 )
408 .into_py_any(py)?;
409 Ok((rows, pymultidet))
410 }
411 PySlaterDeterminant::Complex(pydet_c) => {
412 let sao_opt = sao.map(|pysao| match pysao {
413 PyArray2RC::Real(pysao_r) => pysao_r.to_owned_array().mapv(Complex::from),
414 PyArray2RC::Complex(pysao_c) => pysao_c.to_owned_array(),
415 });
416 let sao_h_opt = sao_h.map(|pysao_h| match pysao_h {
417 PyArray2RC::Real(pysao_h_r) => pysao_h_r.to_owned_array().mapv(Complex::from),
418 PyArray2RC::Complex(pysao_h_c) => pysao_h_c.to_owned_array(),
419 });
420 let enuc_opt = enuc.map(|pyenuc| match pyenuc {
421 PyScalarRC::Real(pyenuc_r) => Complex::from(pyenuc_r),
422 PyScalarRC::Complex(pyenuc_c) => pyenuc_c,
423 });
424 let onee_opt = onee.map(|pyonee| match pyonee {
425 PyArray2RC::Real(pyonee_r) => pyonee_r.to_owned_array().mapv(Complex::from),
426 PyArray2RC::Complex(pyonee_c) => pyonee_c.to_owned_array(),
427 });
428 let twoe_opt = twoe.map(|pytwoe| match pytwoe {
429 PyArray4RC::Real(pytwoe_r) => pytwoe_r.to_owned_array().mapv(Complex::from),
430 PyArray4RC::Complex(pytwoe_c) => pytwoe_c.to_owned_array(),
431 });
432 let get_jk_opt = py_get_jk.map(|py_get_jk_inner| {
433 crate::generate_get_jk!(get_jk_c, py_get_jk_inner, Complex<f64>);
434 get_jk_c
435 });
436
437 match pydet_c.structure_constraint {
438 PyStructureConstraint::SpinConstraint(_) => {
439 let det_c = if augment_to_generalised {
440 pydet_c
441 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
442 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
443 .to_generalised()
444 } else {
445 pydet_c
446 .to_qsym2::<SpinConstraint>(&baos_ref, mol)
447 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
448 };
449
450 let projected_sds = match &use_magnetic_group {
451 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
452 return Err(PyRuntimeError::new_err(
453 "Projection using corepresentations is not yet supported.",
454 ));
455 }
456 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
457 let mut sdp_driver = if calculate_energies {
458 SlaterDeterminantProjectionDriver::<
459 UnitaryRepresentedSymmetryGroup,
460 C128,
461 SpinConstraint,
462 _,
463 >::builder()
464 .parameters(&sdp_params)
465 .determinant(&det_c)
466 .symmetry_group(&pd_res)
467 .sao(sao_opt.as_ref())
468 .sao_h(sao_h_opt.as_ref())
469 .enuc(enuc_opt)
470 .onee(onee_opt.as_ref())
471 .twoe(twoe_opt.as_ref())
472 .get_jk(get_jk_opt)
473 .thresh_offdiag(thresh_offdiag)
474 .thresh_zeroov(thresh_zeroov)
475 .build()
476 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
477 } else {
478 SlaterDeterminantProjectionDriver::<
479 UnitaryRepresentedSymmetryGroup,
480 C128,
481 SpinConstraint,
482 _,
483 >::builder()
484 .parameters(&sdp_params)
485 .determinant(&det_c)
486 .symmetry_group(&pd_res)
487 .sao(sao_opt.as_ref())
488 .sao_h(sao_h_opt.as_ref())
489 .build()
490 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
491 };
492 py.detach(|| {
493 sdp_driver
494 .run()
495 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
496 })?;
497 sdp_driver
498 .result()
499 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
500 .projected_determinants()
501 .clone()
502 }
503 };
504 let multidets_vec = projected_sds
505 .values()
506 .map(|v| v.as_ref())
507 .collect::<Result<Vec<_>, _>>()
508 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
509 let multidets = MultiDeterminants::from_multideterminant_vec(
510 &multidets_vec.into_iter().collect::<Vec<_>>(),
511 )
512 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
513
514 let energies = multidets.energies();
515
516 let (rows, coefficientss): (Vec<_>, Vec<_>) = projected_sds
517 .iter()
518 .map(|(row, multidet_res)| {
519 let coefficients = multidet_res
520 .as_ref()
521 .map(|multidet| {
522 multidet.coefficients().iter().cloned().collect_vec()
523 })
524 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
525 Ok::<_, PyErr>((row.to_string(), coefficients))
526 })
527 .collect::<Result<Vec<_>, _>>()?
528 .into_iter()
529 .unzip();
530 let basis = projected_sds
531 .iter()
532 .next()
533 .and_then(|(_, multidet_res)| {
534 multidet_res.as_ref().ok().and_then(|multidet| {
535 multidet
536 .basis()
537 .iter()
538 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
539 .collect::<Result<Vec<_>, _>>()
540 .ok()
541 })
542 })
543 .ok_or_else(|| {
544 PyRuntimeError::new_err(
545 "Unable to obtain the basis of Slater determinants.".to_string(),
546 )
547 })?;
548 let coefficientss_arr = Array2::from_shape_vec(
549 (basis.len(), coefficientss.len()).f(),
550 coefficientss.into_iter().flatten().collect_vec(),
551 )
552 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
553 .to_pyarray(py);
554 let energies_arr = energies
555 .mapv(|v| v.unwrap_or(Complex::from(f64::NAN)))
556 .to_pyarray(py);
557
558 let density_matrices = match (
559 calculate_density_matrices,
560 thresh_offdiag,
561 thresh_zeroov,
562 sao_opt.as_ref(),
563 ) {
564 (true, Some(thresh_offdiag), Some(thresh_zeroov), Some(sao)) => {
565 log::debug!("Calculating density matrices...");
566 let denmats_opt = multidets
567 .density_matrices(&sao.view(), thresh_offdiag, thresh_zeroov, true)
568 .map(|denmats| {
569 denmats
570 .axis_iter(Axis(0))
571 .map(|denmat| denmat.to_pyarray(py))
572 .collect_vec()
573 })
574 .ok();
575 log::debug!("Calculating density matrices... Done.");
576 denmats_opt
577 }
578 _ => None,
579 };
580
581 let pymultidet = PyMultiDeterminantsComplex::new(
582 basis,
583 coefficientss_arr,
584 energies_arr,
585 density_matrices,
586 pydet_c.threshold,
587 )
588 .into_py_any(py)?;
589 Ok((rows, pymultidet))
590 }
591 PyStructureConstraint::SpinOrbitCoupled(_) => {
592 let det_c = pydet_c
593 .to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
594 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
595
596 let projected_sds = match &use_magnetic_group {
597 Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
598 return Err(PyRuntimeError::new_err(
599 "Projection using corepresentations is not yet supported.",
600 ));
601 }
602 Some(MagneticSymmetryAnalysisKind::Representation) | None => {
603 let mut sdp_driver = if calculate_energies {
604 SlaterDeterminantProjectionDriver::<
605 UnitaryRepresentedSymmetryGroup,
606 C128,
607 SpinOrbitCoupled,
608 _,
609 >::builder()
610 .parameters(&sdp_params)
611 .determinant(&det_c)
612 .symmetry_group(&pd_res)
613 .sao(sao_opt.as_ref())
614 .sao_h(sao_h_opt.as_ref())
615 .enuc(enuc_opt)
616 .onee(onee_opt.as_ref())
617 .twoe(twoe_opt.as_ref())
618 .get_jk(get_jk_opt)
619 .thresh_offdiag(thresh_offdiag)
620 .thresh_zeroov(thresh_zeroov)
621 .build()
622 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
623 } else {
624 SlaterDeterminantProjectionDriver::<
625 UnitaryRepresentedSymmetryGroup,
626 C128,
627 SpinOrbitCoupled,
628 _,
629 >::builder()
630 .parameters(&sdp_params)
631 .determinant(&det_c)
632 .symmetry_group(&pd_res)
633 .sao(sao_opt.as_ref())
634 .sao_h(sao_h_opt.as_ref())
635 .build()
636 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
637 };
638 py.detach(|| {
639 sdp_driver
640 .run()
641 .map_err(|err| PyRuntimeError::new_err(err.to_string()))
642 })?;
643 sdp_driver
644 .result()
645 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
646 .projected_determinants()
647 .clone()
648 }
649 };
650 let multidets_vec = projected_sds
651 .values()
652 .map(|v| v.as_ref())
653 .collect::<Result<Vec<_>, _>>()
654 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
655 let multidets = MultiDeterminants::from_multideterminant_vec(
656 &multidets_vec.into_iter().collect::<Vec<_>>(),
657 )
658 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
659
660 let energies = multidets.energies();
661
662 let (rows, coefficientss): (Vec<_>, Vec<_>) = projected_sds
663 .iter()
664 .map(|(row, multidet_res)| {
665 let coefficients = multidet_res
666 .as_ref()
667 .map(|multidet| {
668 multidet.coefficients().iter().cloned().collect_vec()
669 })
670 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
671 Ok::<_, PyErr>((row.to_string(), coefficients))
672 })
673 .collect::<Result<Vec<_>, _>>()?
674 .into_iter()
675 .unzip();
676 let basis = projected_sds
677 .iter()
678 .next()
679 .and_then(|(_, multidet_res)| {
680 multidet_res.as_ref().ok().and_then(|multidet| {
681 multidet
682 .basis()
683 .iter()
684 .map(|det_res| det_res.and_then(|det| det.to_python(py)))
685 .collect::<Result<Vec<_>, _>>()
686 .ok()
687 })
688 })
689 .ok_or_else(|| {
690 PyRuntimeError::new_err(
691 "Unable to obtain the basis of Slater determinants.".to_string(),
692 )
693 })?;
694 let coefficientss_arr = Array2::from_shape_vec(
695 (basis.len(), coefficientss.len()).f(),
696 coefficientss.into_iter().flatten().collect_vec(),
697 )
698 .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
699 .to_pyarray(py);
700 let energies_arr = energies
701 .mapv(|v| v.unwrap_or(Complex::from(f64::NAN)))
702 .to_pyarray(py);
703
704 let density_matrices = match (
705 calculate_density_matrices,
706 thresh_offdiag,
707 thresh_zeroov,
708 sao_opt.as_ref(),
709 ) {
710 (true, Some(thresh_offdiag), Some(thresh_zeroov), Some(sao)) => {
711 log::debug!("Calculating density matrices...");
712 let denmats_opt = multidets
713 .density_matrices(&sao.view(), thresh_offdiag, thresh_zeroov, true)
714 .map(|denmats| {
715 denmats
716 .axis_iter(Axis(0))
717 .map(|denmat| denmat.to_pyarray(py))
718 .collect_vec()
719 })
720 .ok();
721 log::debug!("Calculating density matrices... Done.");
722 denmats_opt
723 }
724 _ => None,
725 };
726
727 let pymultidet = PyMultiDeterminantsComplex::new(
728 basis,
729 coefficientss_arr,
730 energies_arr,
731 density_matrices,
732 pydet_c.threshold,
733 )
734 .into_py_any(py)?;
735 Ok((rows, pymultidet))
736 }
737 }
738 }
739 }
740}