#[cfg(feature = "serde-serialize-no-std")]
use serde::{Deserialize, Serialize};
use crate::allocator::Allocator;
use crate::base::{DefaultAllocator, Matrix, OMatrix, OVector, Unit};
use crate::dimension::{Const, Dim, DimDiff, DimMin, DimMinimum, DimSub, U1};
use simba::scalar::ComplexField;
use crate::geometry::Reflection;
use crate::linalg::householder;
use crate::num::Zero;
use std::mem::MaybeUninit;
#[cfg_attr(feature = "serde-serialize-no-std", derive(Serialize, Deserialize))]
#[cfg_attr(
feature = "serde-serialize-no-std",
serde(bound(serialize = "DimMinimum<R, C>: DimSub<U1>,
DefaultAllocator: Allocator<R, C> +
Allocator<DimMinimum<R, C>> +
Allocator<DimDiff<DimMinimum<R, C>, U1>>,
OMatrix<T, R, C>: Serialize,
OVector<T, DimMinimum<R, C>>: Serialize,
OVector<T, DimDiff<DimMinimum<R, C>, U1>>: Serialize"))
)]
#[cfg_attr(
feature = "serde-serialize-no-std",
serde(bound(deserialize = "DimMinimum<R, C>: DimSub<U1>,
DefaultAllocator: Allocator<R, C> +
Allocator<DimMinimum<R, C>> +
Allocator<DimDiff<DimMinimum<R, C>, U1>>,
OMatrix<T, R, C>: Deserialize<'de>,
OVector<T, DimMinimum<R, C>>: Deserialize<'de>,
OVector<T, DimDiff<DimMinimum<R, C>, U1>>: Deserialize<'de>"))
)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Debug)]
pub struct Bidiagonal<T: ComplexField, R: DimMin<C>, C: Dim>
where
DimMinimum<R, C>: DimSub<U1>,
DefaultAllocator:
Allocator<R, C> + Allocator<DimMinimum<R, C>> + Allocator<DimDiff<DimMinimum<R, C>, U1>>,
{
uv: OMatrix<T, R, C>,
diagonal: OVector<T, DimMinimum<R, C>>,
off_diagonal: OVector<T, DimDiff<DimMinimum<R, C>, U1>>,
upper_diagonal: bool,
}
impl<T: ComplexField, R: DimMin<C>, C: Dim> Copy for Bidiagonal<T, R, C>
where
DimMinimum<R, C>: DimSub<U1>,
DefaultAllocator:
Allocator<R, C> + Allocator<DimMinimum<R, C>> + Allocator<DimDiff<DimMinimum<R, C>, U1>>,
OMatrix<T, R, C>: Copy,
OVector<T, DimMinimum<R, C>>: Copy,
OVector<T, DimDiff<DimMinimum<R, C>, U1>>: Copy,
{
}
impl<T: ComplexField, R: DimMin<C>, C: Dim> Bidiagonal<T, R, C>
where
DimMinimum<R, C>: DimSub<U1>,
DefaultAllocator: Allocator<R, C>
+ Allocator<C>
+ Allocator<R>
+ Allocator<DimMinimum<R, C>>
+ Allocator<DimDiff<DimMinimum<R, C>, U1>>,
{
pub fn new(mut matrix: OMatrix<T, R, C>) -> Self {
let (nrows, ncols) = matrix.shape_generic();
let min_nrows_ncols = nrows.min(ncols);
let dim = min_nrows_ncols.value();
assert!(
dim != 0,
"Cannot compute the bidiagonalization of an empty matrix."
);
let mut diagonal = Matrix::uninit(min_nrows_ncols, Const::<1>);
let mut off_diagonal = Matrix::uninit(min_nrows_ncols.sub(Const::<1>), Const::<1>);
let mut axis_packed = Matrix::zeros_generic(ncols, Const::<1>);
let mut work = Matrix::zeros_generic(nrows, Const::<1>);
let upper_diagonal = nrows.value() >= ncols.value();
if upper_diagonal {
for ite in 0..dim - 1 {
diagonal[ite] = MaybeUninit::new(householder::clear_column_unchecked(
&mut matrix,
ite,
0,
None,
));
off_diagonal[ite] = MaybeUninit::new(householder::clear_row_unchecked(
&mut matrix,
&mut axis_packed,
&mut work,
ite,
1,
));
}
diagonal[dim - 1] = MaybeUninit::new(householder::clear_column_unchecked(
&mut matrix,
dim - 1,
0,
None,
));
} else {
for ite in 0..dim - 1 {
diagonal[ite] = MaybeUninit::new(householder::clear_row_unchecked(
&mut matrix,
&mut axis_packed,
&mut work,
ite,
0,
));
off_diagonal[ite] = MaybeUninit::new(householder::clear_column_unchecked(
&mut matrix,
ite,
1,
None,
));
}
diagonal[dim - 1] = MaybeUninit::new(householder::clear_row_unchecked(
&mut matrix,
&mut axis_packed,
&mut work,
dim - 1,
0,
));
}
let (diagonal, off_diagonal) =
unsafe { (diagonal.assume_init(), off_diagonal.assume_init()) };
Bidiagonal {
uv: matrix,
diagonal,
off_diagonal,
upper_diagonal,
}
}
#[inline]
#[must_use]
pub const fn is_upper_diagonal(&self) -> bool {
self.upper_diagonal
}
#[inline]
const fn axis_shift(&self) -> (usize, usize) {
if self.upper_diagonal { (0, 1) } else { (1, 0) }
}
#[inline]
pub fn unpack(
self,
) -> (
OMatrix<T, R, DimMinimum<R, C>>,
OMatrix<T, DimMinimum<R, C>, DimMinimum<R, C>>,
OMatrix<T, DimMinimum<R, C>, C>,
)
where
DefaultAllocator: Allocator<DimMinimum<R, C>, DimMinimum<R, C>>
+ Allocator<R, DimMinimum<R, C>>
+ Allocator<DimMinimum<R, C>, C>,
{
(self.u(), self.d(), self.v_t())
}
#[inline]
#[must_use]
pub fn d(&self) -> OMatrix<T, DimMinimum<R, C>, DimMinimum<R, C>>
where
DefaultAllocator: Allocator<DimMinimum<R, C>, DimMinimum<R, C>>,
{
let (nrows, ncols) = self.uv.shape_generic();
let d = nrows.min(ncols);
let mut res = OMatrix::identity_generic(d, d);
res.set_partial_diagonal(
self.diagonal
.iter()
.map(|e| T::from_real(e.clone().modulus())),
);
let start = self.axis_shift();
res.view_mut(start, (d.value() - 1, d.value() - 1))
.set_partial_diagonal(
self.off_diagonal
.iter()
.map(|e| T::from_real(e.clone().modulus())),
);
res
}
#[must_use]
pub fn u(&self) -> OMatrix<T, R, DimMinimum<R, C>>
where
DefaultAllocator: Allocator<R, DimMinimum<R, C>>,
{
let (nrows, ncols) = self.uv.shape_generic();
let mut res = Matrix::identity_generic(nrows, nrows.min(ncols));
let dim = self.diagonal.len();
let shift = self.axis_shift().0;
for i in (0..dim - shift).rev() {
let axis = self.uv.view_range(i + shift.., i);
if axis.norm_squared().is_zero() {
continue;
}
let refl = Reflection::new(Unit::new_unchecked(axis), T::zero());
let mut res_rows = res.view_range_mut(i + shift.., i..);
let sign = if self.upper_diagonal {
self.diagonal[i].clone().signum()
} else {
self.off_diagonal[i].clone().signum()
};
refl.reflect_with_sign(&mut res_rows, sign);
}
res
}
#[must_use]
pub fn v_t(&self) -> OMatrix<T, DimMinimum<R, C>, C>
where
DefaultAllocator: Allocator<DimMinimum<R, C>, C>,
{
let (nrows, ncols) = self.uv.shape_generic();
let min_nrows_ncols = nrows.min(ncols);
let mut res = Matrix::identity_generic(min_nrows_ncols, ncols);
let mut work = Matrix::zeros_generic(min_nrows_ncols, Const::<1>);
let mut axis_packed = Matrix::zeros_generic(ncols, Const::<1>);
let shift = self.axis_shift().1;
for i in (0..min_nrows_ncols.value() - shift).rev() {
let axis = self.uv.view_range(i, i + shift..);
let mut axis_packed = axis_packed.rows_range_mut(i + shift..);
axis_packed.tr_copy_from(&axis);
if axis_packed.norm_squared().is_zero() {
continue;
}
let refl = Reflection::new(Unit::new_unchecked(axis_packed), T::zero());
let mut res_rows = res.view_range_mut(i.., i + shift..);
let sign = if self.upper_diagonal {
self.off_diagonal[i].clone().signum()
} else {
self.diagonal[i].clone().signum()
};
refl.reflect_rows_with_sign(&mut res_rows, &mut work.rows_range_mut(i..), sign);
}
res
}
#[must_use]
pub fn diagonal(&self) -> OVector<T::RealField, DimMinimum<R, C>>
where
DefaultAllocator: Allocator<DimMinimum<R, C>>,
{
self.diagonal.map(|e| e.modulus())
}
#[must_use]
pub fn off_diagonal(&self) -> OVector<T::RealField, DimDiff<DimMinimum<R, C>, U1>>
where
DefaultAllocator: Allocator<DimDiff<DimMinimum<R, C>, U1>>,
{
self.off_diagonal.map(|e| e.modulus())
}
#[doc(hidden)]
pub const fn uv_internal(&self) -> &OMatrix<T, R, C> {
&self.uv
}
}