Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polar decomposition #656

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ee1eafc
Allow getting .min() and .max() of matrices of unsigned integers
koendeschacht Aug 30, 2019
d9a9c60
.min() and .max(): updated examples to be more concise
koendeschacht Aug 30, 2019
6fa096e
Fix multiplication between matrices of dimension 0.
sebcrozet Sep 1, 2019
5cb4d5f
Add test for empty matrix multiplication.
sebcrozet Sep 1, 2019
49abb14
Add test for empty matrix tr_mul.
sebcrozet Sep 1, 2019
94dd355
GEMM on empty matrices: properly take the beta parameter into account.
sebcrozet Sep 1, 2019
bde8fbe
Release v0.18.2
sebcrozet Sep 1, 2019
04eb817
Fixes #637: removes not used parameter from cross
Sep 5, 2019
04e322f
implemented LowerExp
Aug 19, 2019
8d756f4
Switched fmt implementation to a macro, applied that macro to all for…
Aug 21, 2019
17b2c81
First version of Polar decomposition. Bound traits to be implemented
Sep 24, 2019
4340f11
Added polar to module list
Sep 24, 2019
3e69550
Added polar to module list
Sep 24, 2019
b91d9bb
First compiling version
Sep 25, 2019
ef589fd
Removed redundant dependencies
Sep 25, 2019
5d03f04
Implementation of method to DMatrix
Sep 25, 2019
37a708c
Added serialization
Sep 25, 2019
062fb10
Added benchmarks
Sep 27, 2019
5c8f8ff
Fixed and cleaned benchmarks for polar decomposition
Sep 27, 2019
c239c78
Registered benchmarks
Sep 27, 2019
19576a4
Test written, however, compilation fails
Sep 27, 2019
e215b08
Added recompositions and fixed serde types
Sep 28, 2019
cbce9d5
Fixed bug in right decomposition
Oct 3, 2019
20aae95
Reenabled tests
Oct 3, 2019
7643eef
Fixed Serde compatibility
Oct 3, 2019
1e1994b
Added phantom marker to allow compiling in nightly
Oct 3, 2019
7d2e444
Added conditional guards for compilation without std/allocator
Oct 5, 2019
bc68baf
Removed dead code
Oct 5, 2019
1cd4134
Added property tests
Oct 5, 2019
dfd0cd3
Simplified code
Oct 15, 2019
95b669e
Fixed small bug
Oct 15, 2019
bb02dde
REbased
Jul 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "nalgebra"
version = "0.18.1"
version = "0.18.2"
authors = [ "Sébastien Crozet <developer@crozet.re>" ]

description = "Linear algebra library with transformations and statically-sized or dynamically-sized matrices."
Expand Down
3 changes: 2 additions & 1 deletion benches/lib.rs
Expand Up @@ -35,5 +35,6 @@ criterion_main!(
linalg::schur,
linalg::solve,
linalg::svd,
linalg::polar,
linalg::symmetric_eigen,
);
);
6 changes: 5 additions & 1 deletion benches/linalg/mod.rs
Expand Up @@ -7,6 +7,8 @@ pub use self::qr::qr;
pub use self::schur::schur;
pub use self::solve::solve;
pub use self::svd::svd;
#[cfg(any(feature = "std", feature = "alloc"))]
pub use self::polar::polar;
pub use self::symmetric_eigen::symmetric_eigen;

mod bidiagonal;
Expand All @@ -18,5 +20,7 @@ mod qr;
mod schur;
mod solve;
mod svd;
#[cfg(any(feature = "std", feature = "alloc"))]
mod polar;
mod symmetric_eigen;
// mod eigen;
// mod eigen;
28 changes: 28 additions & 0 deletions benches/linalg/polar.rs
@@ -0,0 +1,28 @@
use na::{DMatrix, Polar};

fn polar_decompose_4x4(bh: &mut criterion::Criterion) {
let m = DMatrix::<f64>::new_random(4,4);
bh.bench_function("polar_decompose_4x4", move |bh| bh.iter(|| test::black_box(Polar::new(m.clone()))));
}

fn polar_decompose_10x10(bh: &mut criterion::Criterion) {
let m = crate::reproductible_dmatrix(10, 10);
bh.bench_function("polar_decompose_10x10", move |bh| bh.iter(|| test::black_box(Polar::new(m.clone()))));
}

fn polar_decompose_100x100(bh: &mut criterion::Criterion) {
let m = crate::reproductible_dmatrix(100, 100);
bh.bench_function("polar_decompose_100x100", move |bh| bh.iter(|| test::black_box(Polar::new(m.clone()))));
}

fn polar_decompose_200x200(bh: &mut criterion::Criterion) {
let m = crate::reproductible_dmatrix(200, 200);
bh.bench_function("polar_decompose_200x200", move |bh| bh.iter(|| test::black_box(Polar::new(m.clone()))));
}

criterion_group!(polar,
polar_decompose_4x4,
polar_decompose_10x10,
polar_decompose_100x100,
polar_decompose_200x200,
);
2 changes: 1 addition & 1 deletion nalgebra-glm/src/geometric.rs
Expand Up @@ -4,7 +4,7 @@ use crate::aliases::{TVec, TVec3};
use crate::traits::{Alloc, Dimension, Number};

/// The cross product of two vectors.
pub fn cross<N: Number, D: Dimension>(x: &TVec3<N>, y: &TVec3<N>) -> TVec3<N> {
pub fn cross<N: Number>(x: &TVec3<N>, y: &TVec3<N>) -> TVec3<N> {
x.cross(y)
}

Expand Down
169 changes: 97 additions & 72 deletions src/base/blas.rs
Expand Up @@ -565,6 +565,14 @@ where
);

if ncols2 == 0 {
// NOTE: we can't just always multiply by beta
// because we documented the guaranty that `self` is
// never read if `beta` is zero.
if beta.is_zero() {
self.fill(N::zero());
} else {
*self *= beta;
}
return;
}

Expand Down Expand Up @@ -991,92 +999,109 @@ where N: Scalar + Zero + ClosedAdd + ClosedMul

#[cfg(feature = "std")]
{
// matrixmultiply can be used only if the std feature is available.
let nrows1 = self.nrows();
let (nrows2, ncols2) = a.shape();
let (nrows3, ncols3) = b.shape();

assert_eq!(
ncols2, nrows3,
"gemm: dimensions mismatch for multiplication."
);
assert_eq!(
(nrows1, ncols1),
(nrows2, ncols3),
"gemm: dimensions mismatch for addition."
);

// We assume large matrices will be Dynamic but small matrices static.
// We could use matrixmultiply for large statically-sized matrices but the performance
// threshold to activate it would be different from SMALL_DIM because our code optimizes
// better for statically-sized matrices.
let is_dynamic = R1::is::<Dynamic>()
if R1::is::<Dynamic>()
|| C1::is::<Dynamic>()
|| R2::is::<Dynamic>()
|| C2::is::<Dynamic>()
|| R3::is::<Dynamic>()
|| C3::is::<Dynamic>();
// Threshold determined empirically.
const SMALL_DIM: usize = 5;

if is_dynamic
&& nrows1 > SMALL_DIM
&& ncols1 > SMALL_DIM
&& nrows2 > SMALL_DIM
&& ncols2 > SMALL_DIM
{
if N::is::<f32>() {
let (rsa, csa) = a.strides();
let (rsb, csb) = b.strides();
let (rsc, csc) = self.strides();

unsafe {
matrixmultiply::sgemm(
nrows2,
ncols2,
ncols3,
mem::transmute_copy(&alpha),
a.data.ptr() as *const f32,
rsa as isize,
csa as isize,
b.data.ptr() as *const f32,
rsb as isize,
csb as isize,
mem::transmute_copy(&beta),
self.data.ptr_mut() as *mut f32,
rsc as isize,
csc as isize,
);
|| C3::is::<Dynamic>() {
// matrixmultiply can be used only if the std feature is available.
let nrows1 = self.nrows();
let (nrows2, ncols2) = a.shape();
let (nrows3, ncols3) = b.shape();

// Threshold determined empirically.
const SMALL_DIM: usize = 5;

if nrows1 > SMALL_DIM
&& ncols1 > SMALL_DIM
&& nrows2 > SMALL_DIM
&& ncols2 > SMALL_DIM
{
assert_eq!(
ncols2, nrows3,
"gemm: dimensions mismatch for multiplication."
);
assert_eq!(
(nrows1, ncols1),
(nrows2, ncols3),
"gemm: dimensions mismatch for addition."
);

// NOTE: this case should never happen because we enter this
// codepath only when ncols2 > SMALL_DIM. Though we keep this
// here just in case if in the future we change the conditions to
// enter this codepath.
if ncols2 == 0 {
// NOTE: we can't just always multiply by beta
// because we documented the guaranty that `self` is
// never read if `beta` is zero.
if beta.is_zero() {
self.fill(N::zero());
} else {
*self *= beta;
}
return;
}
return;
} else if N::is::<f64>() {
let (rsa, csa) = a.strides();
let (rsb, csb) = b.strides();
let (rsc, csc) = self.strides();

unsafe {
matrixmultiply::dgemm(
nrows2,
ncols2,
ncols3,
mem::transmute_copy(&alpha),
a.data.ptr() as *const f64,
rsa as isize,
csa as isize,
b.data.ptr() as *const f64,
rsb as isize,
csb as isize,
mem::transmute_copy(&beta),
self.data.ptr_mut() as *mut f64,
rsc as isize,
csc as isize,
);

if N::is::<f32>() {
let (rsa, csa) = a.strides();
let (rsb, csb) = b.strides();
let (rsc, csc) = self.strides();

unsafe {
matrixmultiply::sgemm(
nrows2,
ncols2,
ncols3,
mem::transmute_copy(&alpha),
a.data.ptr() as *const f32,
rsa as isize,
csa as isize,
b.data.ptr() as *const f32,
rsb as isize,
csb as isize,
mem::transmute_copy(&beta),
self.data.ptr_mut() as *mut f32,
rsc as isize,
csc as isize,
);
}
return;
} else if N::is::<f64>() {
let (rsa, csa) = a.strides();
let (rsb, csb) = b.strides();
let (rsc, csc) = self.strides();

unsafe {
matrixmultiply::dgemm(
nrows2,
ncols2,
ncols3,
mem::transmute_copy(&alpha),
a.data.ptr() as *const f64,
rsa as isize,
csa as isize,
b.data.ptr() as *const f64,
rsb as isize,
csb as isize,
mem::transmute_copy(&beta),
self.data.ptr_mut() as *mut f64,
rsc as isize,
csc as isize,
);
}
return;
}
return;
}
}
}


for j1 in 0..ncols1 {
// FIXME: avoid bound checks.
self.column_mut(j1).gemv(alpha, a, &b.column(j1), beta);
Expand Down