qsym2/bindings/python/projection/
slater_determinant.rs

1//! Python bindings for QSym² symmetry projection of Slater determinants.
2
3use std::path::PathBuf;
4
5use itertools::Itertools;
6use ndarray::{Array1, Array2, ShapeBuilder};
7use num_complex::Complex;
8use numpy::{PyArrayMethods, ToPyArray};
9use pyo3::exceptions::{PyIOError, PyRuntimeError};
10use pyo3::{IntoPyObjectExt, prelude::*};
11
12use crate::analysis::Overlap;
13use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled};
14use crate::bindings::python::integrals::{PyBasisAngularOrder, PyStructureConstraint};
15use crate::bindings::python::projection::PyProjectionTarget;
16use crate::bindings::python::representation_analysis::PyArray2RC;
17use crate::bindings::python::representation_analysis::multideterminant::{
18    PyMultiDeterminantsComplex, PyMultiDeterminantsReal,
19};
20use crate::bindings::python::representation_analysis::slater_determinant::PySlaterDeterminant;
21use crate::drivers::QSym2Driver;
22use crate::drivers::projection::slater_determinant::{
23    SlaterDeterminantProjectionDriver, SlaterDeterminantProjectionParams,
24};
25use crate::drivers::representation_analysis::{
26    CharacterTableDisplay, MagneticSymmetryAnalysisKind,
27};
28use crate::drivers::symmetry_group_detection::SymmetryGroupDetectionResult;
29use crate::io::format::qsym2_output;
30use crate::io::{QSym2FileType, read_qsym2_binary};
31use crate::symmetry::symmetry_group::UnitaryRepresentedSymmetryGroup;
32use crate::symmetry::symmetry_transformation::SymmetryTransformationKind;
33use crate::target::noci::basis::Basis;
34
35type C128 = Complex<f64>;
36
37// =====================
38// Functions definitions
39// =====================
40
41/// Python-exposed function to perform symmetry projection for real and complex Slater determinants.
42///
43/// # Arguments
44///
45/// * `inp_sym` - A path to the [`QSym2FileType::Sym`] file containing the symmetry-group detection
46/// result for the system. This will be used to construct abstract groups and character tables for
47/// symmetry projection.
48/// * `pydet` - A Slater determinant whose coefficient matrices are of type `float64` or `complex128`.
49/// * `projection_targets` - A sequence of subspace labels for projection. Each label is either a
50/// symbolic string or a numerical index for the subspace in the character table of the prevailing
51/// group.
52/// * `density_matrix_calculation_thresholds` - An optional pair of thresholds for Löwdin pairing,
53/// one for checking zero off-diagonal values, one for checking zero overlaps, when computing
54/// multi-determinantal density matrices. If `None`, no density matrices will be computed.
55/// * `pybaos` - Python-exposed structures containing basis angular order information, one for each
56/// explicit component per coefficient matrix.
57/// * `use_magnetic_group` - An option indicating if the magnetic group is to be used for symmetry
58/// analysis, and if so, whether unitary representations or unitary-antiunitary corepresentations
59/// should be used.
60/// * `use_double_group` - A boolean indicating if the double group of the prevailing symmetry
61/// group is to be used for representation analysis instead.
62/// * `symmetry_transformation_kind` - An enumerated type indicating the type of symmetry
63/// transformations to be performed on the origin electron density to generate the orbit.
64/// * `write_character_table` - A boolean indicating if the character table of the prevailing
65/// symmetry group is to be printed out.
66/// * `infinite_order_to_finite` - The finite order with which infinite-order generators are to be
67/// interpreted to form a finite subgroup of the prevailing infinite group. This finite subgroup
68/// will be used for symmetry analysis.
69/// * `sao` - The optional atomic-orbital overlap matrix whose elements are of type `float64` or
70/// `complex128`. If this is not present, no squared norms of the resulting multi-determinants will
71/// be computed.
72/// * `sao_h` - The optional complex-symmetric atomic-orbital overlap matrix whose elements
73/// are of type `float64` or `complex128`. This is required if antiunitary symmetry operations are
74/// involved.
75///
76/// # Returns
77///
78/// The result will be returned as a tuple where the first item is a list of the labels of the
79/// subspaces used for projection, and the second item is an object containing the Slater
80/// determinant basis and the linear combination coefficients as a two-dimensional array with each
81/// column corresponding to one projected state.
82#[pyfunction]
83#[pyo3(signature = (
84    inp_sym,
85    pydet,
86    projection_targets,
87    density_matrix_calculation_thresholds,
88    pybaos,
89    use_magnetic_group,
90    use_double_group,
91    symmetry_transformation_kind,
92    write_character_table=true,
93    infinite_order_to_finite=None,
94    sao=None,
95    sao_h=None,
96))]
97pub fn project_slater_determinant(
98    py: Python<'_>,
99    inp_sym: PathBuf,
100    pydet: PySlaterDeterminant,
101    projection_targets: Vec<PyProjectionTarget>,
102    density_matrix_calculation_thresholds: Option<(f64, f64)>,
103    pybaos: Vec<PyBasisAngularOrder>,
104    use_magnetic_group: Option<MagneticSymmetryAnalysisKind>,
105    use_double_group: bool,
106    symmetry_transformation_kind: SymmetryTransformationKind,
107    write_character_table: bool,
108    infinite_order_to_finite: Option<u32>,
109    sao: Option<PyArray2RC>,
110    sao_h: Option<PyArray2RC>,
111) -> PyResult<(Vec<String>, Py<PyAny>)> {
112    let pd_res: SymmetryGroupDetectionResult =
113        read_qsym2_binary(inp_sym.clone(), QSym2FileType::Sym)
114            .map_err(|err| PyIOError::new_err(err.to_string()))?;
115
116    let mut file_name = inp_sym.to_path_buf();
117    file_name.set_extension(QSym2FileType::Sym.ext());
118    qsym2_output!(
119        "Symmetry-group detection results read in from {}.",
120        file_name.display(),
121    );
122    qsym2_output!("");
123
124    let mol = &pd_res.pre_symmetry.recentred_molecule;
125    let baos = pybaos
126        .iter()
127        .map(|bao| {
128            bao.to_qsym2(mol)
129                .map_err(|err| PyRuntimeError::new_err(err.to_string()))
130        })
131        .collect::<Result<Vec<_>, _>>()?;
132    let baos_ref = baos.iter().collect::<Vec<_>>();
133    let augment_to_generalised = match symmetry_transformation_kind {
134        SymmetryTransformationKind::SpatialWithSpinTimeReversal
135        | SymmetryTransformationKind::Spin
136        | SymmetryTransformationKind::SpinSpatial => true,
137        SymmetryTransformationKind::Spatial => false,
138    };
139
140    let symbolic_projection_targets = projection_targets
141        .iter()
142        .filter_map(|pt| match pt {
143            PyProjectionTarget::Symbolic(symbolic_pt) => Some(symbolic_pt.clone()),
144            PyProjectionTarget::Numeric(_) => None,
145        })
146        .collect_vec();
147    let numeric_projection_targets = projection_targets
148        .iter()
149        .filter_map(|pt| match pt {
150            PyProjectionTarget::Symbolic(_) => None,
151            PyProjectionTarget::Numeric(numeric_pt) => Some(*numeric_pt),
152        })
153        .collect_vec();
154
155    let sdp_params = SlaterDeterminantProjectionParams::builder()
156        .symbolic_projection_targets(Some(symbolic_projection_targets))
157        .numeric_projection_targets(Some(numeric_projection_targets))
158        .use_magnetic_group(use_magnetic_group.clone())
159        .use_double_group(use_double_group)
160        .symmetry_transformation_kind(symmetry_transformation_kind)
161        .write_character_table(if write_character_table {
162            Some(CharacterTableDisplay::Symbolic)
163        } else {
164            None
165        })
166        .infinite_order_to_finite(infinite_order_to_finite)
167        .build()
168        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
169
170    // Decision tree:
171    // - real determinant?
172    //   + yes:
173    //     - structure_constraint:
174    //       + SpinConstraint:
175    //         - use_magnetic_group:
176    //           + Some(Corepresentation)
177    //           + Some(Representation) | None
178    //       + SpinOrbitCoupled: not supported
179    //   - no:
180    //     - structure_constraint:
181    //       + SpinConstraint:
182    //         - use_magnetic_group:
183    //           + Some(Corepresentation)
184    //           + Some(Representation) | None
185    //       + SpinOrbitCoupled:
186    //         - use_magnetic_group:
187    //           + Some(Corepresentation)
188    //           + Some(Representation) | None
189    match pydet {
190        PySlaterDeterminant::Real(pydet_r) => {
191            if matches!(
192                pydet_r.structure_constraint,
193                PyStructureConstraint::SpinOrbitCoupled(_)
194            ) {
195                return Err(PyRuntimeError::new_err("Real determinants are not compatible with spin--orbit-coupled structure constraint.".to_string()));
196            }
197
198            let sao_opt = sao.and_then(|pysao| match pysao {
199                PyArray2RC::Real(pysao_r) => Some(pysao_r.to_owned_array()),
200                PyArray2RC::Complex(_) => None,
201            });
202            let sao_h_opt = sao_h.and_then(|pysao_h| match pysao_h {
203                PyArray2RC::Real(pysao_h_r) => Some(pysao_h_r.to_owned_array()),
204                PyArray2RC::Complex(_) => None,
205            });
206
207            let det_r = if augment_to_generalised {
208                pydet_r
209                    .to_qsym2::<SpinConstraint>(&baos_ref, mol)
210                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
211                    .to_generalised()
212            } else {
213                pydet_r
214                    .to_qsym2::<SpinConstraint>(&baos_ref, mol)
215                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
216            };
217
218            let projected_sds = match &use_magnetic_group {
219                Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
220                    return Err(PyRuntimeError::new_err(
221                        "Projection using corepresentations is not yet supported.",
222                    ));
223                }
224                Some(MagneticSymmetryAnalysisKind::Representation) | None => {
225                    let mut sdp_driver = SlaterDeterminantProjectionDriver::<
226                        UnitaryRepresentedSymmetryGroup,
227                        f64,
228                        _,
229                    >::builder()
230                    .parameters(&sdp_params)
231                    .determinant(&det_r)
232                    .symmetry_group(&pd_res)
233                    .sao(sao_opt.as_ref())
234                    .sao_h(sao_h_opt.as_ref())
235                    .build()
236                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
237                    py.detach(|| {
238                        sdp_driver
239                            .run()
240                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))
241                    })?;
242                    sdp_driver
243                        .result()
244                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
245                        .projected_determinants()
246                        .clone()
247                }
248            };
249            let (rows, (coefficientss, energies)): (Vec<_>, (Vec<_>, Vec<_>)) = projected_sds
250                .iter()
251                .map(|(row, multidet_res)| {
252                    let (coefficients, energy) = multidet_res
253                        .as_ref()
254                        .and_then(|multidet| {
255                            let coefficients =
256                                multidet.coefficients().iter().cloned().collect_vec();
257                            let energy = *multidet.energy().unwrap_or(&f64::NAN);
258                            Ok((coefficients, energy))
259                        })
260                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
261                    Ok::<_, PyErr>((row.to_string(), (coefficients, energy)))
262                })
263                .collect::<Result<Vec<_>, _>>()?
264                .into_iter()
265                .unzip();
266            let basis = projected_sds
267                .iter()
268                .next()
269                .and_then(|(_, multidet_res)| {
270                    multidet_res.as_ref().ok().and_then(|multidet| {
271                        multidet
272                            .basis()
273                            .iter()
274                            .map(|det_res| det_res.and_then(|det| det.to_python(py)))
275                            .collect::<Result<Vec<_>, _>>()
276                            .ok()
277                    })
278                })
279                .ok_or_else(|| {
280                    PyRuntimeError::new_err(
281                        "Unable to obtain the basis of Slater determinants.".to_string(),
282                    )
283                })?;
284            let coefficientss_arr = Array2::from_shape_vec(
285                (basis.len(), coefficientss.len()).f(),
286                coefficientss.into_iter().flatten().collect_vec(),
287            )
288            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
289            .to_pyarray(py);
290            let energies_arr = Array1::from_vec(energies).to_pyarray(py);
291            let density_matrices = match (density_matrix_calculation_thresholds, sao_opt.as_ref()) {
292                (Some((thresh_offdiag, thresh_zeroov)), Some(sao)) => Some(
293                    projected_sds
294                        .iter()
295                        .map(|(_, multidet_res)| {
296                            multidet_res
297                                .as_ref()
298                                .map_err(|err| PyRuntimeError::new_err(err.to_string()))
299                                .and_then(|multidet| {
300                                    multidet
301                                        .overlap(multidet, sao_opt.as_ref(), sao_h_opt.as_ref())
302                                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))
303                                        .and_then(|sq_norm| {
304                                            multidet
305                                                .density_matrix(
306                                                    &sao.view(),
307                                                    thresh_offdiag,
308                                                    thresh_zeroov,
309                                                )
310                                                .map_err(|err| {
311                                                    PyRuntimeError::new_err(err.to_string())
312                                                })
313                                                .map(|denmat| (denmat / sq_norm).to_pyarray(py))
314                                        })
315                                })
316                        })
317                        .collect::<Result<Vec<_>, _>>()?,
318                ),
319                _ => None,
320            };
321            let pymultidet = PyMultiDeterminantsReal::new(
322                basis,
323                coefficientss_arr,
324                energies_arr,
325                density_matrices,
326                pydet_r.threshold,
327            )
328            .into_py_any(py)?;
329            Ok((rows, pymultidet))
330        }
331        PySlaterDeterminant::Complex(pydet_c) => {
332            let sao_opt = sao.and_then(|pysao| match pysao {
333                PyArray2RC::Real(pysao_r) => Some(pysao_r.to_owned_array().mapv(Complex::from)),
334                PyArray2RC::Complex(pysao_c) => Some(pysao_c.to_owned_array()),
335            });
336            let sao_h_opt = sao_h.and_then(|pysao_h| match pysao_h {
337                PyArray2RC::Real(pysao_h_r) => Some(pysao_h_r.to_owned_array().mapv(Complex::from)),
338                PyArray2RC::Complex(pysao_h_c) => Some(pysao_h_c.to_owned_array()),
339            });
340
341            match pydet_c.structure_constraint {
342                PyStructureConstraint::SpinConstraint(_) => {
343                    let det_c = if augment_to_generalised {
344                        pydet_c
345                            .to_qsym2::<SpinConstraint>(&baos_ref, mol)
346                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
347                            .to_generalised()
348                    } else {
349                        pydet_c
350                            .to_qsym2::<SpinConstraint>(&baos_ref, mol)
351                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
352                    };
353
354                    let projected_sds = match &use_magnetic_group {
355                        Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
356                            return Err(PyRuntimeError::new_err(
357                                "Projection using corepresentations is not yet supported.",
358                            ));
359                        }
360                        Some(MagneticSymmetryAnalysisKind::Representation) | None => {
361                            let mut sdp_driver = SlaterDeterminantProjectionDriver::<
362                                UnitaryRepresentedSymmetryGroup,
363                                C128,
364                                _,
365                            >::builder()
366                            .parameters(&sdp_params)
367                            .determinant(&det_c)
368                            .symmetry_group(&pd_res)
369                            .sao(sao_opt.as_ref())
370                            .sao_h(sao_h_opt.as_ref())
371                            .build()
372                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
373                            py.detach(|| {
374                                sdp_driver
375                                    .run()
376                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))
377                            })?;
378                            sdp_driver
379                                .result()
380                                .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
381                                .projected_determinants()
382                                .clone()
383                        }
384                    };
385                    let (rows, (coefficientss, energies)): (Vec<_>, (Vec<_>, Vec<_>)) =
386                        projected_sds
387                            .iter()
388                            .map(|(row, multidet_res)| {
389                                let (coefficients, energy) = multidet_res
390                                    .as_ref()
391                                    .and_then(|multidet| {
392                                        let coefficients =
393                                            multidet.coefficients().iter().cloned().collect_vec();
394                                        let energy =
395                                            *multidet.energy().unwrap_or(&Complex::from(f64::NAN));
396                                        Ok((coefficients, energy))
397                                    })
398                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
399                                Ok::<_, PyErr>((row.to_string(), (coefficients, energy)))
400                            })
401                            .collect::<Result<Vec<_>, _>>()?
402                            .into_iter()
403                            .unzip();
404                    let basis = projected_sds
405                        .iter()
406                        .next()
407                        .and_then(|(_, multidet_res)| {
408                            multidet_res.as_ref().ok().and_then(|multidet| {
409                                multidet
410                                    .basis()
411                                    .iter()
412                                    .map(|det_res| det_res.and_then(|det| det.to_python(py)))
413                                    .collect::<Result<Vec<_>, _>>()
414                                    .ok()
415                            })
416                        })
417                        .ok_or_else(|| {
418                            PyRuntimeError::new_err(
419                                "Unable to obtain the basis of Slater determinants.".to_string(),
420                            )
421                        })?;
422                    let coefficientss_arr = Array2::from_shape_vec(
423                        (basis.len(), coefficientss.len()).f(),
424                        coefficientss.into_iter().flatten().collect_vec(),
425                    )
426                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
427                    .to_pyarray(py);
428                    let energies_arr = Array1::from_vec(energies).to_pyarray(py);
429                    let density_matrices =
430                        match (density_matrix_calculation_thresholds, sao_opt.as_ref()) {
431                            (Some((thresh_offdiag, thresh_zeroov)), Some(sao)) => Some(
432                                projected_sds
433                                    .iter()
434                                    .map(|(_, multidet_res)| {
435                                        multidet_res
436                                            .as_ref()
437                                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))
438                                            .and_then(|multidet| {
439                                                multidet
440                                                    .overlap(
441                                                        multidet,
442                                                        sao_opt.as_ref(),
443                                                        sao_h_opt.as_ref(),
444                                                    )
445                                                    .map_err(|err| {
446                                                        PyRuntimeError::new_err(err.to_string())
447                                                    })
448                                                    .and_then(|sq_norm| {
449                                                        multidet
450                                                            .density_matrix(
451                                                                &sao.view(),
452                                                                thresh_offdiag,
453                                                                thresh_zeroov,
454                                                            )
455                                                            .map_err(|err| {
456                                                                PyRuntimeError::new_err(
457                                                                    err.to_string(),
458                                                                )
459                                                            })
460                                                            .map(|denmat| {
461                                                                (denmat / sq_norm).to_pyarray(py)
462                                                            })
463                                                    })
464                                            })
465                                    })
466                                    .collect::<Result<Vec<_>, _>>()?,
467                            ),
468                            _ => None,
469                        };
470
471                    let pymultidet = PyMultiDeterminantsComplex::new(
472                        basis,
473                        coefficientss_arr,
474                        energies_arr,
475                        density_matrices,
476                        pydet_c.threshold,
477                    )
478                    .into_py_any(py)?;
479                    Ok((rows, pymultidet))
480                }
481                PyStructureConstraint::SpinOrbitCoupled(_) => {
482                    let det_c = pydet_c
483                        .to_qsym2::<SpinOrbitCoupled>(&baos_ref, mol)
484                        .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
485
486                    let projected_sds = match &use_magnetic_group {
487                        Some(MagneticSymmetryAnalysisKind::Corepresentation) => {
488                            return Err(PyRuntimeError::new_err(
489                                "Projection using corepresentations is not yet supported.",
490                            ));
491                        }
492                        Some(MagneticSymmetryAnalysisKind::Representation) | None => {
493                            let mut sdp_driver = SlaterDeterminantProjectionDriver::<
494                                UnitaryRepresentedSymmetryGroup,
495                                C128,
496                                _,
497                            >::builder()
498                            .parameters(&sdp_params)
499                            .determinant(&det_c)
500                            .symmetry_group(&pd_res)
501                            .sao(sao_opt.as_ref())
502                            .sao_h(sao_h_opt.as_ref())
503                            .build()
504                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
505                            py.detach(|| {
506                                sdp_driver
507                                    .run()
508                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))
509                            })?;
510                            sdp_driver
511                                .result()
512                                .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
513                                .projected_determinants()
514                                .clone()
515                        }
516                    };
517                    let (rows, (coefficientss, energies)): (Vec<_>, (Vec<_>, Vec<_>)) =
518                        projected_sds
519                            .iter()
520                            .map(|(row, multidet_res)| {
521                                let (coefficients, energy) = multidet_res
522                                    .as_ref()
523                                    .and_then(|multidet| {
524                                        let coefficients =
525                                            multidet.coefficients().iter().cloned().collect_vec();
526                                        let energy =
527                                            *multidet.energy().unwrap_or(&Complex::from(f64::NAN));
528                                        Ok((coefficients, energy))
529                                    })
530                                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?;
531                                Ok::<_, PyErr>((row.to_string(), (coefficients, energy)))
532                            })
533                            .collect::<Result<Vec<_>, _>>()?
534                            .into_iter()
535                            .unzip();
536                    let basis = projected_sds
537                        .iter()
538                        .next()
539                        .and_then(|(_, multidet_res)| {
540                            multidet_res.as_ref().ok().and_then(|multidet| {
541                                multidet
542                                    .basis()
543                                    .iter()
544                                    .map(|det_res| det_res.and_then(|det| det.to_python(py)))
545                                    .collect::<Result<Vec<_>, _>>()
546                                    .ok()
547                            })
548                        })
549                        .ok_or_else(|| {
550                            PyRuntimeError::new_err(
551                                "Unable to obtain the basis of Slater determinants.".to_string(),
552                            )
553                        })?;
554                    let coefficientss_arr = Array2::from_shape_vec(
555                        (basis.len(), coefficientss.len()).f(),
556                        coefficientss.into_iter().flatten().collect_vec(),
557                    )
558                    .map_err(|err| PyRuntimeError::new_err(err.to_string()))?
559                    .to_pyarray(py);
560                    let energies_arr = Array1::from_vec(energies).to_pyarray(py);
561                    let density_matrices =
562                        match (density_matrix_calculation_thresholds, sao_opt.as_ref()) {
563                            (Some((thresh_offdiag, thresh_zeroov)), Some(sao)) => Some(
564                                projected_sds
565                                    .iter()
566                                    .map(|(_, multidet_res)| {
567                                        multidet_res
568                                            .as_ref()
569                                            .map_err(|err| PyRuntimeError::new_err(err.to_string()))
570                                            .and_then(|multidet| {
571                                                multidet
572                                                    .overlap(
573                                                        multidet,
574                                                        sao_opt.as_ref(),
575                                                        sao_h_opt.as_ref(),
576                                                    )
577                                                    .map_err(|err| {
578                                                        PyRuntimeError::new_err(err.to_string())
579                                                    })
580                                                    .and_then(|sq_norm| {
581                                                        multidet
582                                                            .density_matrix(
583                                                                &sao.view(),
584                                                                thresh_offdiag,
585                                                                thresh_zeroov,
586                                                            )
587                                                            .map_err(|err| {
588                                                                PyRuntimeError::new_err(
589                                                                    err.to_string(),
590                                                                )
591                                                            })
592                                                            .map(|denmat| {
593                                                                (denmat / sq_norm).to_pyarray(py)
594                                                            })
595                                                    })
596                                            })
597                                    })
598                                    .collect::<Result<Vec<_>, _>>()?,
599                            ),
600                            _ => None,
601                        };
602
603                    let pymultidet = PyMultiDeterminantsComplex::new(
604                        basis,
605                        coefficientss_arr,
606                        energies_arr,
607                        density_matrices,
608                        pydet_c.threshold,
609                    )
610                    .into_py_any(py)?;
611                    Ok((rows, pymultidet))
612                }
613            }
614        }
615    }
616}