1#[cfg(feature = "serde-serialize-no-std")]
2use serde::{Deserialize, Serialize};
3
4use crate::allocator::Allocator;
5use crate::base::{DefaultAllocator, OMatrix, OVector};
6use crate::dimension::{Const, DimDiff, DimSub, U1};
7use simba::scalar::ComplexField;
8
9use crate::Matrix;
10use crate::linalg::householder;
11use std::mem::MaybeUninit;
12
13#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
15#[cfg_attr(
16 feature = "serde-serialize-no-std",
17 serde(bound(serialize = "DefaultAllocator: Allocator<D, D> +
18 Allocator<DimDiff<D, U1>>,
19 OMatrix<T, D, D>: Serialize,
20 OVector<T, DimDiff<D, U1>>: Serialize"))
21)]
22#[cfg_attr(
23 feature = "serde-serialize-no-std",
24 serde(bound(deserialize = "DefaultAllocator: Allocator<D, D> +
25 Allocator<DimDiff<D, U1>>,
26 OMatrix<T, D, D>: Deserialize<'de>,
27 OVector<T, DimDiff<D, U1>>: Deserialize<'de>"))
28)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[derive(Clone, Debug)]
31pub struct Hessenberg<T: ComplexField, D: DimSub<U1>>
32where
33 DefaultAllocator: Allocator<D, D> + Allocator<DimDiff<D, U1>>,
34{
35 hess: OMatrix<T, D, D>,
36 subdiag: OVector<T, DimDiff<D, U1>>,
37}
38
39impl<T: ComplexField, D: DimSub<U1>> Copy for Hessenberg<T, D>
40where
41 DefaultAllocator: Allocator<D, D> + Allocator<DimDiff<D, U1>>,
42 OMatrix<T, D, D>: Copy,
43 OVector<T, DimDiff<D, U1>>: Copy,
44{
45}
46
47impl<T: ComplexField, D: DimSub<U1>> Hessenberg<T, D>
48where
49 DefaultAllocator: Allocator<D, D> + Allocator<D> + Allocator<DimDiff<D, U1>>,
50{
51 pub fn new(hess: OMatrix<T, D, D>) -> Self {
53 let mut work = Matrix::zeros_generic(hess.shape_generic().0, Const::<1>);
54 Self::new_with_workspace(hess, &mut work)
55 }
56
57 pub fn new_with_workspace(mut hess: OMatrix<T, D, D>, work: &mut OVector<T, D>) -> Self {
62 assert!(
63 hess.is_square(),
64 "Cannot compute the hessenberg decomposition of a non-square matrix."
65 );
66
67 let dim = hess.shape_generic().0;
68
69 assert!(
70 dim.value() != 0,
71 "Cannot compute the hessenberg decomposition of an empty matrix."
72 );
73 assert_eq!(
74 dim.value(),
75 work.len(),
76 "Hessenberg: invalid workspace size."
77 );
78
79 if dim.value() == 0 {
80 return Hessenberg {
81 hess,
82 subdiag: Matrix::zeros_generic(dim.sub(Const::<1>), Const::<1>),
83 };
84 }
85
86 let mut subdiag = Matrix::uninit(dim.sub(Const::<1>), Const::<1>);
87
88 for ite in 0..dim.value() - 1 {
89 subdiag[ite] = MaybeUninit::new(householder::clear_column_unchecked(
90 &mut hess,
91 ite,
92 1,
93 Some(work),
94 ));
95 }
96
97 let subdiag = unsafe { subdiag.assume_init() };
99 Hessenberg { hess, subdiag }
100 }
101
102 #[inline]
105 pub fn unpack(self) -> (OMatrix<T, D, D>, OMatrix<T, D, D>) {
106 let q = self.q();
107
108 (q, self.unpack_h())
109 }
110
111 #[inline]
113 pub fn unpack_h(mut self) -> OMatrix<T, D, D> {
114 let dim = self.hess.nrows();
115 self.hess.fill_lower_triangle(T::zero(), 2);
116 self.hess
117 .view_mut((1, 0), (dim - 1, dim - 1))
118 .set_partial_diagonal(
119 self.subdiag
120 .iter()
121 .map(|e| T::from_real(e.clone().modulus())),
122 );
123 self.hess
124 }
125
126 #[inline]
131 #[must_use]
132 pub fn h(&self) -> OMatrix<T, D, D> {
133 let dim = self.hess.nrows();
134 let mut res = self.hess.clone();
135 res.fill_lower_triangle(T::zero(), 2);
136 res.view_mut((1, 0), (dim - 1, dim - 1))
137 .set_partial_diagonal(
138 self.subdiag
139 .iter()
140 .map(|e| T::from_real(e.clone().modulus())),
141 );
142 res
143 }
144
145 #[must_use]
147 pub fn q(&self) -> OMatrix<T, D, D> {
148 householder::assemble_q(&self.hess, self.subdiag.as_slice())
149 }
150
151 #[doc(hidden)]
152 pub const fn hess_internal(&self) -> &OMatrix<T, D, D> {
153 &self.hess
154 }
155}