faer/linalg/
jacobi.rs

1use crate::internal_prelude::*;
2
3/// jacobi rotation matrix
4///
5/// $$ \begin{bmatrix} c & -\bar s \\\\ s & c \end{bmatrix} $$
6#[derive(Copy, Clone, Debug)]
7#[repr(C)]
8pub struct JacobiRotation<T> {
9	/// cosine
10	pub c: T,
11	/// sine
12	pub s: T,
13}
14
15#[allow(dead_code)]
16impl<T: ComplexField> JacobiRotation<T> {
17	#[math]
18	#[doc(hidden)]
19	pub fn make_givens(p: T, q: T) -> Self
20	where
21		T: RealField,
22	{
23		{
24			let p = real(p);
25			let q = real(q);
26
27			if q == zero() {
28				let c = if p < zero() { -one::<T>() } else { one() };
29				let s = zero();
30				let c = from_real(c);
31				let s = from_real(s);
32
33				Self { c, s }
34			} else if p == zero() {
35				let c = zero();
36				let s = if q < zero() { one() } else { -one::<T>() };
37				let c = from_real(c);
38				let s = from_real(s);
39
40				Self { c, s }
41			} else if abs(p) > abs(q) {
42				let t = q / p;
43				let mut u = hypot(one(), t);
44				if p < zero() {
45					u = -u;
46				}
47				let c = recip(u);
48				let s = -t * c;
49
50				let c = from_real(c);
51				let s = from_real(s);
52
53				Self { c, s }
54			} else {
55				let t = p / q;
56				let mut u = hypot(one(), t);
57				if q < zero() {
58					u = -u;
59				}
60				let s = -recip(u);
61				let c = -t * s;
62
63				let c = from_real(c);
64				let s = from_real(s);
65
66				Self { c, s }
67			}
68		}
69	}
70
71	#[math]
72	#[doc(hidden)]
73	pub fn rotg(p: T, q: T) -> (Self, T) {
74		if try_const! { T::IS_REAL } {
75			{
76				let p = real(p);
77				let q = real(q);
78
79				if q == zero() {
80					let c = one();
81					let s = zero();
82					let c = from_real(c);
83					let s = from_real(s);
84
85					(Self { c, s }, from_real(p))
86				} else if p == zero() {
87					let c = zero();
88					let s = one();
89					let c = from_real(c);
90					let s = from_real(s);
91
92					(Self { c, s }, from_real(q))
93				} else {
94					let safmin = min_positive();
95					let safmax = recip(safmin);
96					let a = p;
97					let b = q;
98
99					let scl = min(safmax, max(safmin, max(abs(a), abs(b))));
100					let r = scl * (sqrt(abs2(a / scl) + abs2(b / scl)));
101					let r = if abs(a) > abs(b) {
102						if a > zero() { r } else { -r }
103					} else {
104						if b > zero() { r } else { -r }
105					};
106					let c = from_real(a / r);
107					let s = from_real(b / r);
108					let r = from_real(r);
109
110					(Self { c, s }, r)
111				}
112			}
113		} else {
114			{
115				let a = p;
116				let b = q;
117
118				let eps = eps();
119				let sml = min_positive();
120				let big = max_positive();
121				let rtmin = sqrt(div(sml, eps));
122				let rtmax = recip(rtmin);
123
124				if b == zero() {
125					return (Self { c: one(), s: zero() }, one());
126				}
127
128				let (c, s, r);
129
130				if a == zero() {
131					c = zero();
132					let g1 = max(abs(real(b)), abs(imag(b)));
133					if g1 > rtmin && g1 < rtmax {
134						// Use unscaled algorithm
135						let g2 = abs2(b);
136
137						let d = sqrt(g2);
138						s = mul_real(conj(b), recip(d));
139						r = from_real(d);
140					} else {
141						// Use scaled algorithm
142						let u = min(big, max(sml, g1));
143						let uu = recip(u);
144						let gs = mul_real(b, uu);
145						let g2 = abs2(gs);
146						let d = sqrt(g2);
147						s = mul_real(conj(gs), recip(d));
148						r = from_real(mul(d, u));
149					}
150				} else {
151					let f1 = max(abs(real(a)), abs(imag(a)));
152					let g1 = max(abs(real(b)), abs(imag(b)));
153					if f1 > rtmin && f1 < rtmax && g1 > rtmin && g1 < rtmax {
154						// Use unscaled algorithm
155						let f2 = abs2(a);
156						let g2 = abs2(b);
157						let h2 = add(f2, g2);
158						let d = if f2 > rtmin && h2 < rtmax {
159							sqrt(mul(f2, h2))
160						} else {
161							mul(sqrt(f2), sqrt(h2))
162						};
163						let p = recip(d);
164						c = from_real(mul(f2, p));
165						s = conj(b) * mul_real(a, p);
166
167						r = mul_real(a, mul(h2, p));
168					} else {
169						// Use scaled algorithm
170						let u = min(big, max(sml, max(f1, g1)));
171						let uu = recip(u);
172						let gs = mul_real(b, uu);
173						let g2 = abs2(gs);
174						let (f2, h2, w);
175						let fs;
176						if mul(f1, uu) < rtmin {
177							// a is not well-scaled when scaled by g1.
178							let v = min(big, max(sml, f1));
179							let vv = recip(v);
180							w = mul(v, uu);
181							fs = mul_real(a, vv);
182							f2 = abs2(fs);
183							h2 = add(mul(mul(f2, w), w), g2);
184						} else {
185							// Otherwise use the same scaling for a and b.
186							w = one();
187							fs = mul_real(a, uu);
188							f2 = abs2(fs);
189							h2 = add(f2, g2);
190						}
191						let d = if f2 > rtmin && h2 < rtmax {
192							sqrt(mul(f2, h2))
193						} else {
194							mul(sqrt(f2), sqrt(h2))
195						};
196						let p = recip(d);
197						c = from_real(mul(mul(f2, p), w));
198						s = conj(gs) * mul_real(fs, p);
199						r = mul_real(mul_real(fs, mul(h2, p)), u);
200					}
201				}
202
203				(Self { c, s }, r)
204			}
205		}
206	}
207
208	/// apply to the given matrix from the left
209	///
210	/// $$ J \begin{bmatrix} m_{00} & m_{01} \\\\ m_{10} & m_{11} \end{bmatrix} $$
211	#[inline]
212	#[math]
213	pub fn apply_on_the_left_2x2(&self, m00: T, m01: T, m10: T, m11: T) -> (T, T, T, T) {
214		let Self { c, s } = self;
215
216		(
217			m00 * *c + m10 * *s,
218			m01 * *c + m11 * *s,
219			*c * m10 - conj(*s) * m00,
220			*c * m11 - conj(*s) * m01,
221		)
222	}
223
224	/// apply to the given matrix from the right
225	///
226	/// $$ \begin{bmatrix} m_{00} & m_{01} \\\\ m_{10} & m_{11} \end{bmatrix} J $$
227	#[inline]
228	pub fn apply_on_the_right_2x2(&self, m00: T, m01: T, m10: T, m11: T) -> (T, T, T, T) {
229		let (r00, r01, r10, r11) = self.transpose().apply_on_the_left_2x2(m00, m10, m01, m11);
230		(r00, r10, r01, r11)
231	}
232
233	#[inline]
234	fn apply_on_the_left_in_place_impl<'N>(&self, (x, y): (RowMut<'_, T, Dim<'N>>, RowMut<'_, T, Dim<'N>>)) {
235		let mut x = x;
236		let mut y = y;
237		if try_const! { T::SIMD_CAPABILITIES.is_simd() } {
238			struct Impl<'a, 'N, T: ComplexField> {
239				this: &'a JacobiRotation<T>,
240				x: RowMut<'a, T, Dim<'N>, ContiguousFwd>,
241				y: RowMut<'a, T, Dim<'N>, ContiguousFwd>,
242			}
243			impl<T: ComplexField> pulp::WithSimd for Impl<'_, '_, T> {
244				type Output = ();
245
246				#[inline(always)]
247				fn with_simd<S: pulp::Simd>(self, simd: S) -> Self::Output {
248					let Self { this, x, y } = self;
249					let simd = SimdCtx::new(T::simd_ctx(simd), x.ncols());
250					this.apply_on_the_left_in_place_with_simd(simd, x, y);
251				}
252			}
253
254			if x.col_stride() < 0 && y.col_stride() < 0 {
255				x = x.reverse_cols_mut();
256				y = y.reverse_cols_mut();
257			}
258
259			if let (Some(x), Some(y)) = (x.rb_mut().try_as_row_major_mut(), y.rb_mut().try_as_row_major_mut()) {
260				dispatch!(Impl { this: self, x, y }, Impl, T);
261				return;
262			}
263		}
264		self.apply_on_the_left_in_place_fallback(x, y);
265	}
266
267	/// apply from the left to $x$ and $y$
268	#[inline]
269	pub fn apply_on_the_left_in_place<N: Shape>(&self, (x, y): (RowMut<'_, T, N>, RowMut<'_, T, N>)) {
270		with_dim!(N, x.ncols().unbound());
271		self.apply_on_the_left_in_place_impl((x.as_col_shape_mut(N), y.as_col_shape_mut(N)));
272	}
273
274	/// apply from the right to $x$ and $y$
275	#[inline]
276	pub fn apply_on_the_right_in_place<N: Shape>(&self, (x, y): (ColMut<'_, T, N>, ColMut<'_, T, N>)) {
277		with_dim!(N, x.nrows().unbound());
278
279		let x = x.as_row_shape_mut(N);
280		let y = y.as_row_shape_mut(N);
281
282		self.transpose().apply_on_the_left_in_place((x.transpose_mut(), y.transpose_mut()));
283	}
284
285	#[inline(never)]
286	#[math]
287	fn apply_on_the_left_in_place_fallback<'N>(&self, x: RowMut<'_, T, Dim<'N>>, y: RowMut<'_, T, Dim<'N>>) {
288		let Self { c, s } = self;
289		zip!(x, y).for_each(move |unzip!(x, y)| {
290			let x_ = *c * *x - conj(*s) * *y;
291			let y_ = *c * *y + *s * *x;
292			*x = x_;
293			*y = y_;
294		});
295	}
296
297	#[inline(always)]
298	pub(crate) fn apply_on_the_right_in_place_with_simd<'N, S: pulp::Simd>(
299		&self,
300		simd: SimdCtx<'N, T, S>,
301		x: ColMut<'_, T, Dim<'N>, ContiguousFwd>,
302		y: ColMut<'_, T, Dim<'N>, ContiguousFwd>,
303	) {
304		self.transpose()
305			.apply_on_the_left_in_place_with_simd(simd, x.transpose_mut(), y.transpose_mut());
306	}
307
308	#[math]
309	#[inline(always)]
310	pub(crate) fn apply_on_the_left_in_place_with_simd<'N, S: pulp::Simd>(
311		&self,
312		simd: SimdCtx<'N, T, S>,
313		x: RowMut<'_, T, Dim<'N>, ContiguousFwd>,
314		y: RowMut<'_, T, Dim<'N>, ContiguousFwd>,
315	) {
316		let Self { c, s } = self;
317
318		if *c == one() && *s == zero() {
319			return;
320		};
321
322		let mut x = x.transpose_mut();
323		let mut y = y.transpose_mut();
324
325		let c = simd.splat_real(&real(*c));
326		let s = simd.splat(&s);
327
328		let (head, body, tail) = simd.indices();
329
330		if let Some(i) = head {
331			let mut xx = simd.read(x.rb(), i);
332			let mut yy = simd.read(y.rb(), i);
333
334			(xx, yy) = (
335				simd.conj_mul_add(simd.neg(s), yy, simd.mul_real(xx, c)),
336				simd.mul_add(s, xx, simd.mul_real(yy, c)),
337			);
338
339			simd.write(x.rb_mut(), i, xx);
340			simd.write(y.rb_mut(), i, yy);
341		}
342		for i in body {
343			let mut xx = simd.read(x.rb(), i);
344			let mut yy = simd.read(y.rb(), i);
345
346			(xx, yy) = (
347				simd.conj_mul_add(simd.neg(s), yy, simd.mul_real(xx, c)),
348				simd.mul_add(s, xx, simd.mul_real(yy, c)),
349			);
350
351			simd.write(x.rb_mut(), i, xx);
352			simd.write(y.rb_mut(), i, yy);
353		}
354		if let Some(i) = tail {
355			let mut xx = simd.read(x.rb(), i);
356			let mut yy = simd.read(y.rb(), i);
357
358			(xx, yy) = (
359				simd.conj_mul_add(simd.neg(s), yy, simd.mul_real(xx, c)),
360				simd.mul_add(s, xx, simd.mul_real(yy, c)),
361			);
362
363			simd.write(x.rb_mut(), i, xx);
364			simd.write(y.rb_mut(), i, yy);
365		}
366	}
367
368	/// returns the adjoint of `self`
369	#[inline]
370	#[math]
371	pub fn adjoint(&self) -> Self {
372		Self {
373			c: copy(self.c),
374			s: -conj(self.s),
375		}
376	}
377
378	/// returns the conjugate of `self`
379	#[inline]
380	#[math]
381	pub fn conjugate(&self) -> Self {
382		Self {
383			c: copy(self.c),
384			s: conj(self.s),
385		}
386	}
387
388	/// returns the transpose of `self`
389	#[inline]
390	#[math]
391	pub fn transpose(&self) -> Self {
392		Self { c: copy(self.c), s: -self.s }
393	}
394}