@@ -163,6 +163,76 @@ def build_minimal_csf_basis(
163163
164164 return min_basis , transposed_basis
165165
166+ def build_minimal_csf_basis_singlet (
167+ configs : List [Tuple [int , ...]],
168+ active_space : List [int ],
169+ nelec : int ,
170+ max_unpaired : int
171+ ) -> Tuple [List [Tuple [Any , ...]], List [Tuple [Any , ...]]]:
172+ """
173+ Construct a minimal CSF (Configuration State Function) basis
174+ from raw MOPAC/Libra configurations.
175+
176+ Parameters
177+ ----------
178+ configs : list[tuple[int]]
179+ Configurations extracted from Libra or MOPAC output (raw orbital numbers).
180+ active_space : list[int]
181+ List of active orbital numbers defining the active space.
182+ nelec : int
183+ Total number of electrons.
184+ max_unpaired : int
185+ Twice the target spin projection (2*Ms).
186+ For example, 0 corresponds to singlet-only configurations.
187+
188+ Returns
189+ -------
190+ min_basis : list[tuple]
191+ List of matching determinants (CSFs) as (configuration, phase) tuples.
192+ transposed_basis : list[tuple]
193+ Transposed representation, i.e. `list(zip(*min_basis))`.
194+ Each element groups together all configurations or all phases across the basis.
195+
196+ Notes
197+ -----
198+ - Each entry in `min_basis` corresponds to a determinant that matches
199+ one of the given MOPAC configurations, within the spin constraint `max_unpaired`.
200+ - `transposed_basis` is convenient for splitting the basis into
201+ separate lists of determinants and phases.
202+ - If no matches are found, `transposed_basis` is returned as an empty list.
203+
204+ Example
205+ -------
206+ >>> import numpy as np
207+ >>> # Example input from MOPAC/Libra
208+ >>> configs0_raw = [(6, -6, 7, -7, 9, -8)]
209+ >>> active_space = [6, 7, 8, 9, 10, 11]
210+ >>> nelec = 6
211+ >>> max_unpaired = 0 # singlet configurations only
212+ >>>
213+ >>> min_basis, (all_confs, all_phases) = build_minimal_csf_basis(
214+ ... configs0_raw, active_space, nelec, max_unpaired
215+ ... )
216+ >>>
217+ >>> print(len(min_basis))
218+ 19
219+ >>> print(all_confs[0])
220+ (6, -6, 7, -7, 8, -8)
221+ >>> print(all_phases[0])
222+ 1
223+ """
224+
225+ # Generate all possible determinants (with parity) for the given active space
226+ # dets: List[Tuple[Any, ...]] = list(sd.generate_determinants_with_parity(active_space, nelec))
227+
228+ min_basis : List [Tuple [Any , ...]] = []
229+ for det , phase in sd .generate_single_excitations (active_space , nelec ):
230+ min_basis .append ((det , phase ))
231+
232+ # Avoid error when basis is empty
233+ transposed_basis : List [Tuple [Any , ...]] = list (zip (* min_basis )) if min_basis else []
234+
235+ return min_basis , transposed_basis
166236
167237
168238
@@ -392,3 +462,72 @@ def configs_and_T_matrix(
392462 return mapped_basis , T
393463
394464
465+ def configs_and_T_matrix_singlet (
466+ configs0_raw : List [Tuple [int , ...]],
467+ active_space : List [int ],
468+ orbital_space : List [int ],
469+ nelec : int ,
470+ S : int ,
471+ Ms : int
472+ ) -> Tuple [List [Tuple [int , ...]], "CMATRIX" ]:
473+ """
474+ Generate the minimal active-space configurations mapped to a given orbital space
475+ and the configuration-to-CSF transformation matrix for a CAS with given spin.
476+
477+ Parameters
478+ ----------
479+ configs0_raw : list[tuple[int]]
480+ List of raw configurations from Libra/MOPAC (signed orbital indices).
481+ active_space : list[int]
482+ Orbitals defining the active space used to generate the minimal determinant basis.
483+ orbital_space : list[int]
484+ Orbital indices used for mapping configurations (output will be relative to this space).
485+ nelec : int
486+ Number of active electrons.
487+ S : int
488+ Total spin quantum number.
489+ Ms : int
490+ Spin projection quantum number.
491+
492+ Returns
493+ -------
494+ mapped_basis : list[tuple[int, ...]]
495+ List of minimal configurations mapped to the specified `orbital_space`,
496+ with signs preserved (positive = α-spin, negative = β-spin).
497+ T : CMATRIX
498+ Complex-valued configuration-to-CSF transformation matrix.
499+
500+ Example
501+ -------
502+ >>> # Build configurations and T matrix for singlet CAS
503+ >>> mapped_basis, T = configs_and_T_matrix(
504+ ... configs0_raw,
505+ ... active_space=[6, 7, 8, 9, 10, 11],
506+ ... orbital_space=[2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
507+ ... nelec=6,
508+ ... S=0,
509+ ... Ms=0
510+ ... )
511+ >>> # mapped_basis shows minimal determinants mapped to orbital_space indices
512+ >>> print(mapped_basis[:5])
513+ [(5, -5, 6, -6, 7, -7),
514+ (5, -5, 6, -6, 7, -8),
515+ (5, -5, 6, -6, -7, 8),
516+ (5, -5, 6, -6, 7, -9),
517+ (5, -5, 6, -6, -7, 9)]
518+ >>> # T is the configuration-to-CSF transformation matrix
519+ >>> print("Shape of T:", T.num_of_rows, "x", T.num_of_cols)
520+ """
521+
522+ # 1. Build minimal SD basis with spin constraint 2*Ms
523+ min_basis , (all_confs , all_phases ) = build_minimal_csf_basis_singlet (
524+ configs0_raw , active_space , nelec , 2 * Ms
525+ )
526+
527+ # 2. Map configurations to the specified orbital space
528+ mapped_basis = map_to_active_indices (all_confs , orbital_space )
529+
530+ # 3. Compute configuration-to-CSF transformation matrix
531+ T = conf2csf_matrix (min_basis , all_confs , all_phases , S , Ms )
532+
533+ return mapped_basis , T
0 commit comments