forked from M-Labs/nalgebra
Implement some BLAS opertaions involving adjoint.
This commit is contained in:
parent
1001e8ee0f
commit
921a05d523
@ -214,7 +214,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Solves the linear system `self.conjugate_transpose() * x = b`, where `x` is the unknown to
|
||||
/// Solves the linear system `self.adjoint() * x = b`, where `x` is the unknown to
|
||||
/// be determined.
|
||||
pub fn solve_conjugate_transpose<R2: Dim, C2: Dim, S2>(
|
||||
&self,
|
||||
@ -249,7 +249,7 @@ where
|
||||
self.generic_solve_mut(b'T', b)
|
||||
}
|
||||
|
||||
/// Solves in-place the linear system `self.conjugate_transpose() * x = b`, where `x` is the unknown to
|
||||
/// Solves in-place the linear system `self.adjoint() * x = b`, where `x` is the unknown to
|
||||
/// be determined.
|
||||
///
|
||||
/// Returns `false` if no solution was found (the decomposed matrix is singular).
|
||||
|
600
src/base/blas.rs
600
src/base/blas.rs
@ -11,7 +11,7 @@ use base::constraint::{
|
||||
};
|
||||
use base::dimension::{Dim, Dynamic, U1, U2, U3, U4};
|
||||
use base::storage::{Storage, StorageMut};
|
||||
use base::{DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector};
|
||||
use base::{DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector, DVectorSlice};
|
||||
|
||||
|
||||
// FIXME: find a way to avoid code duplication just for complex number support.
|
||||
@ -32,11 +32,11 @@ impl<N: Complex, D: Dim, S: Storage<N, D>> Vector<N, D, S> {
|
||||
pub fn icamax(&self) -> usize {
|
||||
assert!(!self.is_empty(), "The input vector must not be empty.");
|
||||
|
||||
let mut the_max = unsafe { self.vget_unchecked(0).asum() };
|
||||
let mut the_max = unsafe { self.vget_unchecked(0).norm1() };
|
||||
let mut the_i = 0;
|
||||
|
||||
for i in 1..self.nrows() {
|
||||
let val = unsafe { self.vget_unchecked(i).asum() };
|
||||
let val = unsafe { self.vget_unchecked(i).norm1() };
|
||||
|
||||
if val > the_max {
|
||||
the_max = val;
|
||||
@ -211,12 +211,12 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
pub fn icamax_full(&self) -> (usize, usize) {
|
||||
assert!(!self.is_empty(), "The input matrix must not be empty.");
|
||||
|
||||
let mut the_max = unsafe { self.get_unchecked((0, 0)).asum() };
|
||||
let mut the_max = unsafe { self.get_unchecked((0, 0)).norm1() };
|
||||
let mut the_ij = (0, 0);
|
||||
|
||||
for j in 0..self.ncols() {
|
||||
for i in 0..self.nrows() {
|
||||
let val = unsafe { self.get_unchecked((i, j)).asum() };
|
||||
let val = unsafe { self.get_unchecked((i, j)).norm1() };
|
||||
|
||||
if val > the_max {
|
||||
the_max = val;
|
||||
@ -263,13 +263,11 @@ impl<N: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<N, R, C>> Matri
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
/// The dot product between two complex or real vectors or matrices (seen as vectors).
|
||||
///
|
||||
/// This is the same as `.dot` except that the conjugate of each component of `self` is taken
|
||||
/// before performing the products.
|
||||
#[inline]
|
||||
pub fn cdot<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<N, R2, C2, SB>) -> N
|
||||
impl<N, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S>
|
||||
where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
{
|
||||
#[inline(always)]
|
||||
fn dotx<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<N, R2, C2, SB>, conjugate: impl Fn(N) -> N) -> N
|
||||
where
|
||||
SB: Storage<N, R2, C2>,
|
||||
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
|
||||
@ -283,27 +281,27 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
// because the `for` loop below won't be very efficient on those.
|
||||
if (R::is::<U2>() || R2::is::<U2>()) && (C::is::<U1>() || C2::is::<U1>()) {
|
||||
unsafe {
|
||||
let a = self.get_unchecked((0, 0)).conjugate() * *rhs.get_unchecked((0, 0));
|
||||
let b = self.get_unchecked((1, 0)).conjugate() * *rhs.get_unchecked((1, 0));
|
||||
let a = conjugate(*self.get_unchecked((0, 0))) * *rhs.get_unchecked((0, 0));
|
||||
let b = conjugate(*self.get_unchecked((1, 0))) * *rhs.get_unchecked((1, 0));
|
||||
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
if (R::is::<U3>() || R2::is::<U3>()) && (C::is::<U1>() || C2::is::<U1>()) {
|
||||
unsafe {
|
||||
let a = self.get_unchecked((0, 0)).conjugate() * *rhs.get_unchecked((0, 0));
|
||||
let b = self.get_unchecked((1, 0)).conjugate() * *rhs.get_unchecked((1, 0));
|
||||
let c = self.get_unchecked((2, 0)).conjugate() * *rhs.get_unchecked((2, 0));
|
||||
let a = conjugate(*self.get_unchecked((0, 0))) * *rhs.get_unchecked((0, 0));
|
||||
let b = conjugate(*self.get_unchecked((1, 0))) * *rhs.get_unchecked((1, 0));
|
||||
let c = conjugate(*self.get_unchecked((2, 0))) * *rhs.get_unchecked((2, 0));
|
||||
|
||||
return a + b + c;
|
||||
}
|
||||
}
|
||||
if (R::is::<U4>() || R2::is::<U4>()) && (C::is::<U1>() || C2::is::<U1>()) {
|
||||
unsafe {
|
||||
let mut a = self.get_unchecked((0, 0)).conjugate() * *rhs.get_unchecked((0, 0));
|
||||
let mut b = self.get_unchecked((1, 0)).conjugate() * *rhs.get_unchecked((1, 0));
|
||||
let c = self.get_unchecked((2, 0)).conjugate() * *rhs.get_unchecked((2, 0));
|
||||
let d = self.get_unchecked((3, 0)).conjugate() * *rhs.get_unchecked((3, 0));
|
||||
let mut a = conjugate(*self.get_unchecked((0, 0))) * *rhs.get_unchecked((0, 0));
|
||||
let mut b = conjugate(*self.get_unchecked((1, 0))) * *rhs.get_unchecked((1, 0));
|
||||
let c = conjugate(*self.get_unchecked((2, 0))) * *rhs.get_unchecked((2, 0));
|
||||
let d = conjugate(*self.get_unchecked((3, 0))) * *rhs.get_unchecked((3, 0));
|
||||
|
||||
a += c;
|
||||
b += d;
|
||||
@ -343,14 +341,14 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
acc7 = N::zero();
|
||||
|
||||
while self.nrows() - i >= 8 {
|
||||
acc0 += unsafe { self.get_unchecked((i + 0, j)).conjugate() * *rhs.get_unchecked((i + 0, j)) };
|
||||
acc1 += unsafe { self.get_unchecked((i + 1, j)).conjugate() * *rhs.get_unchecked((i + 1, j)) };
|
||||
acc2 += unsafe { self.get_unchecked((i + 2, j)).conjugate() * *rhs.get_unchecked((i + 2, j)) };
|
||||
acc3 += unsafe { self.get_unchecked((i + 3, j)).conjugate() * *rhs.get_unchecked((i + 3, j)) };
|
||||
acc4 += unsafe { self.get_unchecked((i + 4, j)).conjugate() * *rhs.get_unchecked((i + 4, j)) };
|
||||
acc5 += unsafe { self.get_unchecked((i + 5, j)).conjugate() * *rhs.get_unchecked((i + 5, j)) };
|
||||
acc6 += unsafe { self.get_unchecked((i + 6, j)).conjugate() * *rhs.get_unchecked((i + 6, j)) };
|
||||
acc7 += unsafe { self.get_unchecked((i + 7, j)).conjugate() * *rhs.get_unchecked((i + 7, j)) };
|
||||
acc0 += unsafe { conjugate(*self.get_unchecked((i + 0, j))) * *rhs.get_unchecked((i + 0, j)) };
|
||||
acc1 += unsafe { conjugate(*self.get_unchecked((i + 1, j))) * *rhs.get_unchecked((i + 1, j)) };
|
||||
acc2 += unsafe { conjugate(*self.get_unchecked((i + 2, j))) * *rhs.get_unchecked((i + 2, j)) };
|
||||
acc3 += unsafe { conjugate(*self.get_unchecked((i + 3, j))) * *rhs.get_unchecked((i + 3, j)) };
|
||||
acc4 += unsafe { conjugate(*self.get_unchecked((i + 4, j))) * *rhs.get_unchecked((i + 4, j)) };
|
||||
acc5 += unsafe { conjugate(*self.get_unchecked((i + 5, j))) * *rhs.get_unchecked((i + 5, j)) };
|
||||
acc6 += unsafe { conjugate(*self.get_unchecked((i + 6, j))) * *rhs.get_unchecked((i + 6, j)) };
|
||||
acc7 += unsafe { conjugate(*self.get_unchecked((i + 7, j))) * *rhs.get_unchecked((i + 7, j)) };
|
||||
i += 8;
|
||||
}
|
||||
|
||||
@ -360,17 +358,14 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
res += acc3 + acc7;
|
||||
|
||||
for k in i..self.nrows() {
|
||||
res += unsafe { self.get_unchecked((k, j)).conjugate() * *rhs.get_unchecked((k, j)) }
|
||||
res += unsafe { conjugate(*self.get_unchecked((k, j))) * *rhs.get_unchecked((k, j)) }
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S>
|
||||
where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
{
|
||||
|
||||
/// The dot product between two vectors or matrices (seen as vectors).
|
||||
///
|
||||
/// Note that this is **not** the matrix multiplication as in, e.g., numpy. For matrix
|
||||
@ -396,97 +391,36 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
SB: Storage<N, R2, C2>,
|
||||
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
|
||||
{
|
||||
assert!(
|
||||
self.nrows() == rhs.nrows(),
|
||||
"Dot product dimensions mismatch."
|
||||
);
|
||||
self.dotx(rhs, |e| e)
|
||||
}
|
||||
|
||||
// So we do some special cases for common fixed-size vectors of dimension lower than 8
|
||||
// because the `for` loop below won't be very efficient on those.
|
||||
if (R::is::<U2>() || R2::is::<U2>()) && (C::is::<U1>() || C2::is::<U1>()) {
|
||||
unsafe {
|
||||
let a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0));
|
||||
let b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0));
|
||||
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
if (R::is::<U3>() || R2::is::<U3>()) && (C::is::<U1>() || C2::is::<U1>()) {
|
||||
unsafe {
|
||||
let a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0));
|
||||
let b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0));
|
||||
let c = *self.get_unchecked((2, 0)) * *rhs.get_unchecked((2, 0));
|
||||
|
||||
return a + b + c;
|
||||
}
|
||||
}
|
||||
if (R::is::<U4>() || R2::is::<U4>()) && (C::is::<U1>() || C2::is::<U1>()) {
|
||||
unsafe {
|
||||
let mut a = *self.get_unchecked((0, 0)) * *rhs.get_unchecked((0, 0));
|
||||
let mut b = *self.get_unchecked((1, 0)) * *rhs.get_unchecked((1, 0));
|
||||
let c = *self.get_unchecked((2, 0)) * *rhs.get_unchecked((2, 0));
|
||||
let d = *self.get_unchecked((3, 0)) * *rhs.get_unchecked((3, 0));
|
||||
|
||||
a += c;
|
||||
b += d;
|
||||
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
|
||||
// All this is inspired from the "unrolled version" discussed in:
|
||||
// http://blog.theincredibleholk.org/blog/2012/12/10/optimizing-dot-product/
|
||||
//
|
||||
// And this comment from bluss:
|
||||
// https://users.rust-lang.org/t/how-to-zip-two-slices-efficiently/2048/12
|
||||
let mut res = N::zero();
|
||||
|
||||
// We have to define them outside of the loop (and not inside at first assignment)
|
||||
// otherwise vectorization won't kick in for some reason.
|
||||
let mut acc0;
|
||||
let mut acc1;
|
||||
let mut acc2;
|
||||
let mut acc3;
|
||||
let mut acc4;
|
||||
let mut acc5;
|
||||
let mut acc6;
|
||||
let mut acc7;
|
||||
|
||||
for j in 0..self.ncols() {
|
||||
let mut i = 0;
|
||||
|
||||
acc0 = N::zero();
|
||||
acc1 = N::zero();
|
||||
acc2 = N::zero();
|
||||
acc3 = N::zero();
|
||||
acc4 = N::zero();
|
||||
acc5 = N::zero();
|
||||
acc6 = N::zero();
|
||||
acc7 = N::zero();
|
||||
|
||||
while self.nrows() - i >= 8 {
|
||||
acc0 += unsafe { *self.get_unchecked((i + 0, j)) * *rhs.get_unchecked((i + 0, j)) };
|
||||
acc1 += unsafe { *self.get_unchecked((i + 1, j)) * *rhs.get_unchecked((i + 1, j)) };
|
||||
acc2 += unsafe { *self.get_unchecked((i + 2, j)) * *rhs.get_unchecked((i + 2, j)) };
|
||||
acc3 += unsafe { *self.get_unchecked((i + 3, j)) * *rhs.get_unchecked((i + 3, j)) };
|
||||
acc4 += unsafe { *self.get_unchecked((i + 4, j)) * *rhs.get_unchecked((i + 4, j)) };
|
||||
acc5 += unsafe { *self.get_unchecked((i + 5, j)) * *rhs.get_unchecked((i + 5, j)) };
|
||||
acc6 += unsafe { *self.get_unchecked((i + 6, j)) * *rhs.get_unchecked((i + 6, j)) };
|
||||
acc7 += unsafe { *self.get_unchecked((i + 7, j)) * *rhs.get_unchecked((i + 7, j)) };
|
||||
i += 8;
|
||||
}
|
||||
|
||||
res += acc0 + acc4;
|
||||
res += acc1 + acc5;
|
||||
res += acc2 + acc6;
|
||||
res += acc3 + acc7;
|
||||
|
||||
for k in i..self.nrows() {
|
||||
res += unsafe { *self.get_unchecked((k, j)) * *rhs.get_unchecked((k, j)) }
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
/// The dot product between two vectors or matrices (seen as vectors).
|
||||
///
|
||||
/// Note that this is **not** the matrix multiplication as in, e.g., numpy. For matrix
|
||||
/// multiplication, use one of: `.gemm`, `.mul_to`, `.mul`, the `*` operator.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Vector3, Matrix2x3};
|
||||
/// let vec1 = Vector3::new(1.0, 2.0, 3.0);
|
||||
/// let vec2 = Vector3::new(0.1, 0.2, 0.3);
|
||||
/// assert_eq!(vec1.dot(&vec2), 1.4);
|
||||
///
|
||||
/// let mat1 = Matrix2x3::new(1.0, 2.0, 3.0,
|
||||
/// 4.0, 5.0, 6.0);
|
||||
/// let mat2 = Matrix2x3::new(0.1, 0.2, 0.3,
|
||||
/// 0.4, 0.5, 0.6);
|
||||
/// assert_eq!(mat1.dot(&mat2), 9.1);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn dotc<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<N, R2, C2, SB>) -> N
|
||||
where
|
||||
N: Complex,
|
||||
SB: Storage<N, R2, C2>,
|
||||
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
|
||||
{
|
||||
self.dotx(rhs, Complex::conjugate)
|
||||
}
|
||||
|
||||
/// The dot product between the transpose of `self` and `rhs`.
|
||||
@ -643,40 +577,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * a * x + beta * self`, where `a` is a **symmetric** matrix, `x` a
|
||||
/// vector, and `alpha, beta` two scalars.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read. If `self` is read, only its lower-triangular part
|
||||
/// (including the diagonal) is actually read.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2, Vector2};
|
||||
/// let mat = Matrix2::new(1.0, 2.0,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector2::new(0.1, 0.2);
|
||||
/// vec1.gemv_symm(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
///
|
||||
///
|
||||
/// // The matrix upper-triangular elements can be garbage because it is never
|
||||
/// // read by this method. Therefore, it is not necessary for the caller to
|
||||
/// // fill the matrix struct upper-triangle.
|
||||
/// let mat = Matrix2::new(1.0, 9999999.9999999,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// vec1.gemv_symm(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn gemv_symm<D2: Dim, D3: Dim, SB, SC>(
|
||||
|
||||
#[inline(always)]
|
||||
fn xgemv<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
a: &SquareMatrix<N, D2, SB>,
|
||||
x: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
dotc: impl Fn(&DVectorSlice<N, SB::RStride, SB::CStride>, &DVectorSlice<N, SC::RStride, SC::CStride>) -> N,
|
||||
) where
|
||||
N: One,
|
||||
SB: Storage<N, D2, D2>,
|
||||
@ -687,83 +596,6 @@ where
|
||||
let dim2 = a.nrows();
|
||||
let dim3 = x.nrows();
|
||||
|
||||
assert!(
|
||||
a.is_square(),
|
||||
"Symmetric gemv: the input matrix must be square."
|
||||
);
|
||||
assert!(
|
||||
dim2 == dim3 && dim1 == dim2,
|
||||
"Symmetric gemv: dimensions mismatch."
|
||||
);
|
||||
|
||||
if dim2 == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: avoid bound checks.
|
||||
let col2 = a.column(0);
|
||||
let val = unsafe { *x.vget_unchecked(0) };
|
||||
self.axpy(alpha * val, &col2, beta);
|
||||
self[0] += alpha * x.rows_range(1..).dot(&a.slice_range(1.., 0));
|
||||
|
||||
for j in 1..dim2 {
|
||||
let col2 = a.column(j);
|
||||
let dot = x.rows_range(j..).dot(&col2.rows_range(j..));
|
||||
|
||||
let val;
|
||||
unsafe {
|
||||
val = *x.vget_unchecked(j);
|
||||
*self.vget_unchecked_mut(j) += alpha * dot;
|
||||
}
|
||||
self.rows_range_mut(j + 1..)
|
||||
.axpy(alpha * val, &col2.rows_range(j + 1..), N::one());
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * a * x + beta * self`, where `a` is a **symmetric** matrix, `x` a
|
||||
/// vector, and `alpha, beta` two scalars.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read. If `self` is read, only its lower-triangular part
|
||||
/// (including the diagonal) is actually read.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2, Vector2};
|
||||
/// let mat = Matrix2::new(1.0, 2.0,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector2::new(0.1, 0.2);
|
||||
/// vec1.gemv_symm(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
///
|
||||
///
|
||||
/// // The matrix upper-triangular elements can be garbage because it is never
|
||||
/// // read by this method. Therefore, it is not necessary for the caller to
|
||||
/// // fill the matrix struct upper-triangle.
|
||||
/// let mat = Matrix2::new(1.0, 9999999.9999999,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// vec1.gemv_symm(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn cgemv_symm<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
a: &SquareMatrix<N, D2, SB>,
|
||||
x: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
) where
|
||||
N: Complex,
|
||||
SB: Storage<N, D2, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<D, D2> + AreMultipliable<D2, D2, D3, U1>,
|
||||
{
|
||||
let dim1 = self.nrows();
|
||||
let dim2 = a.nrows();
|
||||
let dim3 = x.nrows();
|
||||
|
||||
assert!(
|
||||
a.is_square(),
|
||||
"Symmetric cgemv: the input matrix must be square."
|
||||
@ -781,11 +613,11 @@ where
|
||||
let col2 = a.column(0);
|
||||
let val = unsafe { *x.vget_unchecked(0) };
|
||||
self.axpy(alpha * val, &col2, beta);
|
||||
self[0] += alpha * a.slice_range(1.., 0).cdot(&x.rows_range(1..));
|
||||
self[0] += alpha * dotc(&a.slice_range(1.., 0), &x.rows_range(1..));
|
||||
|
||||
for j in 1..dim2 {
|
||||
let col2 = a.column(j);
|
||||
let dot = col2.rows_range(j..).cdot(&x.rows_range(j..));
|
||||
let dot = dotc(&col2.rows_range(j..), &x.rows_range(j..));
|
||||
|
||||
let val;
|
||||
unsafe {
|
||||
@ -797,6 +629,111 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * a * x + beta * self`, where `a` is a **symmetric** matrix, `x` a
|
||||
/// vector, and `alpha, beta` two scalars. DEPRECATED: use `sygemv` instead.
|
||||
#[inline]
|
||||
#[deprecated(note = "This is renamed `sygemv` to match the original BLAS terminology.")]
|
||||
pub fn gemv_symm<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
a: &SquareMatrix<N, D2, SB>,
|
||||
x: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
) where
|
||||
N: One,
|
||||
SB: Storage<N, D2, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<D, D2> + AreMultipliable<D2, D2, D3, U1>,
|
||||
{
|
||||
self.sygemv(alpha, a, x, beta)
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * a * x + beta * self`, where `a` is a **symmetric** matrix, `x` a
|
||||
/// vector, and `alpha, beta` two scalars.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read. If `self` is read, only its lower-triangular part
|
||||
/// (including the diagonal) is actually read.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2, Vector2};
|
||||
/// let mat = Matrix2::new(1.0, 2.0,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector2::new(0.1, 0.2);
|
||||
/// vec1.sygemv(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
///
|
||||
///
|
||||
/// // The matrix upper-triangular elements can be garbage because it is never
|
||||
/// // read by this method. Therefore, it is not necessary for the caller to
|
||||
/// // fill the matrix struct upper-triangle.
|
||||
/// let mat = Matrix2::new(1.0, 9999999.9999999,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// vec1.sygemv(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn sygemv<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
a: &SquareMatrix<N, D2, SB>,
|
||||
x: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
) where
|
||||
N: One,
|
||||
SB: Storage<N, D2, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<D, D2> + AreMultipliable<D2, D2, D3, U1>,
|
||||
{
|
||||
self.xgemv(alpha, a, x, beta, |a, b| a.dot(b))
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * a * x + beta * self`, where `a` is an **hermitian** matrix, `x` a
|
||||
/// vector, and `alpha, beta` two scalars.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read. If `self` is read, only its lower-triangular part
|
||||
/// (including the diagonal) is actually read.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2, Vector2};
|
||||
/// let mat = Matrix2::new(1.0, 2.0,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector2::new(0.1, 0.2);
|
||||
/// vec1.sygemv(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
///
|
||||
///
|
||||
/// // The matrix upper-triangular elements can be garbage because it is never
|
||||
/// // read by this method. Therefore, it is not necessary for the caller to
|
||||
/// // fill the matrix struct upper-triangle.
|
||||
/// let mat = Matrix2::new(1.0, 9999999.9999999,
|
||||
/// 2.0, 4.0);
|
||||
/// let mut vec1 = Vector2::new(1.0, 2.0);
|
||||
/// vec1.sygemv(10.0, &mat, &vec2, 5.0);
|
||||
/// assert_eq!(vec1, Vector2::new(10.0, 20.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn hegemv<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
a: &SquareMatrix<N, D2, SB>,
|
||||
x: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
) where
|
||||
N: Complex,
|
||||
SB: Storage<N, D2, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<D, D2> + AreMultipliable<D2, D2, D3, U1>,
|
||||
{
|
||||
self.xgemv(alpha, a, x, beta, |a, b| a.dotc(b))
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * a.transpose() * x + beta * self`, where `a` is a matrix, `x` a vector, and
|
||||
/// `alpha, beta` two scalars.
|
||||
///
|
||||
@ -855,33 +792,17 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: duplicate code
|
||||
impl<N, R1: Dim, C1: Dim, S: StorageMut<N, R1, C1>> Matrix<N, R1, C1, S>
|
||||
where N: Complex + Zero + ClosedAdd + ClosedMul
|
||||
where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
{
|
||||
/// Computes `self = alpha * x * y.transpose() + beta * self`.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2x3, Vector2, Vector3};
|
||||
/// let mut mat = Matrix2x3::repeat(4.0);
|
||||
/// let vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector3::new(0.1, 0.2, 0.3);
|
||||
/// let expected = vec1 * vec2.transpose() * 10.0 + mat * 5.0;
|
||||
///
|
||||
/// mat.ger(10.0, &vec1, &vec2, 5.0);
|
||||
/// assert_eq!(mat, expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn gerc<D2: Dim, D3: Dim, SB, SC>(
|
||||
#[inline(always)]
|
||||
fn gerx<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
x: &Vector<N, D2, SB>,
|
||||
y: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
conjugate: impl Fn(N) -> N,
|
||||
) where
|
||||
N: One,
|
||||
SB: Storage<N, D2>,
|
||||
@ -899,15 +820,11 @@ impl<N, R1: Dim, C1: Dim, S: StorageMut<N, R1, C1>> Matrix<N, R1, C1, S>
|
||||
|
||||
for j in 0..ncols1 {
|
||||
// FIXME: avoid bound checks.
|
||||
let val = unsafe { y.vget_unchecked(j).conjugate() };
|
||||
let val = unsafe { conjugate(*y.vget_unchecked(j)) };
|
||||
self.column_mut(j).axpy(alpha * val, x, beta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N, R1: Dim, C1: Dim, S: StorageMut<N, R1, C1>> Matrix<N, R1, C1, S>
|
||||
where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
{
|
||||
/// Computes `self = alpha * x * y.transpose() + beta * self`.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read.
|
||||
@ -937,20 +854,39 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
|
||||
{
|
||||
let (nrows1, ncols1) = self.shape();
|
||||
let dim2 = x.nrows();
|
||||
let dim3 = y.nrows();
|
||||
self.gerx(alpha, x, y, beta, |e| e)
|
||||
}
|
||||
|
||||
assert!(
|
||||
nrows1 == dim2 && ncols1 == dim3,
|
||||
"ger: dimensions mismatch."
|
||||
);
|
||||
|
||||
for j in 0..ncols1 {
|
||||
// FIXME: avoid bound checks.
|
||||
let val = unsafe { *y.vget_unchecked(j) };
|
||||
self.column_mut(j).axpy(alpha * val, x, beta);
|
||||
}
|
||||
/// Computes `self = alpha * x * y.transpose() + beta * self`.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2x3, Vector2, Vector3};
|
||||
/// let mut mat = Matrix2x3::repeat(4.0);
|
||||
/// let vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector3::new(0.1, 0.2, 0.3);
|
||||
/// let expected = vec1 * vec2.transpose() * 10.0 + mat * 5.0;
|
||||
///
|
||||
/// mat.ger(10.0, &vec1, &vec2, 5.0);
|
||||
/// assert_eq!(mat, expected);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn gerc<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
x: &Vector<N, D2, SB>,
|
||||
y: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
) where
|
||||
N: Complex,
|
||||
SB: Storage<N, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
|
||||
{
|
||||
self.gerx(alpha, x, y, beta, Complex::conjugate)
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * a * b + beta * self`, where `a, b, self` are matrices.
|
||||
@ -1146,6 +1082,42 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
impl<N, R1: Dim, C1: Dim, S: StorageMut<N, R1, C1>> Matrix<N, R1, C1, S>
|
||||
where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
{
|
||||
#[inline(always)]
|
||||
fn sygerx<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
x: &Vector<N, D2, SB>,
|
||||
y: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
conjugate: impl Fn(N) -> N,
|
||||
) where
|
||||
N: One,
|
||||
SB: Storage<N, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
|
||||
{
|
||||
let dim1 = self.nrows();
|
||||
let dim2 = x.nrows();
|
||||
let dim3 = y.nrows();
|
||||
|
||||
assert!(
|
||||
self.is_square(),
|
||||
"Symmetric ger: the input matrix must be square."
|
||||
);
|
||||
assert!(dim1 == dim2 && dim1 == dim3, "ger: dimensions mismatch.");
|
||||
|
||||
for j in 0..dim1 {
|
||||
let val = unsafe { conjugate(*y.vget_unchecked(j)) };
|
||||
let subdim = Dynamic::new(dim1 - j);
|
||||
// FIXME: avoid bound checks.
|
||||
self.generic_slice_mut((j, j), (subdim, U1)).axpy(
|
||||
alpha * val,
|
||||
&x.rows_range(j..),
|
||||
beta,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes `self = alpha * x * y.transpose() + beta * self`, where `self` is a **symmetric**
|
||||
/// matrix.
|
||||
///
|
||||
@ -1166,6 +1138,7 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
/// assert_eq!(mat.lower_triangle(), expected.lower_triangle());
|
||||
/// assert_eq!(mat.m12, 99999.99999); // This was untouched.
|
||||
#[inline]
|
||||
#[deprecated(note = "This is renamed `syger` to match the original BLAS terminology.")]
|
||||
pub fn ger_symm<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
@ -1178,26 +1151,77 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
|
||||
{
|
||||
let dim1 = self.nrows();
|
||||
let dim2 = x.nrows();
|
||||
let dim3 = y.nrows();
|
||||
self.syger(alpha, x, y, beta)
|
||||
}
|
||||
|
||||
assert!(
|
||||
self.is_square(),
|
||||
"Symmetric ger: the input matrix must be square."
|
||||
);
|
||||
assert!(dim1 == dim2 && dim1 == dim3, "ger: dimensions mismatch.");
|
||||
/// Computes `self = alpha * x * y.transpose() + beta * self`, where `self` is a **symmetric**
|
||||
/// matrix.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read. The result is symmetric. Only the lower-triangular
|
||||
/// (including the diagonal) part of `self` is read/written.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2, Vector2};
|
||||
/// let mut mat = Matrix2::identity();
|
||||
/// let vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector2::new(0.1, 0.2);
|
||||
/// let expected = vec1 * vec2.transpose() * 10.0 + mat * 5.0;
|
||||
/// mat.m12 = 99999.99999; // This component is on the upper-triangular part and will not be read/written.
|
||||
///
|
||||
/// mat.ger_symm(10.0, &vec1, &vec2, 5.0);
|
||||
/// assert_eq!(mat.lower_triangle(), expected.lower_triangle());
|
||||
/// assert_eq!(mat.m12, 99999.99999); // This was untouched.
|
||||
#[inline]
|
||||
pub fn syger<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
x: &Vector<N, D2, SB>,
|
||||
y: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
) where
|
||||
N: One,
|
||||
SB: Storage<N, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
|
||||
{
|
||||
self.sygerx(alpha, x, y, beta, |e| e)
|
||||
}
|
||||
|
||||
for j in 0..dim1 {
|
||||
let val = unsafe { *y.vget_unchecked(j) };
|
||||
let subdim = Dynamic::new(dim1 - j);
|
||||
// FIXME: avoid bound checks.
|
||||
self.generic_slice_mut((j, j), (subdim, U1)).axpy(
|
||||
alpha * val,
|
||||
&x.rows_range(j..),
|
||||
beta,
|
||||
);
|
||||
}
|
||||
/// Computes `self = alpha * x * y.transpose() + beta * self`, where `self` is a **symmetric**
|
||||
/// matrix.
|
||||
///
|
||||
/// If `beta` is zero, `self` is never read. The result is symmetric. Only the lower-triangular
|
||||
/// (including the diagonal) part of `self` is read/written.
|
||||
///
|
||||
/// # Examples:
|
||||
///
|
||||
/// ```
|
||||
/// # use nalgebra::{Matrix2, Vector2};
|
||||
/// let mut mat = Matrix2::identity();
|
||||
/// let vec1 = Vector2::new(1.0, 2.0);
|
||||
/// let vec2 = Vector2::new(0.1, 0.2);
|
||||
/// let expected = vec1 * vec2.transpose() * 10.0 + mat * 5.0;
|
||||
/// mat.m12 = 99999.99999; // This component is on the upper-triangular part and will not be read/written.
|
||||
///
|
||||
/// mat.ger_symm(10.0, &vec1, &vec2, 5.0);
|
||||
/// assert_eq!(mat.lower_triangle(), expected.lower_triangle());
|
||||
/// assert_eq!(mat.m12, 99999.99999); // This was untouched.
|
||||
#[inline]
|
||||
pub fn hegerc<D2: Dim, D3: Dim, SB, SC>(
|
||||
&mut self,
|
||||
alpha: N,
|
||||
x: &Vector<N, D2, SB>,
|
||||
y: &Vector<N, D3, SC>,
|
||||
beta: N,
|
||||
) where
|
||||
N: Complex,
|
||||
SB: Storage<N, D2>,
|
||||
SC: Storage<N, D3>,
|
||||
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
|
||||
{
|
||||
self.sygerx(alpha, x, y, beta, Complex::conjugate)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -914,9 +914,9 @@ impl<N: Scalar, D: Dim, S: StorageMut<N, D, D>> Matrix<N, D, D, S> {
|
||||
}
|
||||
|
||||
impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
/// Takes the conjugate and transposes `self` and store the result into `out`.
|
||||
/// Takes the adjoint (aka. conjugate-transpose) of `self` and store the result into `out`.
|
||||
#[inline]
|
||||
pub fn conjugate_transpose_to<R2, C2, SB>(&self, out: &mut Matrix<N, R2, C2, SB>)
|
||||
pub fn adjoint_to<R2, C2, SB>(&self, out: &mut Matrix<N, R2, C2, SB>)
|
||||
where
|
||||
R2: Dim,
|
||||
C2: Dim,
|
||||
@ -939,20 +939,41 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The conjugate transposition of `self`.
|
||||
/// The adjoint (aka. conjugate-transpose) of `self`.
|
||||
#[inline]
|
||||
pub fn conjugate_transpose(&self) -> MatrixMN<N, C, R>
|
||||
pub fn adjoint(&self) -> MatrixMN<N, C, R>
|
||||
where DefaultAllocator: Allocator<N, C, R> {
|
||||
let (nrows, ncols) = self.data.shape();
|
||||
|
||||
unsafe {
|
||||
let mut res: MatrixMN<_, C, R> = Matrix::new_uninitialized_generic(ncols, nrows);
|
||||
self.conjugate_transpose_to(&mut res);
|
||||
self.adjoint_to(&mut res);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the conjugate and transposes `self` and store the result into `out`.
|
||||
#[deprecated(note = "Renamed `self.adjoint_to(out)`.")]
|
||||
#[inline]
|
||||
pub fn conjugate_transpose_to<R2, C2, SB>(&self, out: &mut Matrix<N, R2, C2, SB>)
|
||||
where
|
||||
R2: Dim,
|
||||
C2: Dim,
|
||||
SB: StorageMut<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R, C2> + SameNumberOfColumns<C, R2>,
|
||||
{
|
||||
self.adjoint_to(out)
|
||||
}
|
||||
|
||||
/// The conjugate transposition of `self`.
|
||||
#[deprecated(note = "Renamed `self.adjoint()`.")]
|
||||
#[inline]
|
||||
pub fn conjugate_transpose(&self) -> MatrixMN<N, C, R>
|
||||
where DefaultAllocator: Allocator<N, C, R> {
|
||||
self.adjoint()
|
||||
}
|
||||
|
||||
/// The conjugate of `self`.
|
||||
#[inline]
|
||||
pub fn conjugate(&self) -> MatrixMN<N, R, C>
|
||||
@ -1088,13 +1109,13 @@ impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
tr
|
||||
}
|
||||
|
||||
/// The hermitian part of `self`, i.e., `0.5 * (self + self.conjugate_transpose())`.
|
||||
/// The hermitian part of `self`, i.e., `0.5 * (self + self.adjoint())`.
|
||||
#[inline]
|
||||
pub fn hermitian_part(&self) -> MatrixMN<N, D, D>
|
||||
where DefaultAllocator: Allocator<N, D, D> {
|
||||
assert!(self.is_square(), "Cannot compute the hermitian part of a non-square matrix.");
|
||||
|
||||
let mut tr = self.conjugate_transpose();
|
||||
let mut tr = self.adjoint();
|
||||
tr += self;
|
||||
tr *= ::convert::<_, N>(0.5);
|
||||
tr
|
||||
@ -1522,7 +1543,7 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
SB: Storage<N, R2, C2>,
|
||||
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
|
||||
{
|
||||
let prod = self.cdot(other);
|
||||
let prod = self.dotc(other);
|
||||
let n1 = self.norm();
|
||||
let n2 = other.norm();
|
||||
|
||||
@ -1593,7 +1614,7 @@ impl<N: Complex, D: Dim, S: Storage<N, D>> Unit<Vector<N, D, S>> {
|
||||
where
|
||||
DefaultAllocator: Allocator<N, D>,
|
||||
{
|
||||
let c_hang = self.cdot(rhs).real();
|
||||
let c_hang = self.dotc(rhs).real();
|
||||
|
||||
// self == other
|
||||
if c_hang.abs() >= N::Real::one() {
|
||||
|
@ -192,7 +192,7 @@ where DefaultAllocator: Allocator<N, R, C>
|
||||
|
||||
#[inline]
|
||||
fn inner_product(&self, other: &Self) -> N {
|
||||
self.cdot(other)
|
||||
self.dotc(other)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ impl<N: Complex> Norm<N> for EuclideanNorm {
|
||||
#[inline]
|
||||
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::Real
|
||||
where R: Dim, C: Dim, S: Storage<N, R, C> {
|
||||
m.cdot(m).real().sqrt()
|
||||
m.dotc(m).real().sqrt()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -101,7 +101,7 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
|
||||
for i in 0..self.ncols() {
|
||||
let col = self.column(i);
|
||||
res += col.cdot(&col).real()
|
||||
res += col.dotc(&col).real()
|
||||
}
|
||||
|
||||
res
|
||||
|
163
src/base/ops.rs
163
src/base/ops.rs
@ -13,7 +13,7 @@ use base::constraint::{
|
||||
};
|
||||
use base::dimension::{Dim, DimMul, DimName, DimProd};
|
||||
use base::storage::{ContiguousStorageMut, Storage, StorageMut};
|
||||
use base::{DefaultAllocator, Matrix, MatrixMN, MatrixN, MatrixSum, Scalar};
|
||||
use base::{DefaultAllocator, Matrix, MatrixMN, MatrixN, MatrixSum, Scalar, VectorSliceN};
|
||||
|
||||
/*
|
||||
*
|
||||
@ -629,13 +629,28 @@ where
|
||||
res
|
||||
}
|
||||
|
||||
/// Equivalent to `self.transpose() * rhs` but stores the result into `out` to avoid
|
||||
/// allocations.
|
||||
/// Equivalent to `self.adjoint() * rhs`.
|
||||
#[inline]
|
||||
pub fn tr_mul_to<R2: Dim, C2: Dim, SB, R3: Dim, C3: Dim, SC>(
|
||||
pub fn ad_mul<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<N, R2, C2, SB>) -> MatrixMN<N, C1, C2>
|
||||
where
|
||||
N: Complex,
|
||||
SB: Storage<N, R2, C2>,
|
||||
DefaultAllocator: Allocator<N, C1, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R1, R2>,
|
||||
{
|
||||
let mut res =
|
||||
unsafe { Matrix::new_uninitialized_generic(self.data.shape().1, rhs.data.shape().1) };
|
||||
|
||||
self.ad_mul_to(rhs, &mut res);
|
||||
res
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn xx_mul_to<R2: Dim, C2: Dim, SB, R3: Dim, C3: Dim, SC>(
|
||||
&self,
|
||||
rhs: &Matrix<N, R2, C2, SB>,
|
||||
out: &mut Matrix<N, R3, C3, SC>,
|
||||
dot: impl Fn(&VectorSliceN<N, R1, SA::RStride, SA::CStride>, &VectorSliceN<N, R2, SB::RStride, SB::CStride>) -> N,
|
||||
) where
|
||||
SB: Storage<N, R2, C2>,
|
||||
SC: StorageMut<N, R3, C3>,
|
||||
@ -656,12 +671,43 @@ where
|
||||
|
||||
for i in 0..ncols1 {
|
||||
for j in 0..ncols2 {
|
||||
let dot = self.column(i).dot(&rhs.column(j));
|
||||
let dot = dot(&self.column(i), &rhs.column(j));
|
||||
unsafe { *out.get_unchecked_mut((i, j)) = dot };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Equivalent to `self.transpose() * rhs` but stores the result into `out` to avoid
|
||||
/// allocations.
|
||||
#[inline]
|
||||
pub fn tr_mul_to<R2: Dim, C2: Dim, SB, R3: Dim, C3: Dim, SC>(
|
||||
&self,
|
||||
rhs: &Matrix<N, R2, C2, SB>,
|
||||
out: &mut Matrix<N, R3, C3, SC>,
|
||||
) where
|
||||
SB: Storage<N, R2, C2>,
|
||||
SC: StorageMut<N, R3, C3>,
|
||||
ShapeConstraint: SameNumberOfRows<R1, R2> + DimEq<C1, R3> + DimEq<C2, C3>,
|
||||
{
|
||||
self.xx_mul_to(rhs, out, |a, b| a.dot(b))
|
||||
}
|
||||
|
||||
/// Equivalent to `self.adjoint() * rhs` but stores the result into `out` to avoid
|
||||
/// allocations.
|
||||
#[inline]
|
||||
pub fn ad_mul_to<R2: Dim, C2: Dim, SB, R3: Dim, C3: Dim, SC>(
|
||||
&self,
|
||||
rhs: &Matrix<N, R2, C2, SB>,
|
||||
out: &mut Matrix<N, R3, C3, SC>,
|
||||
) where
|
||||
N: Complex,
|
||||
SB: Storage<N, R2, C2>,
|
||||
SC: StorageMut<N, R3, C3>,
|
||||
ShapeConstraint: SameNumberOfRows<R1, R2> + DimEq<C1, R3> + DimEq<C2, C3>,
|
||||
{
|
||||
self.xx_mul_to(rhs, out, |a, b| a.dotc(b))
|
||||
}
|
||||
|
||||
/// Equivalent to `self * rhs` but stores the result into `out` to avoid allocations.
|
||||
#[inline]
|
||||
pub fn mul_to<R2: Dim, C2: Dim, SB, R3: Dim, C3: Dim, SC>(
|
||||
@ -760,35 +806,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: avoid code duplication.
|
||||
impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
/// Returns the absolute value of the component with the largest absolute value.
|
||||
#[inline]
|
||||
pub fn camax(&self) -> N::Real {
|
||||
let mut max = N::Real::zero();
|
||||
impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
#[inline(always)]
|
||||
fn xcmp<N2>(&self, abs: impl Fn(N) -> N2, cmp: impl Fn(N2, N2) -> bool) -> N2
|
||||
where N2: Scalar + PartialOrd + Zero {
|
||||
let mut max = N2::zero();
|
||||
|
||||
for e in self.iter() {
|
||||
let ae = e.asum();
|
||||
let ae = abs(*e);
|
||||
|
||||
if ae > max {
|
||||
max = ae;
|
||||
}
|
||||
}
|
||||
|
||||
max
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
/// Returns the absolute value of the component with the largest absolute value.
|
||||
#[inline]
|
||||
pub fn amax(&self) -> N {
|
||||
let mut max = N::zero();
|
||||
|
||||
for e in self.iter() {
|
||||
let ae = e.abs();
|
||||
|
||||
if ae > max {
|
||||
if cmp(ae, max) {
|
||||
max = ae;
|
||||
}
|
||||
}
|
||||
@ -796,61 +823,45 @@ impl<N: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<N, R, C>> Matri
|
||||
max
|
||||
}
|
||||
|
||||
/// Returns the absolute value of the component with the smallest absolute value.
|
||||
/// Returns the absolute value of the component with the largest absolute value.
|
||||
#[inline]
|
||||
pub fn amin(&self) -> N {
|
||||
let mut it = self.iter();
|
||||
let mut min = it
|
||||
.next()
|
||||
.expect("amin: empty matrices not supported.")
|
||||
.abs();
|
||||
pub fn amax(&self) -> N
|
||||
where N: PartialOrd + Signed {
|
||||
self.xcmp(|e| e.abs(), |a, b| a > b)
|
||||
}
|
||||
|
||||
for e in it {
|
||||
let ae = e.abs();
|
||||
|
||||
if ae < min {
|
||||
min = ae;
|
||||
}
|
||||
}
|
||||
|
||||
min
|
||||
/// Returns the the 1-norm of the complex component with the largest 1-norm.
|
||||
#[inline]
|
||||
pub fn camax(&self) -> N::Real
|
||||
where N: Complex {
|
||||
self.xcmp(|e| e.norm1(), |a, b| a > b)
|
||||
}
|
||||
|
||||
/// Returns the component with the largest value.
|
||||
#[inline]
|
||||
pub fn max(&self) -> N {
|
||||
let mut it = self.iter();
|
||||
let mut max = it
|
||||
.next()
|
||||
.expect("max: empty matrices not supported.");
|
||||
pub fn max(&self) -> N
|
||||
where N: PartialOrd + Signed {
|
||||
self.xcmp(|e| e, |a, b| a > b)
|
||||
}
|
||||
|
||||
for e in it {
|
||||
let ae = e;
|
||||
/// Returns the absolute value of the component with the smallest absolute value.
|
||||
#[inline]
|
||||
pub fn amin(&self) -> N
|
||||
where N: PartialOrd + Signed {
|
||||
self.xcmp(|e| e.abs(), |a, b| a < b)
|
||||
}
|
||||
|
||||
if ae > max {
|
||||
max = ae;
|
||||
}
|
||||
}
|
||||
|
||||
*max
|
||||
/// Returns the the 1-norm of the complex component with the smallest 1-norm.
|
||||
#[inline]
|
||||
pub fn camin(&self) -> N::Real
|
||||
where N: Complex {
|
||||
self.xcmp(|e| e.norm1(), |a, b| a < b)
|
||||
}
|
||||
|
||||
/// Returns the component with the smallest value.
|
||||
#[inline]
|
||||
pub fn min(&self) -> N {
|
||||
let mut it = self.iter();
|
||||
let mut min = it
|
||||
.next()
|
||||
.expect("min: empty matrices not supported.");
|
||||
|
||||
for e in it {
|
||||
let ae = e;
|
||||
|
||||
if ae < min {
|
||||
min = ae;
|
||||
}
|
||||
}
|
||||
|
||||
*min
|
||||
pub fn min(&self) -> N
|
||||
where N: PartialOrd + Signed {
|
||||
self.xcmp(|e| e, |a, b| a < b)
|
||||
}
|
||||
}
|
||||
}
|
@ -97,8 +97,7 @@ impl<N: Complex, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
|
||||
N::Epsilon: Copy,
|
||||
DefaultAllocator: Allocator<N, R, C> + Allocator<N, C, C>,
|
||||
{
|
||||
// FIXME: add a conjugate-transpose-mul
|
||||
(self.conjugate().tr_mul(self)).is_identity(eps)
|
||||
(self.ad_mul(self)).is_identity(eps)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ where DefaultAllocator: Allocator<N, D, D>
|
||||
/// random reals generators.
|
||||
pub fn new<Rand: FnMut() -> N>(dim: D, mut rand: Rand) -> Self {
|
||||
let mut m = RandomOrthogonal::new(dim, || rand()).unwrap();
|
||||
let mt = m.conjugate_transpose();
|
||||
let mt = m.adjoint();
|
||||
|
||||
for i in 0..dim.value() {
|
||||
let mut col = m.column_mut(i);
|
||||
|
@ -35,7 +35,7 @@ impl<N: Complex, D: Dim, S: Storage<N, D>> Reflection<N, D, S> {
|
||||
D: DimName,
|
||||
DefaultAllocator: Allocator<N, D>,
|
||||
{
|
||||
let bias = axis.cdot(&pt.coords);
|
||||
let bias = axis.dotc(&pt.coords);
|
||||
Self::new(axis, bias)
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ impl<N: Complex, D: Dim, S: Storage<N, D>> Reflection<N, D, S> {
|
||||
// dot product, and then mutably. Somehow, this allows significantly
|
||||
// better optimizations of the dot product from the compiler.
|
||||
let m_two: N = ::convert(-2.0f64);
|
||||
let factor = (self.axis.cdot(&rhs.column(i)) - self.bias) * m_two;
|
||||
let factor = (self.axis.dotc(&rhs.column(i)) - self.bias) * m_two;
|
||||
rhs.column_mut(i).axpy(factor, &self.axis, N::one());
|
||||
}
|
||||
}
|
||||
@ -73,7 +73,7 @@ impl<N: Complex, D: Dim, S: Storage<N, D>> Reflection<N, D, S> {
|
||||
// dot product, and then mutably. Somehow, this allows significantly
|
||||
// better optimizations of the dot product from the compiler.
|
||||
let m_two = sign.scale(::convert(-2.0f64));
|
||||
let factor = (self.axis.cdot(&rhs.column(i)) - self.bias) * m_two;
|
||||
let factor = (self.axis.dotc(&rhs.column(i)) - self.bias) * m_two;
|
||||
rhs.column_mut(i).axpy(factor, &self.axis, sign);
|
||||
}
|
||||
}
|
||||
|
@ -170,6 +170,7 @@ pub use alga::general::{Id, Real, Complex};
|
||||
/// Gets the ubiquitous multiplicative identity element.
|
||||
///
|
||||
/// Same as `Id::new()`.
|
||||
#[deprecated(note = "use `Id::new()` instead.")]
|
||||
#[inline]
|
||||
pub fn id() -> Id {
|
||||
Id::new()
|
||||
@ -416,6 +417,7 @@ pub fn partial_sort2<'a, T: PartialOrd>(a: &'a T, b: &'a T) -> Option<(&'a T, &'
|
||||
/// # See also:
|
||||
///
|
||||
/// * [`inverse`](fn.inverse.html)
|
||||
#[deprecated(note = "use the `.try_inverse()` method instead")]
|
||||
#[inline]
|
||||
pub fn try_inverse<M: AlgaSquareMatrix>(m: &M) -> Option<M> {
|
||||
m.try_inverse()
|
||||
@ -426,6 +428,7 @@ pub fn try_inverse<M: AlgaSquareMatrix>(m: &M) -> Option<M> {
|
||||
/// # See also:
|
||||
///
|
||||
/// * [`try_inverse`](fn.try_inverse.html)
|
||||
#[deprecated(note = "use the `.inverse()` method instead")]
|
||||
#[inline]
|
||||
pub fn inverse<M: TwoSidedInverse<Multiplicative>>(m: &M) -> M {
|
||||
m.two_sided_inverse()
|
||||
|
@ -121,7 +121,7 @@ where DefaultAllocator: Allocator<N, D, D>
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let _ = self.chol.solve_lower_triangular_mut(b);
|
||||
let _ = self.chol.conjugate().tr_solve_lower_triangular_mut(b);
|
||||
let _ = self.chol.ad_solve_lower_triangular_mut(b);
|
||||
}
|
||||
|
||||
/// Returns the solution of the system `self * x = b` where `self` is the decomposed matrix and
|
||||
|
@ -30,6 +30,6 @@ pub use self::lu::*;
|
||||
pub use self::permutation_sequence::*;
|
||||
pub use self::qr::*;
|
||||
pub use self::schur::*;
|
||||
//pub use self::svd::*;
|
||||
pub use self::svd::*;
|
||||
pub use self::symmetric_eigen::*;
|
||||
pub use self::symmetric_tridiagonal::*;
|
||||
|
@ -4,7 +4,7 @@ use base::allocator::Allocator;
|
||||
use base::constraint::{SameNumberOfRows, ShapeConstraint};
|
||||
use base::dimension::{Dim, U1};
|
||||
use base::storage::{Storage, StorageMut};
|
||||
use base::{DefaultAllocator, Matrix, MatrixMN, SquareMatrix, Vector};
|
||||
use base::{DefaultAllocator, Matrix, MatrixMN, SquareMatrix, Vector, DVectorSlice};
|
||||
|
||||
impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
/// Computes the solution of the linear system `self . x = b` where `x` is the unknown and only
|
||||
@ -180,10 +180,9 @@ impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
|
||||
/*
|
||||
*
|
||||
* Transpose versions
|
||||
* Transpose and adjoint versions
|
||||
*
|
||||
*/
|
||||
|
||||
/// Computes the solution of the linear system `self.transpose() . x = b` where `x` is the unknown and only
|
||||
/// the lower-triangular part of `self` (including the diagonal) is considered not-zero.
|
||||
#[inline]
|
||||
@ -191,10 +190,10 @@ impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
&self,
|
||||
b: &Matrix<N, R2, C2, S2>,
|
||||
) -> Option<MatrixMN<N, R2, C2>>
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
DefaultAllocator: Allocator<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
DefaultAllocator: Allocator<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let mut res = b.clone_owned();
|
||||
if self.tr_solve_lower_triangular_mut(&mut res) {
|
||||
@ -211,10 +210,10 @@ impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
&self,
|
||||
b: &Matrix<N, R2, C2, S2>,
|
||||
) -> Option<MatrixMN<N, R2, C2>>
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
DefaultAllocator: Allocator<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
DefaultAllocator: Allocator<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let mut res = b.clone_owned();
|
||||
if self.tr_solve_upper_triangular_mut(&mut res) {
|
||||
@ -230,14 +229,14 @@ impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
&self,
|
||||
b: &mut Matrix<N, R2, C2, S2>,
|
||||
) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let cols = b.ncols();
|
||||
|
||||
for i in 0..cols {
|
||||
if !self.tr_solve_lower_triangular_vector_mut(&mut b.column_mut(i)) {
|
||||
if !self.xx_solve_lower_triangular_vector_mut(&mut b.column_mut(i), |e| e, |a, b| a.dot(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -245,46 +244,20 @@ impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
true
|
||||
}
|
||||
|
||||
fn tr_solve_lower_triangular_vector_mut<R2: Dim, S2>(&self, b: &mut Vector<N, R2, S2>) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, U1>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let dim = self.nrows();
|
||||
|
||||
for i in (0..dim).rev() {
|
||||
let dot = self.slice_range(i + 1.., i).dot(&b.slice_range(i + 1.., 0));
|
||||
|
||||
unsafe {
|
||||
let b_i = b.vget_unchecked_mut(i);
|
||||
|
||||
let diag = *self.get_unchecked((i, i));
|
||||
|
||||
if diag.is_zero() {
|
||||
return false;
|
||||
}
|
||||
|
||||
*b_i = (*b_i - dot) / diag;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Solves the linear system `self.transpose() . x = b` where `x` is the unknown and only the
|
||||
/// upper-triangular part of `self` (including the diagonal) is considered not-zero.
|
||||
pub fn tr_solve_upper_triangular_mut<R2: Dim, C2: Dim, S2>(
|
||||
&self,
|
||||
b: &mut Matrix<N, R2, C2, S2>,
|
||||
) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let cols = b.ncols();
|
||||
|
||||
for i in 0..cols {
|
||||
if !self.tr_solve_upper_triangular_vector_mut(&mut b.column_mut(i)) {
|
||||
if !self.xx_solve_upper_triangular_vector_mut(&mut b.column_mut(i), |e| e, |a, b| a.dot(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -292,19 +265,140 @@ impl<N: Complex, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
|
||||
true
|
||||
}
|
||||
|
||||
fn tr_solve_upper_triangular_vector_mut<R2: Dim, S2>(&self, b: &mut Vector<N, R2, S2>) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, U1>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
/// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only
|
||||
/// the lower-triangular part of `self` (including the diagonal) is considered not-zero.
|
||||
#[inline]
|
||||
pub fn ad_solve_lower_triangular<R2: Dim, C2: Dim, S2>(
|
||||
&self,
|
||||
b: &Matrix<N, R2, C2, S2>,
|
||||
) -> Option<MatrixMN<N, R2, C2>>
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
DefaultAllocator: Allocator<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let mut res = b.clone_owned();
|
||||
if self.ad_solve_lower_triangular_mut(&mut res) {
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the solution of the linear system `self.adjoint() . x = b` where `x` is the unknown and only
|
||||
/// the upper-triangular part of `self` (including the diagonal) is considered not-zero.
|
||||
#[inline]
|
||||
pub fn ad_solve_upper_triangular<R2: Dim, C2: Dim, S2>(
|
||||
&self,
|
||||
b: &Matrix<N, R2, C2, S2>,
|
||||
) -> Option<MatrixMN<N, R2, C2>>
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
DefaultAllocator: Allocator<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let mut res = b.clone_owned();
|
||||
if self.ad_solve_upper_triangular_mut(&mut res) {
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Solves the linear system `self.adjoint() . x = b` where `x` is the unknown and only the
|
||||
/// lower-triangular part of `self` (including the diagonal) is considered not-zero.
|
||||
pub fn ad_solve_lower_triangular_mut<R2: Dim, C2: Dim, S2>(
|
||||
&self,
|
||||
b: &mut Matrix<N, R2, C2, S2>,
|
||||
) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let cols = b.ncols();
|
||||
|
||||
for i in 0..cols {
|
||||
if !self.xx_solve_lower_triangular_vector_mut(&mut b.column_mut(i), |e| e.conjugate(), |a, b| a.dotc(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Solves the linear system `self.adjoint() . x = b` where `x` is the unknown and only the
|
||||
/// upper-triangular part of `self` (including the diagonal) is considered not-zero.
|
||||
pub fn ad_solve_upper_triangular_mut<R2: Dim, C2: Dim, S2>(
|
||||
&self,
|
||||
b: &mut Matrix<N, R2, C2, S2>,
|
||||
) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, C2>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let cols = b.ncols();
|
||||
|
||||
for i in 0..cols {
|
||||
if !self.xx_solve_upper_triangular_vector_mut(&mut b.column_mut(i), |e| e.conjugate(), |a, b| a.dotc(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
#[inline(always)]
|
||||
fn xx_solve_lower_triangular_vector_mut<R2: Dim, S2>(
|
||||
&self,
|
||||
b: &mut Vector<N, R2, S2>,
|
||||
conjugate: impl Fn(N) -> N,
|
||||
dot: impl Fn(&DVectorSlice<N, S::RStride, S::CStride>, &DVectorSlice<N, S2::RStride, S2::CStride>) -> N,
|
||||
) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, U1>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let dim = self.nrows();
|
||||
|
||||
for i in (0..dim).rev() {
|
||||
let dot = dot(&self.slice_range(i + 1.., i), &b.slice_range(i + 1.., 0));
|
||||
|
||||
unsafe {
|
||||
let b_i = b.vget_unchecked_mut(i);
|
||||
|
||||
let diag = conjugate(*self.get_unchecked((i, i)));
|
||||
|
||||
if diag.is_zero() {
|
||||
return false;
|
||||
}
|
||||
|
||||
*b_i = (*b_i - dot) / diag;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn xx_solve_upper_triangular_vector_mut<R2: Dim, S2>(
|
||||
&self,
|
||||
b: &mut Vector<N, R2, S2>,
|
||||
conjugate: impl Fn(N) -> N,
|
||||
dot: impl Fn(&DVectorSlice<N, S::RStride, S::CStride>, &DVectorSlice<N, S2::RStride, S2::CStride>) -> N,
|
||||
) -> bool
|
||||
where
|
||||
S2: StorageMut<N, R2, U1>,
|
||||
ShapeConstraint: SameNumberOfRows<R2, D>,
|
||||
{
|
||||
let dim = self.nrows();
|
||||
|
||||
for i in 0..dim {
|
||||
let dot = self.slice_range(..i, i).dot(&b.slice_range(..i, 0));
|
||||
let dot = dot(&self.slice_range(..i, i), &b.slice_range(..i, 0));
|
||||
|
||||
unsafe {
|
||||
let b_i = b.vget_unchecked_mut(i);
|
||||
let diag = *self.get_unchecked((i, i));
|
||||
let diag = conjugate(*self.get_unchecked((i, i)));
|
||||
|
||||
if diag.is_zero() {
|
||||
return false;
|
||||
|
@ -496,7 +496,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
self.recompose().map(|m| m.conjugate_transpose())
|
||||
self.recompose().map(|m| m.adjoint())
|
||||
}
|
||||
}
|
||||
|
||||
@ -521,7 +521,7 @@ where
|
||||
else {
|
||||
match (&self.u, &self.v_t) {
|
||||
(Some(u), Some(v_t)) => {
|
||||
let mut ut_b = u.conjugate().tr_mul(b);
|
||||
let mut ut_b = u.ad_mul(b);
|
||||
|
||||
for j in 0..ut_b.ncols() {
|
||||
let mut col = ut_b.column_mut(j);
|
||||
@ -536,7 +536,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
Ok(v_t.conjugate().tr_mul(&ut_b))
|
||||
Ok(v_t.ad_mul(&ut_b))
|
||||
}
|
||||
(None, None) => Err("SVD solve: U and V^t have not been computed."),
|
||||
(None, _) => Err("SVD solve: U has not been computed."),
|
||||
|
@ -75,13 +75,13 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimDiff<D, U1>>
|
||||
if not_zero {
|
||||
let mut p = p.rows_range_mut(i..);
|
||||
|
||||
p.cgemv_symm(::convert(2.0), &m, &axis, N::zero());
|
||||
let dot = axis.cdot(&p);
|
||||
p.hegemv(::convert(2.0), &m, &axis, N::zero());
|
||||
let dot = axis.dotc(&p);
|
||||
|
||||
// p.axpy(-dot, &axis.conjugate(), N::one());
|
||||
m.ger_symm(-N::one(), &p, &axis.conjugate(), N::one());
|
||||
m.ger_symm(-N::one(), &axis, &p.conjugate(), N::one());
|
||||
m.ger_symm(dot * ::convert(2.0), &axis, &axis.conjugate(), N::one());
|
||||
m.hegerc(-N::one(), &p, &axis, N::one());
|
||||
m.hegerc(-N::one(), &axis, &p, N::one());
|
||||
m.hegerc(dot * ::convert(2.0), &axis, &axis, N::one());
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +142,7 @@ where DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimDiff<D, U1>>
|
||||
self.tri[(i, i + 1)] = val;
|
||||
}
|
||||
|
||||
&q * self.tri * q.conjugate_transpose()
|
||||
&q * self.tri * q.adjoint()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,14 +19,14 @@ quickcheck! {
|
||||
let mut y2 = y1.clone();
|
||||
|
||||
y1.gemv(alpha, &a, &x, beta);
|
||||
y2.gemv_symm(alpha, &a.lower_triangle(), &x, beta);
|
||||
y2.sygemv(alpha, &a.lower_triangle(), &x, beta);
|
||||
|
||||
if !relative_eq!(y1, y2, epsilon = 1.0e-10) {
|
||||
return false;
|
||||
}
|
||||
|
||||
y1.gemv(alpha, &a, &x, 0.0);
|
||||
y2.gemv_symm(alpha, &a.lower_triangle(), &x, 0.0);
|
||||
y2.sygemv(alpha, &a.lower_triangle(), &x, 0.0);
|
||||
|
||||
relative_eq!(y1, y2, epsilon = 1.0e-10)
|
||||
}
|
||||
@ -61,14 +61,14 @@ quickcheck! {
|
||||
let y = DVector::new_random(n);
|
||||
|
||||
a1.ger(alpha, &x, &y, beta);
|
||||
a2.ger_symm(alpha, &x, &y, beta);
|
||||
a2.syger(alpha, &x, &y, beta);
|
||||
|
||||
if !relative_eq!(a1.lower_triangle(), a2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
a1.ger(alpha, &x, &y, 0.0);
|
||||
a2.ger_symm(alpha, &x, &y, 0.0);
|
||||
a2.syger(alpha, &x, &y, 0.0);
|
||||
|
||||
relative_eq!(a1.lower_triangle(), a2)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ macro_rules! gen_tests(
|
||||
fn cholesky(n: usize) -> bool {
|
||||
let m = RandomSDP::new(Dynamic::new(n.max(1).min(50)), || random::<$scalar>().0).unwrap();
|
||||
let l = m.clone().cholesky().unwrap().unpack();
|
||||
relative_eq!(m, &l * l.conjugate_transpose(), epsilon = 1.0e-7)
|
||||
relative_eq!(m, &l * l.adjoint(), epsilon = 1.0e-7)
|
||||
}
|
||||
|
||||
fn cholesky_static(_m: RandomSDP<f64, U4>) -> bool {
|
||||
@ -24,7 +24,7 @@ macro_rules! gen_tests(
|
||||
let chol = m.cholesky().unwrap();
|
||||
let l = chol.unpack();
|
||||
|
||||
if !relative_eq!(m, &l * l.conjugate_transpose(), epsilon = 1.0e-7) {
|
||||
if !relative_eq!(m, &l * l.adjoint(), epsilon = 1.0e-7) {
|
||||
false
|
||||
}
|
||||
else {
|
||||
|
@ -27,21 +27,21 @@ macro_rules! gen_tests(
|
||||
|
||||
let hess = m.clone().hessenberg();
|
||||
let (p, h) = hess.unpack();
|
||||
relative_eq!(m, &p * h * p.conjugate_transpose(), epsilon = 1.0e-7)
|
||||
relative_eq!(m, &p * h * p.adjoint(), epsilon = 1.0e-7)
|
||||
}
|
||||
|
||||
fn hessenberg_static_mat2(m: Matrix2<$scalar>) -> bool {
|
||||
let m = m.map(|e| e.0);
|
||||
let hess = m.hessenberg();
|
||||
let (p, h) = hess.unpack();
|
||||
relative_eq!(m, p * h * p.conjugate_transpose(), epsilon = 1.0e-7)
|
||||
relative_eq!(m, p * h * p.adjoint(), epsilon = 1.0e-7)
|
||||
}
|
||||
|
||||
fn hessenberg_static(m: Matrix4<$scalar>) -> bool {
|
||||
let m = m.map(|e| e.0);
|
||||
let hess = m.hessenberg();
|
||||
let (p, h) = hess.unpack();
|
||||
relative_eq!(m, p * h * p.conjugate_transpose(), epsilon = 1.0e-7)
|
||||
relative_eq!(m, p * h * p.adjoint(), epsilon = 1.0e-7)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,21 +31,21 @@ mod quickcheck_tests {
|
||||
|
||||
let (vecs, vals) = m.clone().schur().unpack();
|
||||
|
||||
if !relative_eq!(&vecs * &vals * vecs.conjugate_transpose(), m, epsilon = 1.0e-7) {
|
||||
println!("{:.5}{:.5}", m, &vecs * &vals * vecs.conjugate_transpose());
|
||||
if !relative_eq!(&vecs * &vals * vecs.adjoint(), m, epsilon = 1.0e-7) {
|
||||
println!("{:.5}{:.5}", m, &vecs * &vals * vecs.adjoint());
|
||||
}
|
||||
|
||||
relative_eq!(&vecs * vals * vecs.conjugate_transpose(), m, epsilon = 1.0e-7)
|
||||
relative_eq!(&vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7)
|
||||
}
|
||||
|
||||
fn schur_static_mat2(m: Matrix2<$scalar>) -> bool {
|
||||
let m = m.map(|e| e.0);
|
||||
let (vecs, vals) = m.clone().schur().unpack();
|
||||
|
||||
let ok = relative_eq!(vecs * vals * vecs.conjugate_transpose(), m, epsilon = 1.0e-7);
|
||||
let ok = relative_eq!(vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7);
|
||||
if !ok {
|
||||
println!("Vecs: {:.5} Vals: {:.5}", vecs, vals);
|
||||
println!("Reconstruction:{}{}", m, &vecs * &vals * vecs.conjugate_transpose());
|
||||
println!("Reconstruction:{}{}", m, &vecs * &vals * vecs.adjoint());
|
||||
}
|
||||
ok
|
||||
}
|
||||
@ -54,10 +54,10 @@ mod quickcheck_tests {
|
||||
let m = m.map(|e| e.0);
|
||||
let (vecs, vals) = m.clone().schur().unpack();
|
||||
|
||||
let ok = relative_eq!(vecs * vals * vecs.conjugate_transpose(), m, epsilon = 1.0e-7);
|
||||
let ok = relative_eq!(vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7);
|
||||
if !ok {
|
||||
println!("Vecs: {:.5} Vals: {:.5}", vecs, vals);
|
||||
println!("{:.5}{:.5}", m, &vecs * &vals * vecs.conjugate_transpose());
|
||||
println!("{:.5}{:.5}", m, &vecs * &vals * vecs.adjoint());
|
||||
}
|
||||
ok
|
||||
}
|
||||
@ -66,9 +66,9 @@ mod quickcheck_tests {
|
||||
let m = m.map(|e| e.0);
|
||||
let (vecs, vals) = m.clone().schur().unpack();
|
||||
|
||||
let ok = relative_eq!(vecs * vals * vecs.conjugate_transpose(), m, epsilon = 1.0e-7);
|
||||
let ok = relative_eq!(vecs * vals * vecs.adjoint(), m, epsilon = 1.0e-7);
|
||||
if !ok {
|
||||
println!("{:.5}{:.5}", m, &vecs * &vals * vecs.conjugate_transpose());
|
||||
println!("{:.5}{:.5}", m, &vecs * &vals * vecs.adjoint());
|
||||
}
|
||||
|
||||
ok
|
||||
|
@ -10,7 +10,7 @@ macro_rules! gen_tests(
|
||||
|
||||
fn unzero_diagonal<N: Complex>(a: &mut Matrix4<N>) {
|
||||
for i in 0..4 {
|
||||
if a[(i, i)].asum() < na::convert(1.0e-7) {
|
||||
if a[(i, i)].norm1() < na::convert(1.0e-7) {
|
||||
a[(i, i)] = N::one();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user