nalgebra/src/linalg/hessenberg.rs

196 lines
5.9 KiB
Rust
Raw Normal View History

2021-07-17 15:52:57 +08:00
use std::fmt;
use std::mem::MaybeUninit;
#[cfg(feature = "serde-serialize-no-std")]
2018-10-22 13:00:10 +08:00
use serde::{Deserialize, Serialize};
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};
use crate::dimension::{Const, DimDiff, DimSub, U1};
2021-07-20 07:00:40 +08:00
use crate::storage::{InnerOwned, Storage};
2021-07-17 15:52:57 +08:00
use crate::Matrix;
2020-03-21 19:16:46 +08:00
use simba::scalar::ComplexField;
2019-03-23 21:29:07 +08:00
use crate::linalg::householder;
/// Hessenberg decomposition of a general matrix.
#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde-serialize-no-std",
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"))
)]
#[cfg_attr(
feature = "serde-serialize-no-std",
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>"))
)]
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>>,
}
2021-07-20 07:00:40 +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>>,
2021-07-20 07:00:40 +08:00
InnerOwned<T, D, D>: Copy,
InnerOwned<T, DimDiff<D, U1>>: Copy,
2021-07-17 15:52:57 +08:00
{
}
2021-07-20 07:00:40 +08:00
*/
2021-07-17 15:52:57 +08:00
impl<T: ComplexField, D: DimSub<U1>> Clone for Hessenberg<T, D>
where
DefaultAllocator: Allocator<T, D, D> + Allocator<T, DimDiff<D, U1>>,
2021-07-20 07:00:40 +08:00
InnerOwned<T, D, D>: Clone,
InnerOwned<T, DimDiff<D, U1>>: Clone,
2020-03-21 19:16:46 +08:00
{
2021-07-17 15:52:57 +08:00
fn clone(&self) -> Self {
Self {
hess: self.hess.clone(),
subdiag: self.subdiag.clone(),
}
}
}
impl<T: ComplexField, D: DimSub<U1>> fmt::Debug for Hessenberg<T, D>
where
DefaultAllocator: Allocator<T, D, D> + Allocator<T, DimDiff<D, U1>>,
2021-07-20 07:00:40 +08:00
InnerOwned<T, D, D>: fmt::Debug,
InnerOwned<T, DimDiff<D, U1>>: fmt::Debug,
2021-07-17 15:52:57 +08:00
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Hessenberg")
.field("hess", &self.hess)
.field("subdiag", &self.subdiag)
.finish()
}
2020-03-21 19:16:46 +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
{
/// Computes the Hessenberg decomposition using householder reflections.
2021-04-11 17:00:38 +08:00
pub fn new(hess: OMatrix<T, D, D>) -> Self {
2021-07-17 15:52:57 +08:00
let mut work = OVector::new_uninitialized_generic(hess.data.shape().0, Const::<1>);
Self::new_with_workspace(hess, &mut work)
}
/// Computes the Hessenberg decomposition using householder reflections.
///
/// The workspace containing `D` elements must be provided but its content does not have to be
/// initialized.
2021-07-17 15:52:57 +08:00
pub fn new_with_workspace(
mut hess: OMatrix<T, D, D>,
work: &mut OVector<MaybeUninit<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."
);
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."
);
2021-07-17 15:52:57 +08:00
let mut subdiag = Matrix::new_uninitialized_generic(dim.sub(Const::<1>), Const::<1>);
if dim.value() == 0 {
// Safety: there's no (uninitialized) values.
unsafe {
return Self {
hess,
subdiag: subdiag.assume_init(),
};
}
}
2018-02-02 19:26:35 +08:00
for ite in 0..dim.value() - 1 {
// Safety: the pointer is valid for writes, aligned, and uninitialized.
unsafe {
householder::clear_column_unchecked(
&mut hess,
subdiag[ite].as_mut_ptr(),
ite,
1,
Some(work),
);
}
}
// Safety: all values have been initialized.
unsafe {
Self {
hess,
subdiag: subdiag.assume_init(),
}
2021-07-17 15:52:57 +08:00
}
}
/// 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>) {
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> {
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())));
self.hess
}
2020-11-15 23:57:49 +08:00
// TODO: add a h that moves out of self.
/// Retrieves the upper trapezoidal submatrix `H` of this decomposition.
///
/// This is less efficient than `.unpack_h()` as it allocates a new matrix.
#[inline]
#[must_use]
2021-07-17 15:52:57 +08:00
pub fn h(&self) -> OMatrix<T, D, D>
where
2021-07-20 07:00:40 +08:00
InnerOwned<T, D, D>: Clone,
2021-07-17 15:52:57 +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())));
res
}
/// Computes the orthogonal matrix `Q` of this decomposition.
#[must_use]
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())
}
#[doc(hidden)]
2021-04-11 17:00:38 +08:00
pub fn hess_internal(&self) -> &OMatrix<T, D, D> {
&self.hess
}
}