1use std::ops::Mul;
4
5use approx;
6use nalgebra::Vector3;
7use ndarray::{array, concatenate, s, Array2, Axis, LinalgScalar, ScalarOperand};
8use ndarray_linalg::types::Lapack;
9use num_complex::{Complex, ComplexFloat};
10
11use crate::angmom::spinor_rotation_3d::{SpinConstraint, SpinOrbitCoupled, StructureConstraint};
12use crate::permutation::{IntoPermutation, PermutableCollection, Permutation};
13use crate::symmetry::symmetry_element::symmetry_operation::SpecialSymmetryTransformation;
14use crate::symmetry::symmetry_element::{RotationGroup, SymmetryElement, SymmetryOperation, TRROT};
15use crate::symmetry::symmetry_element_order::ElementOrder;
16use crate::symmetry::symmetry_transformation::{
17 assemble_sh_rotation_3d_matrices, assemble_spinor_rotation_matrices, permute_array_by_atoms,
18 ComplexConjugationTransformable, DefaultTimeReversalTransformable, SpatialUnitaryTransformable,
19 SpinUnitaryTransformable, SymmetryTransformable, TimeReversalTransformable,
20 TransformationError,
21};
22use crate::target::orbital::MolecularOrbital;
23
24impl<'a, T> SpatialUnitaryTransformable for MolecularOrbital<'a, T, SpinConstraint>
32where
33 T: ComplexFloat + LinalgScalar + ScalarOperand + Copy + Lapack,
34 f64: Into<T>,
35{
36 fn transform_spatial_mut(
37 &mut self,
38 rmat: &Array2<f64>,
39 perm: Option<&Permutation<usize>>,
40 ) -> Result<&mut Self, TransformationError> {
41 let tmats: Vec<Array2<T>> = assemble_sh_rotation_3d_matrices(self.bao, rmat, perm)
42 .map_err(|err| TransformationError(err.to_string()))?
43 .iter()
44 .map(|tmat| tmat.map(|&x| x.into()))
45 .collect();
46 let pbao = if let Some(p) = perm {
47 self.bao
48 .permute(p)
49 .map_err(|err| TransformationError(err.to_string()))?
50 } else {
51 self.bao.clone()
52 };
53 let old_coeff = &self.coefficients;
54 let new_coefficients = match self.structure_constraint {
55 SpinConstraint::Restricted(_) | SpinConstraint::Unrestricted(_, _) => {
56 let p_coeff = if let Some(p) = perm {
57 permute_array_by_atoms(old_coeff, p, &[Axis(0)], self.bao)
58 } else {
59 old_coeff.clone()
60 };
61 let t_p_blocks = pbao
62 .shell_boundary_indices()
63 .into_iter()
64 .zip(tmats.iter())
65 .map(|((shl_start, shl_end), tmat)| {
66 tmat.dot(&p_coeff.slice(s![shl_start..shl_end]))
67 })
68 .collect::<Vec<_>>();
69 concatenate(
70 Axis(0),
71 &t_p_blocks
72 .iter()
73 .map(|t_p_block| t_p_block.view())
74 .collect::<Vec<_>>(),
75 )
76 .expect("Unable to concatenate the transformed rows for the various shells.")
77 }
78 SpinConstraint::Generalised(nspins, _) => {
79 let nspatial = self.bao.n_funcs();
80 let t_p_spin_blocks =
81 (0..nspins)
82 .map(|ispin| {
83 let spin_start = usize::from(ispin) * nspatial;
85 let spin_end = (usize::from(ispin) + 1) * nspatial;
86 let spin_block = old_coeff.slice(s![spin_start..spin_end]).to_owned();
87
88 let p_spin_block = if let Some(p) = perm {
90 permute_array_by_atoms(&spin_block, p, &[Axis(0)], self.bao)
91 } else {
92 spin_block
93 };
94
95 let t_p_blocks = pbao
97 .shell_boundary_indices()
98 .into_iter()
99 .zip(tmats.iter())
100 .map(|((shl_start, shl_end), tmat)| {
101 tmat.dot(&p_spin_block.slice(s![shl_start..shl_end]))
102 })
103 .collect::<Vec<_>>();
104
105 concatenate(
107 Axis(0),
108 &t_p_blocks.iter().map(|t_p_block| t_p_block.view()).collect::<Vec<_>>(),
109 )
110 .expect("Unable to concatenate the transformed rows for the various shells.")
111 })
112 .collect::<Vec<_>>();
113
114 concatenate(
116 Axis(0),
117 &t_p_spin_blocks
118 .iter()
119 .map(|t_p_spin_block| t_p_spin_block.view())
120 .collect::<Vec<_>>(),
121 )
122 .expect("Unable to concatenate the transformed spin blocks.")
123 }
124 };
125 self.coefficients = new_coefficients;
126 Ok(self)
127 }
128}
129
130impl<'a> SpinUnitaryTransformable for MolecularOrbital<'a, f64, SpinConstraint> {
139 fn transform_spin_mut(
140 &mut self,
141 dmat: &Array2<Complex<f64>>,
142 ) -> Result<&mut Self, TransformationError> {
143 let cdmat = dmat.view().split_complex();
144 if approx::relative_ne!(
145 cdmat.im.map(|x| x.powi(2)).sum().sqrt(),
146 0.0,
147 epsilon = 1e-14,
148 max_relative = 1e-14,
149 ) {
150 log::error!("Spin transformation matrix is complex-valued:\n{dmat}");
151 Err(TransformationError(
152 "Complex spin transformations cannot be performed with real coefficients."
153 .to_string(),
154 ))
155 } else {
156 let rdmat = cdmat.re.to_owned();
157 match self.structure_constraint {
158 SpinConstraint::Restricted(_) => {
159 if approx::relative_eq!(
160 (&rdmat - Array2::<f64>::eye(2))
161 .map(|x| x.abs().powi(2))
162 .sum()
163 .sqrt(),
164 0.0,
165 epsilon = 1e-14,
166 max_relative = 1e-14,
167 ) {
168 Ok(self)
170 } else if approx::relative_eq!(
171 (&rdmat + Array2::<f64>::eye(2))
172 .map(|x| x.abs().powi(2))
173 .sum()
174 .sqrt(),
175 0.0,
176 epsilon = 1e-14,
177 max_relative = 1e-14,
178 ) {
179 self.coefficients *= -1.0;
181 Ok(self)
182 } else {
183 log::error!("Unsupported spin transformation matrix:\n{}", &rdmat);
184 Err(TransformationError(
185 "Only the identity or negative identity spin transformations are possible with restricted spin constraint."
186 .to_string(),
187 ))
188 }
189 }
190 SpinConstraint::Unrestricted(nspins, increasingm) => {
191 if nspins != 2 {
194 return Err(TransformationError(
195 "Only two-component spinor transformations are supported for now."
196 .to_string(),
197 ));
198 }
199 let dmat_y = array![[0.0, -1.0], [1.0, 0.0]];
200 if approx::relative_eq!(
201 (&rdmat - Array2::<f64>::eye(2))
202 .map(|x| x.abs().powi(2))
203 .sum()
204 .sqrt(),
205 0.0,
206 epsilon = 1e-14,
207 max_relative = 1e-14,
208 ) {
209 Ok(self)
211 } else if approx::relative_eq!(
212 (&rdmat + Array2::<f64>::eye(2))
213 .map(|x| x.abs().powi(2))
214 .sum()
215 .sqrt(),
216 0.0,
217 epsilon = 1e-14,
218 max_relative = 1e-14,
219 ) {
220 self.coefficients *= -1.0;
222 Ok(self)
223 } else if approx::relative_eq!(
224 (&rdmat - &dmat_y).map(|x| x.abs().powi(2)).sum().sqrt(),
225 0.0,
226 epsilon = 1e-14,
227 max_relative = 1e-14,
228 ) {
229 if increasingm {
231 if self.component_index == 0 {
232 self.component_index = 1;
233 self.coefficients *= -1.0;
234 } else {
235 assert_eq!(self.component_index, 1);
236 self.component_index = 0;
237 }
238 } else if self.component_index == 0 {
239 self.component_index = 1;
240 } else {
241 assert_eq!(self.component_index, 1);
242 self.component_index = 0;
243 self.coefficients *= -1.0;
244 }
245 Ok(self)
246 } else if approx::relative_eq!(
247 (&rdmat + &dmat_y).map(|x| x.abs().powi(2)).sum().sqrt(),
248 0.0,
249 epsilon = 1e-14,
250 max_relative = 1e-14,
251 ) {
252 if increasingm {
254 if self.component_index == 0 {
255 self.component_index = 1;
256 } else {
257 assert_eq!(self.component_index, 1);
258 self.component_index = 0;
259 self.coefficients *= -1.0;
260 }
261 } else if self.component_index == 0 {
262 self.component_index = 1;
263 self.coefficients *= -1.0;
264 } else {
265 assert_eq!(self.component_index, 1);
266 self.component_index = 0;
267 }
268 Ok(self)
269 } else {
270 log::error!("Unsupported spin transformation matrix:\n{rdmat}");
271 Err(TransformationError(
272 "Only the identity or πy spin transformations are possible with unrestricted spin constraint."
273 .to_string(),
274 ))
275 }
276 }
277 SpinConstraint::Generalised(nspins, increasingm) => {
278 if nspins != 2 {
279 return Err(TransformationError(
280 "Only two-component spinor transformations are supported for now."
281 .to_string(),
282 ));
283 }
284
285 let nspatial = self.bao.n_funcs();
286 let old_coeff = &self.coefficients;
287 let new_coefficients = if increasingm {
288 let b_coeff = old_coeff.slice(s![0..nspatial]).to_owned();
289 let a_coeff = old_coeff.slice(s![nspatial..2 * nspatial]).to_owned();
290 let t_a_coeff = &a_coeff * rdmat[[0, 0]] + &b_coeff * rdmat[[0, 1]];
291 let t_b_coeff = &a_coeff * rdmat[[1, 0]] + &b_coeff * rdmat[[1, 1]];
292 concatenate(Axis(0), &[t_b_coeff.view(), t_a_coeff.view()]).expect(
293 "Unable to concatenate the transformed rows for the various shells.",
294 )
295 } else {
296 let a_coeff = old_coeff.slice(s![0..nspatial]).to_owned();
297 let b_coeff = old_coeff.slice(s![nspatial..2 * nspatial]).to_owned();
298 let t_a_coeff = &a_coeff * rdmat[[0, 0]] + &b_coeff * rdmat[[0, 1]];
299 let t_b_coeff = &a_coeff * rdmat[[1, 0]] + &b_coeff * rdmat[[1, 1]];
300 concatenate(Axis(0), &[t_a_coeff.view(), t_b_coeff.view()]).expect(
301 "Unable to concatenate the transformed rows for the various shells.",
302 )
303 };
304 self.coefficients = new_coefficients;
305 Ok(self)
306 }
307 }
308 }
309 }
310}
311
312impl<'a, T> SpinUnitaryTransformable for MolecularOrbital<'a, Complex<T>, SpinConstraint>
317where
318 T: Clone,
319 Complex<T>: ComplexFloat<Real = T>
320 + LinalgScalar
321 + ScalarOperand
322 + Lapack
323 + Mul<Complex<T>, Output = Complex<T>>
324 + Mul<Complex<f64>, Output = Complex<T>>,
325{
326 fn transform_spin_mut(
327 &mut self,
328 dmat: &Array2<Complex<f64>>,
329 ) -> Result<&mut Self, TransformationError> {
330 match self.structure_constraint {
331 SpinConstraint::Restricted(_) => {
332 if approx::relative_eq!(
333 (dmat - Array2::<Complex<f64>>::eye(2))
334 .map(|x| x.abs().powi(2))
335 .sum()
336 .sqrt(),
337 0.0,
338 epsilon = 1e-14,
339 max_relative = 1e-14,
340 ) {
341 Ok(self)
343 } else if approx::relative_eq!(
344 (dmat + Array2::<Complex<f64>>::eye(2))
345 .map(|x| x.abs().powi(2))
346 .sum()
347 .sqrt(),
348 0.0,
349 epsilon = 1e-14,
350 max_relative = 1e-14,
351 ) {
352 self.coefficients.map_inplace(|x| *x = -*x);
354 Ok(self)
355 } else {
356 log::error!("Unsupported spin transformation matrix:\n{}", dmat);
357 Err(TransformationError(
358 "Only the identity or negative identity spin transformations are possible with restricted spin constraint."
359 .to_string(),
360 ))
361 }
362 }
363 SpinConstraint::Unrestricted(nspins, increasingm) => {
364 if nspins != 2 {
367 return Err(TransformationError(
368 "Only two-component spinor transformations are supported for now."
369 .to_string(),
370 ));
371 }
372 let dmat_y = array![
373 [Complex::from(0.0), Complex::from(-1.0)],
374 [Complex::from(1.0), Complex::from(0.0)],
375 ];
376 if approx::relative_eq!(
377 (dmat - Array2::<Complex<f64>>::eye(2))
378 .map(|x| x.abs().powi(2))
379 .sum()
380 .sqrt(),
381 0.0,
382 epsilon = 1e-14,
383 max_relative = 1e-14,
384 ) {
385 Ok(self)
387 } else if approx::relative_eq!(
388 (dmat + Array2::<Complex<f64>>::eye(2))
389 .map(|x| x.abs().powi(2))
390 .sum()
391 .sqrt(),
392 0.0,
393 epsilon = 1e-14,
394 max_relative = 1e-14,
395 ) {
396 self.coefficients.map_inplace(|x| *x = -*x);
398 Ok(self)
399 } else if approx::relative_eq!(
400 (dmat - &dmat_y).map(|x| x.abs().powi(2)).sum().sqrt(),
401 0.0,
402 epsilon = 1e-14,
403 max_relative = 1e-14,
404 ) {
405 if increasingm {
407 if self.component_index == 0 {
408 self.component_index = 1;
409 self.coefficients.map_inplace(|x| *x = -*x);
410 } else {
411 assert_eq!(self.component_index, 1);
412 self.component_index = 0;
413 }
414 } else if self.component_index == 0 {
415 self.component_index = 1;
416 } else {
417 assert_eq!(self.component_index, 1);
418 self.component_index = 0;
419 self.coefficients.map_inplace(|x| *x = -*x);
420 }
421 Ok(self)
422 } else if approx::relative_eq!(
423 (dmat + &dmat_y).map(|x| x.abs().powi(2)).sum().sqrt(),
424 0.0,
425 epsilon = 1e-14,
426 max_relative = 1e-14,
427 ) {
428 if increasingm {
430 if self.component_index == 0 {
431 self.component_index = 1;
432 } else {
433 assert_eq!(self.component_index, 1);
434 self.component_index = 0;
435 self.coefficients.map_inplace(|x| *x = -*x);
436 }
437 } else if self.component_index == 0 {
438 self.component_index = 1;
439 self.coefficients.map_inplace(|x| *x = -*x);
440 } else {
441 assert_eq!(self.component_index, 1);
442 self.component_index = 0;
443 }
444 Ok(self)
445 } else {
446 log::error!("Unsupported spin transformation matrix:\n{dmat}");
447 Err(TransformationError(
448 "Only the identity or πy spin transformations are possible with unrestricted spin constraint."
449 .to_string(),
450 ))
451 }
452 }
453 SpinConstraint::Generalised(nspins, increasingm) => {
454 if nspins != 2 {
455 panic!("Only two-component spinor transformations are supported for now.");
456 }
457
458 let nspatial = self.bao.n_funcs();
459
460 let old_coeff = &self.coefficients;
461 let new_coefficients = if increasingm {
462 let b_coeff = old_coeff.slice(s![0..nspatial]).to_owned();
463 let a_coeff = old_coeff.slice(s![nspatial..2 * nspatial]).to_owned();
464 let t_a_coeff = &a_coeff * dmat[[0, 0]] + &b_coeff * dmat[[0, 1]];
465 let t_b_coeff = &a_coeff * dmat[[1, 0]] + &b_coeff * dmat[[1, 1]];
466 concatenate(Axis(0), &[t_b_coeff.view(), t_a_coeff.view()]).expect(
467 "Unable to concatenate the transformed rows for the various shells.",
468 )
469 } else {
470 let a_coeff = old_coeff.slice(s![0..nspatial]).to_owned();
471 let b_coeff = old_coeff.slice(s![nspatial..2 * nspatial]).to_owned();
472 let t_a_coeff = &a_coeff * dmat[[0, 0]] + &b_coeff * dmat[[0, 1]];
473 let t_b_coeff = &a_coeff * dmat[[1, 0]] + &b_coeff * dmat[[1, 1]];
474 concatenate(Axis(0), &[t_a_coeff.view(), t_b_coeff.view()]).expect(
475 "Unable to concatenate the transformed rows for the various shells.",
476 )
477 };
478 self.coefficients = new_coefficients;
479 Ok(self)
480 }
481 }
482 }
483}
484
485impl<'a, T> SymmetryTransformable for MolecularOrbital<'a, T, SpinConstraint>
489where
490 T: ComplexFloat + Lapack,
491 MolecularOrbital<'a, T, SpinConstraint>:
492 SpatialUnitaryTransformable + SpinUnitaryTransformable + TimeReversalTransformable,
493{
494 fn sym_permute_sites_spatial(
495 &self,
496 symop: &SymmetryOperation,
497 ) -> Result<Permutation<usize>, TransformationError> {
498 symop
499 .act_permute(&self.mol.molecule_ordinary_atoms())
500 .ok_or(TransformationError(format!(
501 "Unable to determine the atom permutation corresponding to the operation `{symop}`."
502 )))
503 }
504}
505
506impl<'a, T> DefaultTimeReversalTransformable for MolecularOrbital<'a, T, SpinConstraint> where
510 T: ComplexFloat + Lapack
511{
512}
513
514impl<'a, T> SpatialUnitaryTransformable for MolecularOrbital<'a, T, SpinOrbitCoupled>
523where
524 T: ComplexFloat + LinalgScalar + ScalarOperand + Copy + Lapack,
525 f64: Into<T>,
526{
527 fn transform_spatial_mut(
528 &mut self,
529 _rmat: &Array2<f64>,
530 _perm: Option<&Permutation<usize>>,
531 ) -> Result<&mut Self, TransformationError> {
532 Err(TransformationError(
533 "Unable to apply only spatial transformations to a spin--orbit-coupled molecular orbital."
534 .to_string(),
535 ))
536 }
537}
538
539impl<'a, T> SpinUnitaryTransformable for MolecularOrbital<'a, T, SpinOrbitCoupled>
544where
545 T: ComplexFloat + LinalgScalar + ScalarOperand + Copy + Lapack,
546 f64: Into<T>,
547{
548 fn transform_spin_mut(
549 &mut self,
550 _dmat: &Array2<Complex<f64>>,
551 ) -> Result<&mut Self, TransformationError> {
552 Err(TransformationError(
553 "Unable to apply only spin transformations to a spin--orbit-coupled molecular orbital."
554 .to_string(),
555 ))
556 }
557}
558
559impl<'a> TimeReversalTransformable for MolecularOrbital<'a, Complex<f64>, SpinOrbitCoupled> {
563 fn transform_timerev_mut(&mut self) -> Result<&mut Self, TransformationError> {
564 let t_element = SymmetryElement::builder()
565 .threshold(1e-12)
566 .proper_order(ElementOrder::Int(1))
567 .proper_power(1)
568 .raw_axis(Vector3::new(0.0, 0.0, 1.0))
569 .kind(TRROT)
570 .rotation_group(RotationGroup::SU2(true))
571 .build()
572 .unwrap();
573 let t = SymmetryOperation::builder()
574 .generating_element(t_element)
575 .power(1)
576 .build()
577 .unwrap();
578 let tmats: Vec<Array2<Complex<f64>>> =
579 assemble_spinor_rotation_matrices(self.bao, &t, None)
580 .map_err(|err| TransformationError(err.to_string()))?
581 .iter()
582 .map(|tmat| tmat.map(|&x| x.into()))
583 .collect();
584
585 let new_coefficients = {
586 let old_coeff = self.coefficients();
587 match self.structure_constraint {
588 SpinOrbitCoupled::JAdapted(ncomps) => {
589 let nfuncs_per_comp = self.bao.n_funcs();
590 let t_comp_blocks = (0..ncomps)
591 .map(|icomp| {
592 let comp_start = usize::from(icomp) * nfuncs_per_comp;
594 let comp_end = (usize::from(icomp) + 1) * nfuncs_per_comp;
595 let comp_block = old_coeff.slice(s![comp_start..comp_end]).to_owned();
596
597 let t_blocks = self
599 .bao
600 .shell_boundary_indices()
601 .iter()
602 .zip(tmats.iter())
603 .map(|((shl_start, shl_end), tmat)| {
604 tmat.dot(&comp_block.slice(s![*shl_start..*shl_end]))
605 })
606 .collect::<Vec<_>>();
607
608 concatenate(
610 Axis(0),
611 &t_blocks.iter().map(|t_block| t_block.view()).collect::<Vec<_>>(),
612 )
613 .expect("Unable to concatenate the transformed rows for the various shells.")
614 })
615 .collect::<Vec<_>>();
616
617 concatenate(
619 Axis(0),
620 &t_comp_blocks
621 .iter()
622 .map(|t_spin_block| t_spin_block.view())
623 .collect::<Vec<_>>(),
624 )
625 .expect("Unable to concatenate the transformed component blocks.")
626 }
627 }
628 };
629 self.coefficients = new_coefficients;
630
631 self.transform_cc_mut()?;
632 Ok(self)
633 }
634}
635
636impl<'a> SymmetryTransformable for MolecularOrbital<'a, Complex<f64>, SpinOrbitCoupled> {
640 fn sym_permute_sites_spatial(
644 &self,
645 symop: &SymmetryOperation,
646 ) -> Result<Permutation<usize>, TransformationError> {
647 if (symop.generating_element.threshold().log10() - self.mol.threshold.log10()).abs() >= 3.0
648 {
649 log::warn!(
650 "Symmetry operation threshold ({:.3e}) and molecule threshold ({:.3e}) \
651 differ by more than three orders of magnitudes.",
652 symop.generating_element.threshold(),
653 self.mol.threshold
654 )
655 }
656 symop
657 .act_permute(&self.mol.molecule_ordinary_atoms())
658 .ok_or(TransformationError(format!(
659 "Unable to determine the atom permutation corresponding to the operation `{symop}`.",
660 )))
661 }
662
663 fn sym_transform_spin_spatial_mut(
667 &mut self,
668 symop: &SymmetryOperation,
669 ) -> Result<&mut Self, TransformationError> {
670 let perm = self.sym_permute_sites_spatial(symop)?;
671 let pbao = self
672 .bao
673 .permute(&perm)
674 .map_err(|err| TransformationError(err.to_string()))?;
675
676 let tmats: Vec<Array2<Complex<f64>>> =
677 assemble_spinor_rotation_matrices(&pbao, symop, Some(&perm))
678 .map_err(|err| TransformationError(err.to_string()))?
679 .iter()
680 .map(|tmat| tmat.map(|&x| x.into()))
681 .collect();
682
683 if symop.contains_time_reversal() {
685 self.transform_cc_mut()?;
690 }
691
692 let new_coefficients = {
693 let old_coeffs = self.coefficients();
694 match self.structure_constraint {
695 SpinOrbitCoupled::JAdapted(ncomps) => {
696 let nfuncs_per_comp = self.bao.n_funcs();
697 let t_p_comp_blocks = (0..ncomps).map(|icomp| {
698 let comp_start = usize::from(icomp) * nfuncs_per_comp;
700 let comp_end = (usize::from(icomp) + 1) * nfuncs_per_comp;
701 let comp_block = old_coeffs.slice(s![comp_start..comp_end]).to_owned();
702
703 let p_comp_block = permute_array_by_atoms(&comp_block, &perm, &[Axis(0)], self.bao);
705
706 let t_p_blocks = pbao
708 .shell_boundary_indices()
709 .into_iter()
710 .zip(tmats.iter())
711 .map(|((shl_start, shl_end), tmat)| {
712 tmat.dot(&p_comp_block.slice(s![shl_start..shl_end]))
713 })
714 .collect::<Vec<_>>();
715
716 concatenate(
718 Axis(0),
719 &t_p_blocks.iter().map(|t_p_block| t_p_block.view()).collect::<Vec<_>>(),
720 )
721 .expect("Unable to concatenate the transformed rows for the various shells.")
722 }).collect::<Vec<_>>();
723
724 concatenate(
726 Axis(0),
727 &t_p_comp_blocks
728 .iter()
729 .map(|t_p_comp_block| t_p_comp_block.view())
730 .collect::<Vec<_>>(),
731 )
732 .expect("Unable to concatenate the transformed comp blocks.")
733 }
734 }
735 };
736 self.coefficients = new_coefficients;
737
738 Ok(self)
739 }
740}
741
742impl<'a, T, SC> ComplexConjugationTransformable for MolecularOrbital<'a, T, SC>
750where
751 T: ComplexFloat + Lapack,
752 SC: StructureConstraint + Clone,
753{
754 fn transform_cc_mut(&mut self) -> Result<&mut Self, TransformationError> {
755 self.coefficients.mapv_inplace(|x| x.conj());
756 self.complex_conjugated = !self.complex_conjugated;
757 Ok(self)
758 }
759}