Make blas, matrix, norm, and ops.rs compatible with SoA Simd.

This commit is contained in:
Sébastien Crozet 2020-03-17 17:58:36 +01:00
parent 73af0f9179
commit 002e735c76
5 changed files with 515 additions and 293 deletions

View File

@ -1,3 +1,4 @@
use crate::SimdComplexField;
use alga::general::{ClosedAdd, ClosedMul, ComplexField};
#[cfg(feature = "std")]
use matrixmultiply;
@ -11,8 +12,9 @@ use crate::base::constraint::{
};
use crate::base::dimension::{Dim, Dynamic, U1, U2, U3, U4};
use crate::base::storage::{Storage, StorageMut};
use crate::base::{DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector, DVectorSlice, VectorSliceN};
use crate::base::{
DVectorSlice, DefaultAllocator, Matrix, Scalar, SquareMatrix, Vector, VectorSliceN,
};
// FIXME: find a way to avoid code duplication just for complex number support.
impl<N: ComplexField, D: Dim, S: Storage<N, D>> Vector<N, D, S> {
@ -102,7 +104,7 @@ impl<N: Scalar + PartialOrd, D: Dim, S: Storage<N, D>> Vector<N, D, S> {
/// ```
#[inline]
pub fn iamax(&self) -> usize
where N: Signed {
where N: Signed {
assert!(!self.is_empty(), "The input vector must not be empty.");
let mut the_max = unsafe { self.vget_unchecked(0).abs() };
@ -173,7 +175,7 @@ impl<N: Scalar + PartialOrd, D: Dim, S: Storage<N, D>> Vector<N, D, S> {
/// ```
#[inline]
pub fn iamin(&self) -> usize
where N: Signed {
where N: Signed {
assert!(!self.is_empty(), "The input vector must not be empty.");
let mut the_min = unsafe { self.vget_unchecked(0).abs() };
@ -229,7 +231,6 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
}
}
impl<N: Scalar + PartialOrd + Signed, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// Computes the index of the matrix component with the largest absolute value.
///
@ -267,10 +268,14 @@ 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>,
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>,
{
assert!(
self.nrows() == rhs.nrows(),
@ -281,27 +286,36 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
// 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 = conjugate(self.get_unchecked((0, 0)).inlined_clone()) * rhs.get_unchecked((0, 0)).inlined_clone();
let b = conjugate(self.get_unchecked((1, 0)).inlined_clone()) * rhs.get_unchecked((1, 0)).inlined_clone();
let a = conjugate(self.get_unchecked((0, 0)).inlined_clone())
* rhs.get_unchecked((0, 0)).inlined_clone();
let b = conjugate(self.get_unchecked((1, 0)).inlined_clone())
* rhs.get_unchecked((1, 0)).inlined_clone();
return a + b;
}
}
if (R::is::<U3>() || R2::is::<U3>()) && (C::is::<U1>() || C2::is::<U1>()) {
unsafe {
let a = conjugate(self.get_unchecked((0, 0)).inlined_clone()) * rhs.get_unchecked((0, 0)).inlined_clone();
let b = conjugate(self.get_unchecked((1, 0)).inlined_clone()) * rhs.get_unchecked((1, 0)).inlined_clone();
let c = conjugate(self.get_unchecked((2, 0)).inlined_clone()) * rhs.get_unchecked((2, 0)).inlined_clone();
let a = conjugate(self.get_unchecked((0, 0)).inlined_clone())
* rhs.get_unchecked((0, 0)).inlined_clone();
let b = conjugate(self.get_unchecked((1, 0)).inlined_clone())
* rhs.get_unchecked((1, 0)).inlined_clone();
let c = conjugate(self.get_unchecked((2, 0)).inlined_clone())
* rhs.get_unchecked((2, 0)).inlined_clone();
return a + b + c;
}
}
if (R::is::<U4>() || R2::is::<U4>()) && (C::is::<U1>() || C2::is::<U1>()) {
unsafe {
let mut a = conjugate(self.get_unchecked((0, 0)).inlined_clone()) * rhs.get_unchecked((0, 0)).inlined_clone();
let mut b = conjugate(self.get_unchecked((1, 0)).inlined_clone()) * rhs.get_unchecked((1, 0)).inlined_clone();
let c = conjugate(self.get_unchecked((2, 0)).inlined_clone()) * rhs.get_unchecked((2, 0)).inlined_clone();
let d = conjugate(self.get_unchecked((3, 0)).inlined_clone()) * rhs.get_unchecked((3, 0)).inlined_clone();
let mut a = conjugate(self.get_unchecked((0, 0)).inlined_clone())
* rhs.get_unchecked((0, 0)).inlined_clone();
let mut b = conjugate(self.get_unchecked((1, 0)).inlined_clone())
* rhs.get_unchecked((1, 0)).inlined_clone();
let c = conjugate(self.get_unchecked((2, 0)).inlined_clone())
* rhs.get_unchecked((2, 0)).inlined_clone();
let d = conjugate(self.get_unchecked((3, 0)).inlined_clone())
* rhs.get_unchecked((3, 0)).inlined_clone();
a += c;
b += d;
@ -341,14 +355,38 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
acc7 = N::zero();
while self.nrows() - i >= 8 {
acc0 += unsafe { conjugate(self.get_unchecked((i + 0, j)).inlined_clone()) * rhs.get_unchecked((i + 0, j)).inlined_clone() };
acc1 += unsafe { conjugate(self.get_unchecked((i + 1, j)).inlined_clone()) * rhs.get_unchecked((i + 1, j)).inlined_clone() };
acc2 += unsafe { conjugate(self.get_unchecked((i + 2, j)).inlined_clone()) * rhs.get_unchecked((i + 2, j)).inlined_clone() };
acc3 += unsafe { conjugate(self.get_unchecked((i + 3, j)).inlined_clone()) * rhs.get_unchecked((i + 3, j)).inlined_clone() };
acc4 += unsafe { conjugate(self.get_unchecked((i + 4, j)).inlined_clone()) * rhs.get_unchecked((i + 4, j)).inlined_clone() };
acc5 += unsafe { conjugate(self.get_unchecked((i + 5, j)).inlined_clone()) * rhs.get_unchecked((i + 5, j)).inlined_clone() };
acc6 += unsafe { conjugate(self.get_unchecked((i + 6, j)).inlined_clone()) * rhs.get_unchecked((i + 6, j)).inlined_clone() };
acc7 += unsafe { conjugate(self.get_unchecked((i + 7, j)).inlined_clone()) * rhs.get_unchecked((i + 7, j)).inlined_clone() };
acc0 += unsafe {
conjugate(self.get_unchecked((i + 0, j)).inlined_clone())
* rhs.get_unchecked((i + 0, j)).inlined_clone()
};
acc1 += unsafe {
conjugate(self.get_unchecked((i + 1, j)).inlined_clone())
* rhs.get_unchecked((i + 1, j)).inlined_clone()
};
acc2 += unsafe {
conjugate(self.get_unchecked((i + 2, j)).inlined_clone())
* rhs.get_unchecked((i + 2, j)).inlined_clone()
};
acc3 += unsafe {
conjugate(self.get_unchecked((i + 3, j)).inlined_clone())
* rhs.get_unchecked((i + 3, j)).inlined_clone()
};
acc4 += unsafe {
conjugate(self.get_unchecked((i + 4, j)).inlined_clone())
* rhs.get_unchecked((i + 4, j)).inlined_clone()
};
acc5 += unsafe {
conjugate(self.get_unchecked((i + 5, j)).inlined_clone())
* rhs.get_unchecked((i + 5, j)).inlined_clone()
};
acc6 += unsafe {
conjugate(self.get_unchecked((i + 6, j)).inlined_clone())
* rhs.get_unchecked((i + 6, j)).inlined_clone()
};
acc7 += unsafe {
conjugate(self.get_unchecked((i + 7, j)).inlined_clone())
* rhs.get_unchecked((i + 7, j)).inlined_clone()
};
i += 8;
}
@ -358,14 +396,16 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
res += acc3 + acc7;
for k in i..self.nrows() {
res += unsafe { conjugate(self.get_unchecked((k, j)).inlined_clone()) * rhs.get_unchecked((k, j)).inlined_clone() }
res += unsafe {
conjugate(self.get_unchecked((k, j)).inlined_clone())
* rhs.get_unchecked((k, j)).inlined_clone()
}
}
}
res
}
/// The dot product between two vectors or matrices (seen as vectors).
///
/// This is equal to `self.transpose() * rhs`. For the sesquilinear complex dot product, use
@ -419,12 +459,12 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
/// ```
#[inline]
pub fn dotc<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<N, R2, C2, SB>) -> N
where
N: ComplexField,
SB: Storage<N, R2, C2>,
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
where
N: SimdComplexField,
SB: Storage<N, R2, C2>,
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
{
self.dotx(rhs, ComplexField::conjugate)
self.dotx(rhs, N::simd_conjugate)
}
/// The dot product between the transpose of `self` and `rhs`.
@ -460,7 +500,10 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
for j in 0..self.nrows() {
for i in 0..self.ncols() {
res += unsafe { self.get_unchecked((j, i)).inlined_clone() * rhs.get_unchecked((i, j)).inlined_clone() }
res += unsafe {
self.get_unchecked((j, i)).inlined_clone()
* rhs.get_unchecked((i, j)).inlined_clone()
}
}
}
@ -468,12 +511,25 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
}
}
fn array_axcpy<N>(y: &mut [N], a: N, x: &[N], c: N, beta: N, stride1: usize, stride2: usize, len: usize)
where N: Scalar + Zero + ClosedAdd + ClosedMul {
fn array_axcpy<N>(
y: &mut [N],
a: N,
x: &[N],
c: N,
beta: N,
stride1: usize,
stride2: usize,
len: usize,
) where
N: Scalar + Zero + ClosedAdd + ClosedMul,
{
for i in 0..len {
unsafe {
let y = y.get_unchecked_mut(i * stride1);
*y = a.inlined_clone() * x.get_unchecked(i * stride2).inlined_clone() * c.inlined_clone() + beta.inlined_clone() * y.inlined_clone();
*y = a.inlined_clone()
* x.get_unchecked(i * stride2).inlined_clone()
* c.inlined_clone()
+ beta.inlined_clone() * y.inlined_clone();
}
}
}
@ -482,7 +538,9 @@ fn array_axc<N>(y: &mut [N], a: N, x: &[N], c: N, stride1: usize, stride2: usize
where N: Scalar + Zero + ClosedAdd + ClosedMul {
for i in 0..len {
unsafe {
*y.get_unchecked_mut(i * stride1) = a.inlined_clone() * x.get_unchecked(i * stride2).inlined_clone() * c.inlined_clone();
*y.get_unchecked_mut(i * stride1) = a.inlined_clone()
* x.get_unchecked(i * stride2).inlined_clone()
* c.inlined_clone();
}
}
}
@ -613,7 +671,6 @@ where
}
}
#[inline(always)]
fn xxgemv<D2: Dim, D3: Dim, SB, SC>(
&mut self,
@ -621,7 +678,10 @@ where
a: &SquareMatrix<N, D2, SB>,
x: &Vector<N, D3, SC>,
beta: N,
dot: impl Fn(&DVectorSlice<N, SB::RStride, SB::CStride>, &DVectorSlice<N, SC::RStride, SC::CStride>) -> N,
dot: impl Fn(
&DVectorSlice<N, SB::RStride, SB::CStride>,
&DVectorSlice<N, SC::RStride, SC::CStride>,
) -> N,
) where
N: One,
SB: Storage<N, D2, D2>,
@ -660,8 +720,11 @@ where
val = x.vget_unchecked(j).inlined_clone();
*self.vget_unchecked_mut(j) += alpha.inlined_clone() * dot;
}
self.rows_range_mut(j + 1..)
.axpy(alpha.inlined_clone() * val, &col2.rows_range(j + 1..), N::one());
self.rows_range_mut(j + 1..).axpy(
alpha.inlined_clone() * val,
&col2.rows_range(j + 1..),
N::one(),
);
}
}
@ -765,7 +828,7 @@ where
x: &Vector<N, D3, SC>,
beta: N,
) where
N: ComplexField,
N: SimdComplexField,
SB: Storage<N, D2, D2>,
SC: Storage<N, D3>,
ShapeConstraint: DimEq<D, D2> + AreMultipliable<D2, D2, D3, U1>,
@ -773,7 +836,6 @@ where
self.xxgemv(alpha, a, x, beta, |a, b| a.dotc(b))
}
#[inline(always)]
fn gemv_xx<R2: Dim, C2: Dim, D3: Dim, SB, SC>(
&mut self,
@ -809,12 +871,12 @@ where
} else {
for j in 0..ncols2 {
let val = unsafe { self.vget_unchecked_mut(j) };
*val = alpha.inlined_clone() * dot(&a.column(j), x) + beta.inlined_clone() * val.inlined_clone();
*val = alpha.inlined_clone() * dot(&a.column(j), x)
+ beta.inlined_clone() * val.inlined_clone();
}
}
}
/// Computes `self = alpha * a.transpose() * x + beta * self`, where `a` is a matrix, `x` a vector, and
/// `alpha, beta` two scalars.
///
@ -876,7 +938,7 @@ where
x: &Vector<N, D3, SC>,
beta: N,
) where
N: ComplexField,
N: SimdComplexField,
SB: Storage<N, R2, C2>,
SC: Storage<N, D3>,
ShapeConstraint: DimEq<D, C2> + AreMultipliable<C2, R2, D3, U1>,
@ -914,7 +976,8 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
for j in 0..ncols1 {
// FIXME: avoid bound checks.
let val = unsafe { conjugate(y.vget_unchecked(j).inlined_clone()) };
self.column_mut(j).axpy(alpha.inlined_clone() * val, x, beta.inlined_clone());
self.column_mut(j)
.axpy(alpha.inlined_clone() * val, x, beta.inlined_clone());
}
}
@ -975,12 +1038,12 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
y: &Vector<N, D3, SC>,
beta: N,
) where
N: ComplexField,
N: SimdComplexField,
SB: Storage<N, D2>,
SC: Storage<N, D3>,
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
{
self.gerx(alpha, x, y, beta, ComplexField::conjugate)
self.gerx(alpha, x, y, beta, SimdComplexField::simd_conjugate)
}
/// Computes `self = alpha * a * b + beta * self`, where `a, b, self` are matrices.
@ -1032,7 +1095,8 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
|| R2::is::<Dynamic>()
|| C2::is::<Dynamic>()
|| R3::is::<Dynamic>()
|| C3::is::<Dynamic>() {
|| C3::is::<Dynamic>()
{
// matrixmultiply can be used only if the std feature is available.
let nrows1 = self.nrows();
let (nrows2, ncols2) = a.shape();
@ -1125,10 +1189,14 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
}
}
for j1 in 0..ncols1 {
// FIXME: avoid bound checks.
self.column_mut(j1).gemv(alpha.inlined_clone(), a, &b.column(j1), beta.inlined_clone());
self.column_mut(j1).gemv(
alpha.inlined_clone(),
a,
&b.column(j1),
beta.inlined_clone(),
);
}
}
@ -1185,11 +1253,15 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
for j1 in 0..ncols1 {
// FIXME: avoid bound checks.
self.column_mut(j1).gemv_tr(alpha.inlined_clone(), a, &b.column(j1), beta.inlined_clone());
self.column_mut(j1).gemv_tr(
alpha.inlined_clone(),
a,
&b.column(j1),
beta.inlined_clone(),
);
}
}
/// Computes `self = alpha * a.adjoint() * b + beta * self`, where `a, b, self` are matrices.
/// `alpha` and `beta` are scalar.
///
@ -1220,12 +1292,12 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
b: &Matrix<N, R3, C3, SC>,
beta: N,
) where
N: ComplexField,
N: SimdComplexField,
SB: Storage<N, R2, C2>,
SC: Storage<N, R3, C3>,
ShapeConstraint: SameNumberOfRows<R1, C2>
+ SameNumberOfColumns<C1, C3>
+ AreMultipliable<C2, R2, R3, C3>,
+ SameNumberOfColumns<C1, C3>
+ AreMultipliable<C2, R2, R3, C3>,
{
let (nrows1, ncols1) = self.shape();
let (nrows2, ncols2) = a.shape();
@ -1386,12 +1458,12 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul
y: &Vector<N, D3, SC>,
beta: N,
) where
N: ComplexField,
N: SimdComplexField,
SB: Storage<N, D2>,
SC: Storage<N, D3>,
ShapeConstraint: DimEq<R1, D2> + DimEq<C1, D3>,
{
self.xxgerx(alpha, x, y, beta, ComplexField::conjugate)
self.xxgerx(alpha, x, y, beta, SimdComplexField::simd_conjugate)
}
}
@ -1534,11 +1606,13 @@ where N: Scalar + Zero + One + ClosedAdd + ClosedMul
DimEq<D3, R4> + DimEq<D1, C4> + DimEq<D2, D3> + AreMultipliable<C4, R4, D2, U1>,
{
work.gemv(N::one(), mid, &rhs.column(0), N::zero());
self.column_mut(0).gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone());
self.column_mut(0)
.gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone());
for j in 1..rhs.ncols() {
work.gemv(N::one(), mid, &rhs.column(j), N::zero());
self.column_mut(j).gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone());
self.column_mut(j)
.gemv_tr(alpha.inlined_clone(), &rhs, work, beta.inlined_clone());
}
}

View File

@ -16,16 +16,20 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "abomonation-serialize")]
use abomonation::Abomonation;
use alga::general::{ClosedAdd, ClosedMul, ClosedSub, RealField, Ring, ComplexField, Field};
use alga::general::{ClosedAdd, ClosedMul, ClosedSub, Field, RealField, Ring};
use alga::simd::SimdPartialOrd;
use crate::base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR};
use crate::base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, ShapeConstraint};
use crate::base::dimension::{Dim, DimAdd, DimSum, IsNotStaticOne, U1, U2, U3};
use crate::base::iter::{MatrixIter, MatrixIterMut, RowIter, RowIterMut, ColumnIter, ColumnIterMut};
use crate::base::iter::{
ColumnIter, ColumnIterMut, MatrixIter, MatrixIterMut, RowIter, RowIterMut,
};
use crate::base::storage::{
ContiguousStorage, ContiguousStorageMut, Owned, SameShapeStorage, Storage, StorageMut,
};
use crate::base::{DefaultAllocator, MatrixMN, MatrixN, Scalar, Unit, VectorN};
use crate::{SimdComplexField, SimdRealField};
/// A square matrix.
pub type SquareMatrix<N, D, S> = Matrix<N, D, D, S>;
@ -431,6 +435,25 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
res
}
/// Similar to `self.iter().fold(init, f)` except that `init` is replaced by a closure.
///
/// The initialization closure is given the first component of this matrix:
/// - If the matrix has no component (0 rows or 0 columns) then `init_f` is called with `None`
/// and its return value is the value returned by this method.
/// - If the matrix has has least one component, then `init_f` is called with the first component
/// to compute the initial value. Folding then continues on all the remaining components of the matrix.
#[inline]
pub fn fold_with<N2>(
&self,
init_f: impl FnOnce(Option<&N>) -> N2,
f: impl FnMut(N2, &N) -> N2,
) -> N2
{
let mut it = self.iter();
let init = init_f(it.next());
it.fold(init, f)
}
/// Returns a matrix containing the result of `f` applied to each of its entries. Unlike `map`,
/// `f` also gets passed the row and column index, i.e. `f(row, col, value)`.
#[inline]
@ -553,13 +576,18 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// Folds a function `f` on each pairs of entries from `self` and `rhs`.
#[inline]
pub fn zip_fold<N2, R2, C2, S2, Acc>(&self, rhs: &Matrix<N2, R2, C2, S2>, init: Acc, mut f: impl FnMut(Acc, N, N2) -> Acc) -> Acc
where
N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>
pub fn zip_fold<N2, R2, C2, S2, Acc>(
&self,
rhs: &Matrix<N2, R2, C2, S2>,
init: Acc,
mut f: impl FnMut(Acc, N, N2) -> Acc,
) -> Acc
where
N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
let (nrows, ncols) = self.data.shape();
@ -718,7 +746,8 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
for j in 0..ncols {
for i in 0..nrows {
unsafe {
*self.get_unchecked_mut((i, j)) = slice.get_unchecked(i + j * nrows).inlined_clone();
*self.get_unchecked_mut((i, j)) =
slice.get_unchecked(i + j * nrows).inlined_clone();
}
}
}
@ -774,7 +803,7 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
// FIXME: rename `apply` to `apply_mut` and `apply_into` to `apply`?
/// Returns `self` with each of its components replaced by the result of a closure `f` applied on it.
#[inline]
pub fn apply_into<F: FnMut(N) -> N>(mut self, f: F) -> Self{
pub fn apply_into<F: FnMut(N) -> N>(mut self, f: F) -> Self {
self.apply(f);
self
}
@ -797,12 +826,17 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
/// Replaces each component of `self` by the result of a closure `f` applied on its components
/// joined with the components from `rhs`.
#[inline]
pub fn zip_apply<N2, R2, C2, S2>(&mut self, rhs: &Matrix<N2, R2, C2, S2>, mut f: impl FnMut(N, N2) -> N)
where N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
pub fn zip_apply<N2, R2, C2, S2>(
&mut self,
rhs: &Matrix<N2, R2, C2, S2>,
mut f: impl FnMut(N, N2) -> N,
) where
N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
let (nrows, ncols) = self.shape();
assert!(
@ -821,21 +855,26 @@ impl<N: Scalar, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
}
}
/// Replaces each component of `self` by the result of a closure `f` applied on its components
/// joined with the components from `b` and `c`.
#[inline]
pub fn zip_zip_apply<N2, R2, C2, S2, N3, R3, C3, S3>(&mut self, b: &Matrix<N2, R2, C2, S2>, c: &Matrix<N3, R3, C3, S3>, mut f: impl FnMut(N, N2, N3) -> N)
where N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
N3: Scalar,
R3: Dim,
C3: Dim,
S3: Storage<N3, R3, C3>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
pub fn zip_zip_apply<N2, R2, C2, S2, N3, R3, C3, S3>(
&mut self,
b: &Matrix<N2, R2, C2, S2>,
c: &Matrix<N3, R3, C3, S3>,
mut f: impl FnMut(N, N2, N3) -> N,
) where
N2: Scalar,
R2: Dim,
C2: Dim,
S2: Storage<N2, R2, C2>,
N3: Scalar,
R3: Dim,
C3: Dim,
S3: Storage<N3, R3, C3>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
let (nrows, ncols) = self.shape();
assert!(
@ -914,7 +953,7 @@ impl<N: Scalar, D: Dim, S: StorageMut<N, D, D>> Matrix<N, D, D, S> {
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
impl<N: SimdComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// Takes the adjoint (aka. conjugate-transpose) of `self` and store the result into `out`.
#[inline]
pub fn adjoint_to<R2, C2, SB>(&self, out: &mut Matrix<N, R2, C2, SB>)
@ -934,7 +973,7 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
for i in 0..nrows {
for j in 0..ncols {
unsafe {
*out.get_unchecked_mut((j, i)) = self.get_unchecked((i, j)).conjugate();
*out.get_unchecked_mut((j, i)) = self.get_unchecked((i, j)).simd_conjugate();
}
}
}
@ -959,11 +998,11 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
#[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>,
where
R2: Dim,
C2: Dim,
SB: StorageMut<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, C2> + SameNumberOfColumns<C, R2>,
{
self.adjoint_to(out)
}
@ -972,7 +1011,7 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
#[deprecated(note = "Renamed `self.adjoint()`.")]
#[inline]
pub fn conjugate_transpose(&self) -> MatrixMN<N, C, R>
where DefaultAllocator: Allocator<N, C, R> {
where DefaultAllocator: Allocator<N, C, R> {
self.adjoint()
}
@ -980,48 +1019,48 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
#[inline]
#[must_use = "Did you mean to use conjugate_mut()?"]
pub fn conjugate(&self) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.map(|e| e.conjugate())
where DefaultAllocator: Allocator<N, R, C> {
self.map(|e| e.simd_conjugate())
}
/// Divides each component of the complex matrix `self` by the given real.
#[inline]
#[must_use = "Did you mean to use unscale_mut()?"]
pub fn unscale(&self, real: N::RealField) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.map(|e| e.unscale(real))
pub fn unscale(&self, real: N::SimdRealField) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.map(|e| e.simd_unscale(real))
}
/// Multiplies each component of the complex matrix `self` by the given real.
#[inline]
#[must_use = "Did you mean to use scale_mut()?"]
pub fn scale(&self, real: N::RealField) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.map(|e| e.scale(real))
pub fn scale(&self, real: N::SimdRealField) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.map(|e| e.simd_scale(real))
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
impl<N: SimdComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
/// The conjugate of the complex matrix `self` computed in-place.
#[inline]
pub fn conjugate_mut(&mut self) {
self.apply(|e| e.conjugate())
self.apply(|e| e.simd_conjugate())
}
/// Divides each component of the complex matrix `self` by the given real.
#[inline]
pub fn unscale_mut(&mut self, real: N::RealField) {
self.apply(|e| e.unscale(real))
pub fn unscale_mut(&mut self, real: N::SimdRealField) {
self.apply(|e| e.simd_unscale(real))
}
/// Multiplies each component of the complex matrix `self` by the given real.
#[inline]
pub fn scale_mut(&mut self, real: N::RealField) {
self.apply(|e| e.scale(real))
pub fn scale_mut(&mut self, real: N::SimdRealField) {
self.apply(|e| e.simd_scale(real))
}
}
impl<N: ComplexField, D: Dim, S: StorageMut<N, D, D>> Matrix<N, D, D, S> {
impl<N: SimdComplexField, D: Dim, S: StorageMut<N, D, D>> Matrix<N, D, D, S> {
/// Sets `self` to its adjoint.
#[deprecated(note = "Renamed to `self.adjoint_mut()`.")]
pub fn conjugate_transform_mut(&mut self) {
@ -1042,8 +1081,8 @@ impl<N: ComplexField, D: Dim, S: StorageMut<N, D, D>> Matrix<N, D, D, S> {
unsafe {
let ref_ij = self.get_unchecked_mut((i, j)) as *mut N;
let ref_ji = self.get_unchecked_mut((j, i)) as *mut N;
let conj_ij = (*ref_ij).conjugate();
let conj_ji = (*ref_ji).conjugate();
let conj_ij = (*ref_ij).simd_conjugate();
let conj_ji = (*ref_ji).simd_conjugate();
*ref_ij = conj_ji;
*ref_ji = conj_ij;
}
@ -1051,7 +1090,7 @@ impl<N: ComplexField, D: Dim, S: StorageMut<N, D, D>> Matrix<N, D, D, S> {
{
let diag = unsafe { self.get_unchecked_mut((i, i)) };
*diag = diag.conjugate();
*diag = diag.simd_conjugate();
}
}
}
@ -1061,7 +1100,7 @@ impl<N: Scalar, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
/// The diagonal of this matrix.
#[inline]
pub fn diagonal(&self) -> VectorN<N, D>
where DefaultAllocator: Allocator<N, D> {
where DefaultAllocator: Allocator<N, D> {
self.map_diagonal(|e| e)
}
@ -1070,7 +1109,7 @@ impl<N: Scalar, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
/// This is a more efficient version of `self.diagonal().map(f)` since this
/// allocates only once.
pub fn map_diagonal<N2: Scalar>(&self, mut f: impl FnMut(N) -> N2) -> VectorN<N2, D>
where DefaultAllocator: Allocator<N2, D> {
where DefaultAllocator: Allocator<N2, D> {
assert!(
self.is_square(),
"Unable to get the diagonal of a non-square matrix."
@ -1091,7 +1130,7 @@ impl<N: Scalar, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
/// Computes a trace of a square matrix, i.e., the sum of its diagonal elements.
#[inline]
pub fn trace(&self) -> N
where N: Ring {
where N: Ring {
assert!(
self.is_square(),
"Cannot compute the trace of non-square matrix."
@ -1108,12 +1147,15 @@ impl<N: Scalar, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
}
}
impl<N: ComplexField, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
impl<N: SimdComplexField, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
/// The symmetric part of `self`, i.e., `0.5 * (self + self.transpose())`.
#[inline]
pub fn symmetric_part(&self) -> MatrixMN<N, D, D>
where DefaultAllocator: Allocator<N, D, D> {
assert!(self.is_square(), "Cannot compute the symmetric part of a non-square matrix.");
where DefaultAllocator: Allocator<N, D, D> {
assert!(
self.is_square(),
"Cannot compute the symmetric part of a non-square matrix."
);
let mut tr = self.transpose();
tr += self;
tr *= crate::convert::<_, N>(0.5);
@ -1123,8 +1165,11 @@ impl<N: ComplexField, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
/// 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.");
where DefaultAllocator: Allocator<N, D, D> {
assert!(
self.is_square(),
"Cannot compute the hermitian part of a non-square matrix."
);
let mut tr = self.adjoint();
tr += self;
@ -1133,20 +1178,24 @@ impl<N: ComplexField, D: Dim, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
}
}
impl<N: Scalar + Zero + One, D: DimAdd<U1> + IsNotStaticOne, S: Storage<N, D, D>> Matrix<N, D, D, S> {
impl<N: Scalar + Zero + One, D: DimAdd<U1> + IsNotStaticOne, S: Storage<N, D, D>>
Matrix<N, D, D, S>
{
/// Yields the homogeneous matrix for this matrix, i.e., appending an additional dimension and
/// and setting the diagonal element to `1`.
#[inline]
pub fn to_homogeneous(&self) -> MatrixN<N, DimSum<D, U1>>
where DefaultAllocator: Allocator<N, DimSum<D, U1>, DimSum<D, U1>> {
assert!(self.is_square(), "Only square matrices can currently be transformed to homogeneous coordinates.");
assert!(
self.is_square(),
"Only square matrices can currently be transformed to homogeneous coordinates."
);
let dim = DimSum::<D, U1>::from_usize(self.nrows() + 1);
let mut res = MatrixN::identity_generic(dim, dim);
res.generic_slice_mut::<D, D>((0, 0), self.data.shape()).copy_from(&self);
res.generic_slice_mut::<D, D>((0, 0), self.data.shape())
.copy_from(&self);
res
}
}
impl<N: Scalar + Zero, D: DimAdd<U1>, S: Storage<N, D>> Vector<N, D, S> {
@ -1347,7 +1396,8 @@ impl<N, R: Dim, C: Dim, S> Eq for Matrix<N, R, C, S>
where
N: Scalar + Eq,
S: Storage<N, R, C>,
{}
{
}
impl<N, R, R2, C, C2, S, S2> PartialEq<Matrix<N, R2, C2, S2>> for Matrix<N, R, C, S>
where
@ -1357,7 +1407,7 @@ where
R: Dim,
R2: Dim,
S: Storage<N, R, C>,
S2: Storage<N, R2, C2>
S2: Storage<N, R2, C2>,
{
#[inline]
fn eq(&self, right: &Matrix<N, R2, C2, S2>) -> bool {
@ -1377,7 +1427,9 @@ macro_rules! impl_fmt {
#[cfg(feature = "std")]
fn val_width<N: Scalar + $trait>(val: &N, f: &mut fmt::Formatter) -> usize {
match f.precision() {
Some(precision) => format!($fmt_str_with_precision, val, precision).chars().count(),
Some(precision) => format!($fmt_str_with_precision, val, precision)
.chars()
.count(),
None => format!($fmt_str_without_precision, val).chars().count(),
}
}
@ -1421,7 +1473,9 @@ macro_rules! impl_fmt {
let pad = max_length_with_space - number_length;
write!(f, " {:>thepad$}", "", thepad = pad)?;
match f.precision() {
Some(precision) => write!(f, $fmt_str_with_precision, (*self)[(i, j)], precision)?,
Some(precision) => {
write!(f, $fmt_str_with_precision, (*self)[(i, j)], precision)?
}
None => write!(f, $fmt_str_without_precision, (*self)[(i, j)])?,
}
}
@ -1451,13 +1505,16 @@ impl_fmt!(fmt::Pointer, "{:p}", "{:.1$p}");
#[test]
fn lower_exp() {
let test = crate::Matrix2::new(1e6, 2e5, 2e-5, 1.);
assert_eq!(format!("{:e}", test), r"
assert_eq!(
format!("{:e}", test),
r"
1e6 2e5
2e-5 1e0
")
"
)
}
impl<N: Scalar + Ring, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
@ -1477,7 +1534,8 @@ impl<N: Scalar + Ring, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
unsafe {
self.get_unchecked((0, 0)).inlined_clone() * b.get_unchecked((1, 0)).inlined_clone()
- self.get_unchecked((1, 0)).inlined_clone() * b.get_unchecked((0, 0)).inlined_clone()
- self.get_unchecked((1, 0)).inlined_clone()
* b.get_unchecked((0, 0)).inlined_clone()
}
}
@ -1520,9 +1578,12 @@ impl<N: Scalar + Ring, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
let by = b.get_unchecked((1, 0));
let bz = b.get_unchecked((2, 0));
*res.get_unchecked_mut((0, 0)) = ay.inlined_clone() * bz.inlined_clone() - az.inlined_clone() * by.inlined_clone();
*res.get_unchecked_mut((1, 0)) = az.inlined_clone() * bx.inlined_clone() - ax.inlined_clone() * bz.inlined_clone();
*res.get_unchecked_mut((2, 0)) = ax.inlined_clone() * by.inlined_clone() - ay.inlined_clone() * bx.inlined_clone();
*res.get_unchecked_mut((0, 0)) = ay.inlined_clone() * bz.inlined_clone()
- az.inlined_clone() * by.inlined_clone();
*res.get_unchecked_mut((1, 0)) = az.inlined_clone() * bx.inlined_clone()
- ax.inlined_clone() * bz.inlined_clone();
*res.get_unchecked_mut((2, 0)) = ax.inlined_clone() * by.inlined_clone()
- ay.inlined_clone() * bx.inlined_clone();
res
}
@ -1541,9 +1602,12 @@ impl<N: Scalar + Ring, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
let by = b.get_unchecked((0, 1));
let bz = b.get_unchecked((0, 2));
*res.get_unchecked_mut((0, 0)) = ay.inlined_clone() * bz.inlined_clone() - az.inlined_clone() * by.inlined_clone();
*res.get_unchecked_mut((0, 1)) = az.inlined_clone() * bx.inlined_clone() - ax.inlined_clone() * bz.inlined_clone();
*res.get_unchecked_mut((0, 2)) = ax.inlined_clone() * by.inlined_clone() - ay.inlined_clone() * bx.inlined_clone();
*res.get_unchecked_mut((0, 0)) = ay.inlined_clone() * bz.inlined_clone()
- az.inlined_clone() * by.inlined_clone();
*res.get_unchecked_mut((0, 1)) = az.inlined_clone() * bx.inlined_clone()
- ax.inlined_clone() * bz.inlined_clone();
*res.get_unchecked_mut((0, 2)) = ax.inlined_clone() * by.inlined_clone()
- ay.inlined_clone() * bx.inlined_clone();
res
}
@ -1571,10 +1635,10 @@ where DefaultAllocator: Allocator<N, U3>
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
impl<N: SimdComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// The smallest angle between two vectors.
#[inline]
pub fn angle<R2: Dim, C2: Dim, SB>(&self, other: &Matrix<N, R2, C2, SB>) -> N::RealField
pub fn angle<R2: Dim, C2: Dim, SB>(&self, other: &Matrix<N, R2, C2, SB>) -> N::SimdRealField
where
SB: Storage<N, R2, C2>,
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
@ -1584,17 +1648,11 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
let n2 = other.norm();
if n1.is_zero() || n2.is_zero() {
N::RealField::zero()
N::SimdRealField::zero()
} else {
let cang = prod.real() / (n1 * n2);
if cang > N::RealField::one() {
N::RealField::zero()
} else if cang < -N::RealField::one() {
N::RealField::pi()
} else {
cang.acos()
}
let cang = prod.simd_real() / (n1 * n2);
cang.simd_clamp(-N::SimdRealField::one(), N::SimdRealField::one())
.simd_acos()
}
}
}
@ -1761,7 +1819,7 @@ where
for j in 0..ncols {
for i in 0..nrows {
unsafe {
self.get_unchecked((i, j)).hash(state);
self.get_unchecked((i, j)).hash(state);
}
}
}

View File

@ -1,25 +1,37 @@
use num::Zero;
use crate::allocator::Allocator;
use crate::{RealField, ComplexField};
use crate::base::{DefaultAllocator, Dim, Matrix, MatrixMN};
use crate::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint};
use crate::storage::{Storage, StorageMut};
use crate::base::{DefaultAllocator, Matrix, Dim, MatrixMN};
use crate::constraint::{SameNumberOfRows, SameNumberOfColumns, ShapeConstraint};
use crate::{ComplexField, RealField, SimdComplexField, SimdRealField};
use alga::simd::SimdPartialOrd;
// FIXME: this should be be a trait on alga?
/// A trait for abstract matrix norms.
///
/// This may be moved to the alga crate in the future.
pub trait Norm<N: ComplexField> {
pub trait Norm<N: SimdComplexField> {
/// Apply this norm to the given matrix.
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C>;
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
where
R: Dim,
C: Dim,
S: Storage<N, R, C>;
/// Use the metric induced by this norm to compute the metric distance between the two given matrices.
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>;
fn metric_distance<R1, C1, S1, R2, C2, S2>(
&self,
m1: &Matrix<N, R1, C1, S1>,
m2: &Matrix<N, R2, C2, S2>,
) -> N::SimdRealField
where
R1: Dim,
C1: Dim,
S1: Storage<N, R1, C1>,
R2: Dim,
C2: Dim,
S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>;
}
/// Euclidean norm.
@ -29,81 +41,123 @@ pub struct LpNorm(pub i32);
/// L-infinite norm aka. Chebytchev norm aka. uniform norm aka. suppremum norm.
pub struct UniformNorm;
impl<N: ComplexField> Norm<N> for EuclideanNorm {
impl<N: SimdComplexField> Norm<N> for EuclideanNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.norm_squared().sqrt()
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
where
R: Dim,
C: Dim,
S: Storage<N, R, C>,
{
m.norm_squared().simd_sqrt()
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::RealField::zero(), |acc, a, b| {
fn metric_distance<R1, C1, S1, R2, C2, S2>(
&self,
m1: &Matrix<N, R1, C1, S1>,
m2: &Matrix<N, R2, C2, S2>,
) -> N::SimdRealField
where
R1: Dim,
C1: Dim,
S1: Storage<N, R1, C1>,
R2: Dim,
C2: Dim,
S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>,
{
m1.zip_fold(m2, N::SimdRealField::zero(), |acc, a, b| {
let diff = a - b;
acc + diff.modulus_squared()
}).sqrt()
acc + diff.simd_modulus_squared()
})
.simd_sqrt()
}
}
impl<N: ComplexField> Norm<N> for LpNorm {
impl<N: SimdComplexField> Norm<N> for LpNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C> {
m.fold(N::RealField::zero(), |a, b| {
a + b.modulus().powi(self.0)
}).powf(crate::convert(1.0 / (self.0 as f64)))
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
where
R: Dim,
C: Dim,
S: Storage<N, R, C>,
{
m.fold(N::SimdRealField::zero(), |a, b| {
a + b.simd_modulus().simd_powi(self.0)
})
.simd_powf(crate::convert(1.0 / (self.0 as f64)))
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::RealField::zero(), |acc, a, b| {
fn metric_distance<R1, C1, S1, R2, C2, S2>(
&self,
m1: &Matrix<N, R1, C1, S1>,
m2: &Matrix<N, R2, C2, S2>,
) -> N::SimdRealField
where
R1: Dim,
C1: Dim,
S1: Storage<N, R1, C1>,
R2: Dim,
C2: Dim,
S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>,
{
m1.zip_fold(m2, N::SimdRealField::zero(), |acc, a, b| {
let diff = a - b;
acc + diff.modulus().powi(self.0)
}).powf(crate::convert(1.0 / (self.0 as f64)))
acc + diff.simd_modulus().simd_powi(self.0)
})
.simd_powf(crate::convert(1.0 / (self.0 as f64)))
}
}
impl<N: ComplexField> Norm<N> for UniformNorm {
impl<N: SimdComplexField> Norm<N> for UniformNorm {
#[inline]
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::RealField
where R: Dim, C: Dim, S: Storage<N, R, C> {
fn norm<R, C, S>(&self, m: &Matrix<N, R, C, S>) -> N::SimdRealField
where
R: Dim,
C: Dim,
S: Storage<N, R, C>,
{
// NOTE: we don't use `m.amax()` here because for the complex
// numbers this will return the max norm1 instead of the modulus.
m.fold(N::RealField::zero(), |acc, a| acc.max(a.modulus()))
m.fold(N::SimdRealField::zero(), |acc, a| {
acc.simd_max(a.simd_modulus())
})
}
#[inline]
fn metric_distance<R1, C1, S1, R2, C2, S2>(&self, m1: &Matrix<N, R1, C1, S1>, m2: &Matrix<N, R2, C2, S2>) -> N::RealField
where R1: Dim, C1: Dim, S1: Storage<N, R1, C1>,
R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2> {
m1.zip_fold(m2, N::RealField::zero(), |acc, a, b| {
let val = (a - b).modulus();
if val > acc {
val
} else {
acc
}
fn metric_distance<R1, C1, S1, R2, C2, S2>(
&self,
m1: &Matrix<N, R1, C1, S1>,
m2: &Matrix<N, R2, C2, S2>,
) -> N::SimdRealField
where
R1: Dim,
C1: Dim,
S1: Storage<N, R1, C1>,
R2: Dim,
C2: Dim,
S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>,
{
m1.zip_fold(m2, N::SimdRealField::zero(), |acc, a, b| {
let val = (a - b).simd_modulus();
acc.simd_max(val)
})
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
impl<N: SimdComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// The squared L2 norm of this vector.
#[inline]
pub fn norm_squared(&self) -> N::RealField {
let mut res = N::RealField::zero();
pub fn norm_squared(&self) -> N::SimdRealField {
let mut res = N::SimdRealField::zero();
for i in 0..self.ncols() {
let col = self.column(i);
res += col.dotc(&col).real()
res += col.dotc(&col).simd_real()
}
res
@ -113,17 +167,21 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
///
/// Use `.apply_norm` to apply a custom norm.
#[inline]
pub fn norm(&self) -> N::RealField {
self.norm_squared().sqrt()
pub fn norm(&self) -> N::SimdRealField {
self.norm_squared().simd_sqrt()
}
/// Compute the distance between `self` and `rhs` using the metric induced by the euclidean norm.
///
/// Use `.apply_metric_distance` to apply a custom norm.
#[inline]
pub fn metric_distance<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>) -> N::RealField
where R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
pub fn metric_distance<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>) -> N::SimdRealField
where
R2: Dim,
C2: Dim,
S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
self.apply_metric_distance(rhs, &EuclideanNorm)
}
@ -140,7 +198,7 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm());
/// ```
#[inline]
pub fn apply_norm(&self, norm: &impl Norm<N>) -> N::RealField {
pub fn apply_norm(&self, norm: &impl Norm<N>) -> N::SimdRealField {
norm.norm(self)
}
@ -159,9 +217,17 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm());
/// ```
#[inline]
pub fn apply_metric_distance<R2, C2, S2>(&self, rhs: &Matrix<N, R2, C2, S2>, norm: &impl Norm<N>) -> N::RealField
where R2: Dim, C2: Dim, S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2> {
pub fn apply_metric_distance<R2, C2, S2>(
&self,
rhs: &Matrix<N, R2, C2, S2>,
norm: &impl Norm<N>,
) -> N::SimdRealField
where
R2: Dim,
C2: Dim,
S2: Storage<N, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
norm.metric_distance(self, rhs)
}
@ -171,7 +237,7 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
///
/// This function is simply implemented as a call to `norm()`
#[inline]
pub fn magnitude(&self) -> N::RealField {
pub fn magnitude(&self) -> N::SimdRealField {
self.norm()
}
@ -181,18 +247,41 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
///
/// This function is simply implemented as a call to `norm_squared()`
#[inline]
pub fn magnitude_squared(&self) -> N::RealField {
pub fn magnitude_squared(&self) -> N::SimdRealField {
self.norm_squared()
}
/// Sets the magnitude of this vector.
#[inline]
pub fn set_magnitude(&mut self, magnitude: N::SimdRealField)
where S: StorageMut<N, R, C> {
let n = self.norm();
self.scale_mut(magnitude / n)
}
/// Returns a normalized version of this matrix.
#[inline]
#[must_use = "Did you mean to use normalize_mut()?"]
pub fn normalize(&self) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.unscale(self.norm())
}
/// The Lp norm of this matrix.
#[inline]
pub fn lp_norm(&self, p: i32) -> N::SimdRealField {
self.apply_norm(&LpNorm(p))
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// Sets the magnitude of this vector unless it is smaller than `min_magnitude`.
///
/// If `self.magnitude()` is smaller than `min_magnitude`, it will be left unchanged.
/// Otherwise this is equivalent to: `*self = self.normalize() * magnitude.
#[inline]
pub fn try_set_magnitude(&mut self, magnitude: N::RealField, min_magnitude: N::RealField)
where S: StorageMut<N, R, C> {
where S: StorageMut<N, R, C> {
let n = self.norm();
if n >= min_magnitude {
@ -200,19 +289,11 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
}
}
/// Returns a normalized version of this matrix.
#[inline]
#[must_use = "Did you mean to use normalize_mut()?"]
pub fn normalize(&self) -> MatrixMN<N, R, C>
where DefaultAllocator: Allocator<N, R, C> {
self.unscale(self.norm())
}
/// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`.
#[inline]
#[must_use = "Did you mean to use try_normalize_mut()?"]
pub fn try_normalize(&self, min_norm: N::RealField) -> Option<MatrixMN<N, R, C>>
where DefaultAllocator: Allocator<N, R, C> {
where DefaultAllocator: Allocator<N, R, C> {
let n = self.norm();
if n <= min_norm {
@ -221,30 +302,26 @@ impl<N: ComplexField, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
Some(self.unscale(n))
}
}
/// The Lp norm of this matrix.
#[inline]
pub fn lp_norm(&self, p: i32) -> N::RealField {
self.apply_norm(&LpNorm(p))
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
impl<N: SimdComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
/// Normalizes this matrix in-place and returns its norm.
#[inline]
pub fn normalize_mut(&mut self) -> N::RealField {
pub fn normalize_mut(&mut self) -> N::SimdRealField {
let n = self.norm();
self.unscale_mut(n);
n
}
}
impl<N: ComplexField, R: Dim, C: Dim, S: StorageMut<N, R, C>> Matrix<N, R, C, S> {
/// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`.
///
/// If the normalization succeeded, returns the old norm of this matrix.
#[inline]
pub fn try_normalize_mut(&mut self, min_norm: N::RealField) -> Option<N::RealField> {
pub fn try_normalize_mut(&mut self, min_norm: N::RealField) -> Option<N::RealField>
where N: ComplexField {
let n = self.norm();
if n <= min_norm {

View File

@ -1,11 +1,12 @@
use num::{One, Signed, Zero};
use std::cmp::{PartialOrd, Ordering};
use std::cmp::{Ordering, PartialOrd};
use std::iter;
use std::ops::{
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
};
use alga::general::{ComplexField, ClosedAdd, ClosedDiv, ClosedMul, ClosedNeg, ClosedSub};
use alga::general::{ClosedAdd, ClosedDiv, ClosedMul, ClosedNeg, ClosedSub};
use alga::simd::{SimdPartialOrd, SimdSigned};
use crate::base::allocator::{Allocator, SameShapeAllocator, SameShapeC, SameShapeR};
use crate::base::constraint::{
@ -14,6 +15,7 @@ use crate::base::constraint::{
use crate::base::dimension::{Dim, DimMul, DimName, DimProd, Dynamic};
use crate::base::storage::{ContiguousStorageMut, Storage, StorageMut};
use crate::base::{DefaultAllocator, Matrix, MatrixMN, MatrixN, MatrixSum, Scalar, VectorSliceN};
use crate::SimdComplexField;
/*
*
@ -445,7 +447,9 @@ where
/// # use nalgebra::DMatrix;
/// iter::empty::<&DMatrix<f64>>().sum::<DMatrix<f64>>(); // panics!
/// ```
fn sum<I: Iterator<Item = &'a MatrixMN<N, Dynamic, C>>>(mut iter: I) -> MatrixMN<N, Dynamic, C> {
fn sum<I: Iterator<Item = &'a MatrixMN<N, Dynamic, C>>>(
mut iter: I,
) -> MatrixMN<N, Dynamic, C> {
if let Some(first) = iter.next() {
iter.fold(first.clone(), |acc, x| acc + x)
} else {
@ -692,11 +696,11 @@ where
/// Equivalent to `self.adjoint() * rhs`.
#[inline]
pub fn ad_mul<R2: Dim, C2: Dim, SB>(&self, rhs: &Matrix<N, R2, C2, SB>) -> MatrixMN<N, C1, C2>
where
N: ComplexField,
SB: Storage<N, R2, C2>,
DefaultAllocator: Allocator<N, C1, C2>,
ShapeConstraint: SameNumberOfRows<R1, R2>,
where
N: SimdComplexField,
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) };
@ -710,7 +714,10 @@ where
&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,
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>,
@ -760,7 +767,7 @@ where
rhs: &Matrix<N, R2, C2, SB>,
out: &mut Matrix<N, R3, C3, SC>,
) where
N: ComplexField,
N: SimdComplexField,
SB: Storage<N, R2, C2>,
SC: StorageMut<N, R3, C3>,
ShapeConstraint: SameNumberOfRows<R1, R2> + DimEq<C1, R3> + DimEq<C2, C3>,
@ -813,7 +820,8 @@ where
let coeff = self.get_unchecked((i1, j1)).inlined_clone();
for i2 in 0..nrows2.value() {
*data_res = coeff.inlined_clone() * rhs.get_unchecked((i2, j2)).inlined_clone();
*data_res = coeff.inlined_clone()
* rhs.get_unchecked((i2, j2)).inlined_clone();
data_res = data_res.offset(1);
}
}
@ -868,23 +876,6 @@ where
}
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, ordering: Ordering) -> N2
where N2: Scalar + PartialOrd + Zero {
let mut iter = self.iter();
let mut max = iter.next().cloned().map_or(N2::zero(), &abs);
for e in iter {
let ae = abs(e.inlined_clone());
if ae.partial_cmp(&max) == Some(ordering) {
max = ae;
}
}
max
}
/// Returns the absolute value of the component with the largest absolute value.
/// # Example
/// ```
@ -894,8 +885,11 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// ```
#[inline]
pub fn amax(&self) -> N
where N: PartialOrd + Signed {
self.xcmp(|e| e.abs(), Ordering::Greater)
where N: Zero + SimdSigned + SimdPartialOrd {
self.fold_with(
|e| e.unwrap_or(&N::zero()).simd_abs(),
|a, b| a.simd_max(b.simd_abs()),
)
}
/// Returns the the 1-norm of the complex component with the largest 1-norm.
@ -908,9 +902,12 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// Complex::new(1.0, 3.0)).camax(), 5.0);
/// ```
#[inline]
pub fn camax(&self) -> N::RealField
where N: ComplexField {
self.xcmp(|e| e.norm1(), Ordering::Greater)
pub fn camax(&self) -> N::SimdRealField
where N: SimdComplexField {
self.fold_with(
|e| e.unwrap_or(&N::zero()).simd_norm1(),
|a, b| a.simd_max(b.simd_norm1()),
)
}
/// Returns the component with the largest value.
@ -923,8 +920,11 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// ```
#[inline]
pub fn max(&self) -> N
where N: PartialOrd + Zero {
self.xcmp(|e| e, Ordering::Greater)
where N: SimdPartialOrd + Zero {
self.fold_with(
|e| e.map(|e| e.inlined_clone()).unwrap_or(N::zero()),
|a, b| a.simd_max(b.inlined_clone()),
)
}
/// Returns the absolute value of the component with the smallest absolute value.
@ -936,8 +936,11 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// ```
#[inline]
pub fn amin(&self) -> N
where N: PartialOrd + Signed {
self.xcmp(|e| e.abs(), Ordering::Less)
where N: Zero + SimdPartialOrd + SimdSigned {
self.fold_with(
|e| e.map(|e| e.simd_abs()).unwrap_or(N::zero()),
|a, b| a.simd_min(b.simd_abs()),
)
}
/// Returns the the 1-norm of the complex component with the smallest 1-norm.
@ -950,9 +953,15 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// Complex::new(1.0, 3.0)).camin(), 3.0);
/// ```
#[inline]
pub fn camin(&self) -> N::RealField
where N: ComplexField {
self.xcmp(|e| e.norm1(), Ordering::Less)
pub fn camin(&self) -> N::SimdRealField
where N: SimdComplexField {
self.fold_with(
|e| {
e.map(|e| e.simd_norm1())
.unwrap_or(N::SimdRealField::zero())
},
|a, b| a.simd_min(b.simd_norm1()),
)
}
/// Returns the component with the smallest value.
@ -965,7 +974,10 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
/// ```
#[inline]
pub fn min(&self) -> N
where N: PartialOrd + Zero {
self.xcmp(|e| e, Ordering::Less)
where N: SimdPartialOrd + Zero {
self.fold_with(
|e| e.map(|e| e.inlined_clone()).unwrap_or(N::zero()),
|a, b| a.simd_min(b.inlined_clone()),
)
}
}

View File

@ -110,8 +110,8 @@ extern crate generic_array;
#[cfg(feature = "std")]
extern crate matrixmultiply;
extern crate num_complex;
extern crate num_traits as num;
extern crate num_rational;
extern crate num_traits as num;
extern crate rand;
#[cfg(feature = "std")]
extern crate rand_distr;
@ -141,30 +141,31 @@ pub mod linalg;
#[cfg(feature = "sparse")]
pub mod sparse;
#[cfg(feature = "std")]
#[deprecated(
note = "The 'core' module is being renamed to 'base' to avoid conflicts with the 'core' crate."
)]
pub use base as core;
pub use crate::base::*;
pub use crate::geometry::*;
pub use crate::linalg::*;
#[cfg(feature = "sparse")]
pub use crate::sparse::*;
#[cfg(feature = "std")]
#[deprecated(
note = "The 'core' module is being renamed to 'base' to avoid conflicts with the 'core' crate."
)]
pub use base as core;
use std::cmp::{self, Ordering, PartialOrd};
use alga::general::{
Additive, AdditiveGroup, Identity, TwoSidedInverse, JoinSemilattice, Lattice, MeetSemilattice,
Multiplicative, SupersetOf,
Additive, AdditiveGroup, Identity, JoinSemilattice, Lattice, MeetSemilattice, Multiplicative,
SupersetOf, TwoSidedInverse,
};
use alga::linear::SquareMatrix as AlgaSquareMatrix;
use alga::linear::{EuclideanSpace, FiniteDimVectorSpace, InnerSpace, NormedSpace};
use num::Signed;
pub use alga::general::{Id, RealField, ComplexField};
#[allow(deprecated)]
pub use alga::general::Real;
pub use alga::general::{ComplexField, Id, RealField};
pub use alga::simd::{SimdComplexField, SimdRealField};
pub use num_complex::Complex;
/*