2017-08-14 01:53:04 +08:00
|
|
|
|
#[cfg(feature = "serde-serialize")]
|
2018-10-22 13:00:10 +08:00
|
|
|
|
use serde::{Deserialize, Serialize};
|
2017-08-14 01:53:04 +08:00
|
|
|
|
|
2019-03-23 21:29:07 +08:00
|
|
|
|
use crate::allocator::Allocator;
|
2021-04-11 17:00:38 +08:00
|
|
|
|
use crate::base::{DefaultAllocator, OMatrix, OVector};
|
2021-01-03 22:20:34 +08:00
|
|
|
|
use crate::dimension::{Const, DimDiff, DimSub, U1};
|
2019-03-23 21:29:07 +08:00
|
|
|
|
use crate::storage::Storage;
|
2020-03-21 19:16:46 +08:00
|
|
|
|
use simba::scalar::ComplexField;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2019-03-23 21:29:07 +08:00
|
|
|
|
use crate::linalg::householder;
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2017-08-14 01:53:04 +08:00
|
|
|
|
/// Hessenberg decomposition of a general matrix.
|
|
|
|
|
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
|
2018-05-19 23:15:15 +08:00
|
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "serde-serialize",
|
2021-04-11 17:00:38 +08:00
|
|
|
|
serde(bound(serialize = "DefaultAllocator: Allocator<T, D, D> +
|
|
|
|
|
Allocator<T, DimDiff<D, U1>>,
|
|
|
|
|
OMatrix<T, D, D>: Serialize,
|
|
|
|
|
OVector<T, DimDiff<D, U1>>: Serialize"))
|
2018-05-19 23:15:15 +08:00
|
|
|
|
)]
|
|
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "serde-serialize",
|
2021-04-11 17:00:38 +08:00
|
|
|
|
serde(bound(deserialize = "DefaultAllocator: Allocator<T, D, D> +
|
|
|
|
|
Allocator<T, DimDiff<D, U1>>,
|
|
|
|
|
OMatrix<T, D, D>: Deserialize<'de>,
|
|
|
|
|
OVector<T, DimDiff<D, U1>>: Deserialize<'de>"))
|
2018-05-19 23:15:15 +08:00
|
|
|
|
)]
|
2017-08-14 01:53:00 +08:00
|
|
|
|
#[derive(Clone, Debug)]
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub struct Hessenberg<T: ComplexField, D: DimSub<U1>>
|
2020-04-06 00:49:48 +08:00
|
|
|
|
where
|
2021-04-11 17:00:38 +08:00
|
|
|
|
DefaultAllocator: Allocator<T, D, D> + Allocator<T, DimDiff<D, U1>>,
|
2018-02-02 19:26:35 +08:00
|
|
|
|
{
|
2021-04-11 17:00:38 +08:00
|
|
|
|
hess: OMatrix<T, D, D>,
|
|
|
|
|
subdiag: OVector<T, DimDiff<D, U1>>,
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-11 17:00:38 +08:00
|
|
|
|
impl<T: ComplexField, D: DimSub<U1>> Copy for Hessenberg<T, D>
|
2018-02-02 19:26:35 +08:00
|
|
|
|
where
|
2021-04-11 17:00:38 +08:00
|
|
|
|
DefaultAllocator: Allocator<T, D, D> + Allocator<T, DimDiff<D, U1>>,
|
|
|
|
|
OMatrix<T, D, D>: Copy,
|
|
|
|
|
OVector<T, DimDiff<D, U1>>: Copy,
|
2020-03-21 19:16:46 +08:00
|
|
|
|
{
|
|
|
|
|
}
|
2017-08-14 01:53:00 +08:00
|
|
|
|
|
2021-04-11 17:00:38 +08:00
|
|
|
|
impl<T: ComplexField, D: DimSub<U1>> Hessenberg<T, D>
|
2020-04-06 00:49:48 +08:00
|
|
|
|
where
|
2021-04-11 17:00:38 +08:00
|
|
|
|
DefaultAllocator: Allocator<T, D, D> + Allocator<T, D> + Allocator<T, DimDiff<D, U1>>,
|
2018-02-02 19:26:35 +08:00
|
|
|
|
{
|
2017-08-03 01:37:44 +08:00
|
|
|
|
/// Computes the Hessenberg decomposition using householder reflections.
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn new(hess: OMatrix<T, D, D>) -> Self {
|
|
|
|
|
let mut work = unsafe {
|
|
|
|
|
crate::unimplemented_or_uninitialized_generic!(hess.data.shape().0, Const::<1>)
|
|
|
|
|
};
|
2017-08-03 01:37:44 +08:00
|
|
|
|
Self::new_with_workspace(hess, &mut work)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Computes the Hessenberg decomposition using householder reflections.
|
|
|
|
|
///
|
2017-08-14 01:53:04 +08:00
|
|
|
|
/// The workspace containing `D` elements must be provided but its content does not have to be
|
|
|
|
|
/// initialized.
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn new_with_workspace(mut hess: OMatrix<T, D, D>, work: &mut OVector<T, D>) -> Self {
|
2018-02-02 19:26:35 +08:00
|
|
|
|
assert!(
|
|
|
|
|
hess.is_square(),
|
|
|
|
|
"Cannot compute the hessenberg decomposition of a non-square matrix."
|
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
let dim = hess.data.shape().0;
|
|
|
|
|
|
2018-02-02 19:26:35 +08:00
|
|
|
|
assert!(
|
|
|
|
|
dim.value() != 0,
|
|
|
|
|
"Cannot compute the hessenberg decomposition of an empty matrix."
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
dim.value(),
|
|
|
|
|
work.len(),
|
|
|
|
|
"Hessenberg: invalid workspace size."
|
|
|
|
|
);
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
2021-04-11 17:00:38 +08:00
|
|
|
|
let mut subdiag = unsafe {
|
|
|
|
|
crate::unimplemented_or_uninitialized_generic!(dim.sub(Const::<1>), Const::<1>)
|
|
|
|
|
};
|
2017-08-03 01:37:44 +08:00
|
|
|
|
|
|
|
|
|
if dim.value() == 0 {
|
|
|
|
|
return Hessenberg { hess, subdiag };
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-02 19:26:35 +08:00
|
|
|
|
for ite in 0..dim.value() - 1 {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
householder::clear_column_unchecked(&mut hess, &mut subdiag[ite], ite, 1, Some(work));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Hessenberg { hess, subdiag }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Retrieves `(q, h)` with `q` the orthogonal matrix of this decomposition and `h` the
|
|
|
|
|
/// hessenberg matrix.
|
|
|
|
|
#[inline]
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn unpack(self) -> (OMatrix<T, D, D>, OMatrix<T, D, D>) {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
let q = self.q();
|
|
|
|
|
|
|
|
|
|
(q, self.unpack_h())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Retrieves the upper trapezoidal submatrix `H` of this decomposition.
|
|
|
|
|
#[inline]
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn unpack_h(mut self) -> OMatrix<T, D, D> {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
let dim = self.hess.nrows();
|
2021-04-11 17:00:38 +08:00
|
|
|
|
self.hess.fill_lower_triangle(T::zero(), 2);
|
2018-02-02 19:26:35 +08:00
|
|
|
|
self.hess
|
|
|
|
|
.slice_mut((1, 0), (dim - 1, dim - 1))
|
2021-04-11 17:00:38 +08:00
|
|
|
|
.set_partial_diagonal(self.subdiag.iter().map(|e| T::from_real(e.modulus())));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
self.hess
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-15 23:57:49 +08:00
|
|
|
|
// TODO: add a h that moves out of self.
|
2017-08-03 01:37:44 +08:00
|
|
|
|
/// Retrieves the upper trapezoidal submatrix `H` of this decomposition.
|
|
|
|
|
///
|
2017-08-14 01:53:04 +08:00
|
|
|
|
/// This is less efficient than `.unpack_h()` as it allocates a new matrix.
|
2017-08-03 01:37:44 +08:00
|
|
|
|
#[inline]
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn h(&self) -> OMatrix<T, D, D> {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
let dim = self.hess.nrows();
|
|
|
|
|
let mut res = self.hess.clone();
|
2021-04-11 17:00:38 +08:00
|
|
|
|
res.fill_lower_triangle(T::zero(), 2);
|
2018-02-02 19:26:35 +08:00
|
|
|
|
res.slice_mut((1, 0), (dim - 1, dim - 1))
|
2021-04-11 17:00:38 +08:00
|
|
|
|
.set_partial_diagonal(self.subdiag.iter().map(|e| T::from_real(e.modulus())));
|
2017-08-03 01:37:44 +08:00
|
|
|
|
res
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Computes the orthogonal matrix `Q` of this decomposition.
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn q(&self) -> OMatrix<T, D, D> {
|
2019-03-19 19:00:10 +08:00
|
|
|
|
householder::assemble_q(&self.hess, self.subdiag.as_slice())
|
2017-08-03 01:37:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
2021-04-11 17:00:38 +08:00
|
|
|
|
pub fn hess_internal(&self) -> &OMatrix<T, D, D> {
|
2017-08-03 01:37:44 +08:00
|
|
|
|
&self.hess
|
|
|
|
|
}
|
|
|
|
|
}
|