1#[cfg(feature = "serde-serialize")]
2use serde::{Deserialize, Serialize};
3
4use num::Zero;
5use num_complex::Complex;
6
7use simba::scalar::RealField;
8
9use crate::ComplexHelper;
10use na::allocator::Allocator;
11use na::dimension::{Const, Dim};
12use na::{DefaultAllocator, Matrix, OMatrix, OVector, Scalar};
13
14use lapack;
15
16#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
18#[cfg_attr(
19 feature = "serde-serialize",
20 serde(bound(serialize = "DefaultAllocator: Allocator<D, D> + Allocator<D>,
21 OVector<T, D>: Serialize,
22 OMatrix<T, D, D>: Serialize"))
23)]
24#[cfg_attr(
25 feature = "serde-serialize",
26 serde(bound(deserialize = "DefaultAllocator: Allocator<D, D> + Allocator<D>,
27 OVector<T, D>: Deserialize<'de>,
28 OMatrix<T, D, D>: Deserialize<'de>"))
29)]
30#[derive(Clone, Debug)]
31pub struct Schur<T: Scalar, D: Dim>
32where
33 DefaultAllocator: Allocator<D> + Allocator<D, D>,
34{
35 re: OVector<T, D>,
36 im: OVector<T, D>,
37 t: OMatrix<T, D, D>,
38 q: OMatrix<T, D, D>,
39}
40
41impl<T: Scalar + Copy, D: Dim> Copy for Schur<T, D>
42where
43 DefaultAllocator: Allocator<D, D> + Allocator<D>,
44 OMatrix<T, D, D>: Copy,
45 OVector<T, D>: Copy,
46{
47}
48
49impl<T: SchurScalar + RealField, D: Dim> Schur<T, D>
50where
51 DefaultAllocator: Allocator<D, D> + Allocator<D>,
52{
53 pub fn new(m: OMatrix<T, D, D>) -> Self {
57 Self::try_new(m).expect("Schur decomposition: convergence failed.")
58 }
59
60 pub fn try_new(mut m: OMatrix<T, D, D>) -> Option<Self> {
64 assert!(
65 m.is_square(),
66 "Unable to compute the eigenvalue decomposition of a non-square matrix."
67 );
68
69 let (nrows, ncols) = m.shape_generic();
70 let n = nrows.value();
71
72 let lda = n as i32;
73
74 let mut info = 0;
75
76 let mut wr = Matrix::zeros_generic(nrows, Const::<1>);
77 let mut wi = Matrix::zeros_generic(nrows, Const::<1>);
78 let mut q = Matrix::zeros_generic(nrows, ncols);
79 let mut bwork = [0i32];
81 let mut unused = 0;
82
83 let lwork = T::xgees_work_size(
84 b'V',
85 b'N',
90 n as i32,
91 m.as_mut_slice(),
92 lda,
93 &mut unused,
94 wr.as_mut_slice(),
95 wi.as_mut_slice(),
96 q.as_mut_slice(),
97 n as i32,
98 &mut bwork,
99 &mut info,
100 );
101 lapack_check!(info);
102
103 let mut work = vec![T::zero(); lwork as usize];
104
105 T::xgees(
106 b'V',
107 b'N',
108 n as i32,
109 m.as_mut_slice(),
110 lda,
111 &mut unused,
112 wr.as_mut_slice(),
113 wi.as_mut_slice(),
114 q.as_mut_slice(),
115 n as i32,
116 &mut work,
117 lwork,
118 &mut bwork,
119 &mut info,
120 );
121 lapack_check!(info);
122
123 Some(Schur {
124 re: wr,
125 im: wi,
126 t: m,
127 q,
128 })
129 }
130
131 pub fn unpack(self) -> (OMatrix<T, D, D>, OMatrix<T, D, D>) {
134 (self.q, self.t)
135 }
136
137 #[must_use]
141 pub fn eigenvalues(&self) -> Option<OVector<T, D>> {
142 if self.im.iter().all(|e| e.is_zero()) {
143 Some(self.re.clone())
144 } else {
145 None
146 }
147 }
148
149 #[must_use]
151 pub fn complex_eigenvalues(&self) -> OVector<Complex<T>, D>
152 where
153 DefaultAllocator: Allocator<D>,
154 {
155 let mut out = Matrix::zeros_generic(self.t.shape_generic().0, Const::<1>);
156
157 for i in 0..out.len() {
158 out[i] = Complex::new(self.re[i].clone(), self.im[i].clone())
159 }
160
161 out
162 }
163}
164
165pub trait SchurScalar: Scalar {
172 #[allow(missing_docs)]
173 fn xgees(
174 jobvs: u8,
175 sort: u8,
176 n: i32,
179 a: &mut [Self],
180 lda: i32,
181 sdim: &mut i32,
182 wr: &mut [Self],
183 wi: &mut [Self],
184 vs: &mut [Self],
185 ldvs: i32,
186 work: &mut [Self],
187 lwork: i32,
188 bwork: &mut [i32],
189 info: &mut i32,
190 );
191
192 #[allow(missing_docs)]
193 fn xgees_work_size(
194 jobvs: u8,
195 sort: u8,
196 n: i32,
198 a: &mut [Self],
199 lda: i32,
200 sdim: &mut i32,
201 wr: &mut [Self],
202 wi: &mut [Self],
203 vs: &mut [Self],
204 ldvs: i32,
205 bwork: &mut [i32],
206 info: &mut i32,
207 ) -> i32;
208}
209
210macro_rules! real_eigensystem_scalar_impl (
211 ($N: ty, $xgees: path) => (
212 impl SchurScalar for $N {
213 #[inline]
214 fn xgees(jobvs: u8,
215 sort: u8,
216 n: i32,
222 a: &mut [$N],
223 lda: i32,
224 sdim: &mut i32,
225 wr: &mut [$N],
226 wi: &mut [$N],
227 vs: &mut [$N],
228 ldvs: i32,
229 work: &mut [$N],
230 lwork: i32,
231 bwork: &mut [i32],
232 info: &mut i32) {
233 unsafe { $xgees(jobvs, sort, None, n, a, lda, sdim, wr, wi, vs, ldvs, work, lwork, bwork, info); }
234 }
235
236
237 #[inline]
238 fn xgees_work_size(jobvs: u8,
239 sort: u8,
240 n: i32,
242 a: &mut [$N],
243 lda: i32,
244 sdim: &mut i32,
245 wr: &mut [$N],
246 wi: &mut [$N],
247 vs: &mut [$N],
248 ldvs: i32,
249 bwork: &mut [i32],
250 info: &mut i32)
251 -> i32 {
252 let mut work = [ Zero::zero() ];
253 let lwork = -1 as i32;
254
255 unsafe { $xgees(jobvs, sort, None, n, a, lda, sdim, wr, wi, vs, ldvs, &mut work, lwork, bwork, info); }
256 ComplexHelper::real_part(work[0]) as i32
257 }
258 }
259 )
260);
261
262real_eigensystem_scalar_impl!(f32, lapack::sgees);
263real_eigensystem_scalar_impl!(f64, lapack::dgees);