1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
// Matrix properties checks.
use approx::RelativeEq;
use num::{One, Zero};
use simba::scalar::{ClosedAdd, ClosedMul, ComplexField, RealField};
use crate::base::allocator::Allocator;
use crate::base::dimension::{Dim, DimMin};
use crate::base::storage::Storage;
use crate::base::{DefaultAllocator, Matrix, Scalar, SquareMatrix};
impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// The total number of elements of this matrix.
///
/// # Examples:
///
/// ```
/// # use nalgebra::Matrix3x4;
/// let mat = Matrix3x4::<f32>::zeros();
/// assert_eq!(mat.len(), 12);
/// ```
#[inline]
pub fn len(&self) -> usize {
let (nrows, ncols) = self.shape();
nrows * ncols
}
/// Returns true if the matrix contains no elements.
///
/// # Examples:
///
/// ```
/// # use nalgebra::Matrix3x4;
/// let mat = Matrix3x4::<f32>::zeros();
/// assert!(!mat.is_empty());
/// ```
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Indicates if this is a square matrix.
#[inline]
pub fn is_square(&self) -> bool {
let (nrows, ncols) = self.shape();
nrows == ncols
}
// TODO: RelativeEq prevents us from using those methods on integer matrices…
/// Indicated if this is the identity matrix within a relative error of `eps`.
///
/// If the matrix is diagonal, this checks that diagonal elements (i.e. at coordinates `(i, i)`
/// for i from `0` to `min(R, C)`) are equal one; and that all other elements are zero.
#[inline]
pub fn is_identity(&self, eps: T::Epsilon) -> bool
where
T: Zero + One + RelativeEq,
T::Epsilon: Copy,
{
let (nrows, ncols) = self.shape();
let d;
if nrows > ncols {
d = ncols;
for i in d..nrows {
for j in 0..ncols {
if !relative_eq!(self[(i, j)], T::zero(), epsilon = eps) {
return false;
}
}
}
} else {
// nrows <= ncols
d = nrows;
for i in 0..nrows {
for j in d..ncols {
if !relative_eq!(self[(i, j)], T::zero(), epsilon = eps) {
return false;
}
}
}
}
// Off-diagonal elements of the sub-square matrix.
for i in 1..d {
for j in 0..i {
// TODO: use unsafe indexing.
if !relative_eq!(self[(i, j)], T::zero(), epsilon = eps)
|| !relative_eq!(self[(j, i)], T::zero(), epsilon = eps)
{
return false;
}
}
}
// Diagonal elements of the sub-square matrix.
for i in 0..d {
if !relative_eq!(self[(i, i)], T::one(), epsilon = eps) {
return false;
}
}
true
}
}
impl<T: ComplexField, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// Checks that `Mᵀ × M = Id`.
///
/// In this definition `Id` is approximately equal to the identity matrix with a relative error
/// equal to `eps`.
#[inline]
pub fn is_orthogonal(&self, eps: T::Epsilon) -> bool
where
T: Zero + One + ClosedAdd + ClosedMul + RelativeEq,
S: Storage<T, R, C>,
T::Epsilon: Copy,
DefaultAllocator: Allocator<T, R, C> + Allocator<T, C, C>,
{
(self.ad_mul(self)).is_identity(eps)
}
}
impl<T: RealField, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S>
where
DefaultAllocator: Allocator<T, D, D>,
{
/// Checks that this matrix is orthogonal and has a determinant equal to 1.
#[inline]
pub fn is_special_orthogonal(&self, eps: T) -> bool
where
D: DimMin<D, Output = D>,
DefaultAllocator: Allocator<(usize, usize), D>,
{
self.is_square() && self.is_orthogonal(eps) && self.determinant() > T::zero()
}
/// Returns `true` if this matrix is invertible.
#[inline]
pub fn is_invertible(&self) -> bool {
// TODO: improve this?
self.clone_owned().try_inverse().is_some()
}
}