nalgebra/src/base/matrix.rs
Violeta Hernández 2243a11e89 Added some derives
2021-07-29 12:33:45 -05:00

2375 lines
80 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use num::{One, Zero};
#[cfg(feature = "abomonation-serialize")]
use std::io::{Result as IOResult, Write};
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use std::any::TypeId;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop, MaybeUninit};
use std::ptr;
#[cfg(feature = "serde-serialize-no-std")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "abomonation-serialize")]
use abomonation::Abomonation;
use simba::scalar::{ClosedAdd, ClosedMul, ClosedSub, Field, SupersetOf};
use simba::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::{
ColumnIter, ColumnIterMut, MatrixIter, MatrixIterMut, RowIter, RowIterMut,
};
use crate::base::storage::{
ContiguousStorage, ContiguousStorageMut, SameShapeStorage, Storage, StorageMut,
};
use crate::base::{Const, DefaultAllocator, OMatrix, OVector, Scalar, Unit};
use crate::{ArrayStorage, MatrixSlice, MatrixSliceMut, SMatrix, SimdComplexField};
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::{DMatrix, DVector, Dynamic, VecStorage};
/// A square matrix.
pub type SquareMatrix<T, D, S> = Matrix<T, D, D, S>;
/// A matrix with one column and `D` rows.
pub type Vector<T, D, S> = Matrix<T, D, U1, S>;
/// A matrix with one row and `D` columns .
pub type RowVector<T, D, S> = Matrix<T, U1, D, S>;
/// The type of the result of a matrix sum.
pub type MatrixSum<T, R1, C1, R2, C2> =
Matrix<T, SameShapeR<R1, R2>, SameShapeC<C1, C2>, SameShapeStorage<T, R1, C1, R2, C2>>;
/// The type of the result of a matrix sum.
pub type VectorSum<T, R1, R2> =
Matrix<T, SameShapeR<R1, R2>, U1, SameShapeStorage<T, R1, U1, R2, U1>>;
/// The type of the result of a matrix cross product.
pub type MatrixCross<T, R1, C1, R2, C2> =
Matrix<T, SameShapeR<R1, R2>, SameShapeC<C1, C2>, SameShapeStorage<T, R1, C1, R2, C2>>;
/// The most generic column-major matrix (and vector) type.
///
/// # Methods summary
/// Because `Matrix` is the most generic types used as a common representation of all matrices and
/// vectors of **nalgebra** this documentation page contains every single matrix/vector-related
/// method. In order to make browsing this page simpler, the next subsections contain direct links
/// to groups of methods related to a specific topic.
///
/// #### Vector and matrix construction
/// - [Constructors of statically-sized vectors or statically-sized matrices](#constructors-of-statically-sized-vectors-or-statically-sized-matrices)
/// (`Vector3`, `Matrix3x6`…)
/// - [Constructors of fully dynamic matrices](#constructors-of-fully-dynamic-matrices) (`DMatrix`)
/// - [Constructors of dynamic vectors and matrices with a dynamic number of rows](#constructors-of-dynamic-vectors-and-matrices-with-a-dynamic-number-of-rows)
/// (`DVector`, `MatrixXx3`…)
/// - [Constructors of matrices with a dynamic number of columns](#constructors-of-matrices-with-a-dynamic-number-of-columns)
/// (`Matrix2xX`…)
/// - [Generic constructors](#generic-constructors)
/// (For code generic wrt. the vectors or matrices dimensions.)
///
/// #### Computer graphics utilities for transformations
/// - [2D transformations as a Matrix3 <span style="float:right;">`new_rotation`…</span>](#2d-transformations-as-a-matrix3)
/// - [3D transformations as a Matrix4 <span style="float:right;">`new_rotation`, `new_perspective`, `look_at_rh`…</span>](#3d-transformations-as-a-matrix4)
/// - [Translation and scaling in any dimension <span style="float:right;">`new_scaling`, `new_translation`…</span>](#translation-and-scaling-in-any-dimension)
/// - [Append/prepend translation and scaling <span style="float:right;">`append_scaling`, `prepend_translation_mut`…</span>](#appendprepend-translation-and-scaling)
/// - [Transformation of vectors and points <span style="float:right;">`transform_vector`, `transform_point`…</span>](#transformation-of-vectors-and-points)
///
/// #### Common math operations
/// - [Componentwise operations <span style="float:right;">`component_mul`, `component_div`, `inf`…</span>](#componentwise-operations)
/// - [Special multiplications <span style="float:right;">`tr_mul`, `ad_mul`, `kronecker`…</span>](#special-multiplications)
/// - [Dot/scalar product <span style="float:right;">`dot`, `dotc`, `tr_dot`…</span>](#dotscalar-product)
/// - [Cross product <span style="float:right;">`cross`, `perp`…</span>](#cross-product)
/// - [Magnitude and norms <span style="float:right;">`norm`, `normalize`, `metric_distance`…</span>](#magnitude-and-norms)
/// - [In-place normalization <span style="float:right;">`normalize_mut`, `try_normalize_mut`…</span>](#in-place-normalization)
/// - [Interpolation <span style="float:right;">`lerp`, `slerp`…</span>](#interpolation)
/// - [BLAS functions <span style="float:right;">`gemv`, `gemm`, `syger`…</span>](#blas-functions)
/// - [Swizzling <span style="float:right;">`xx`, `yxz`…</span>](#swizzling)
///
/// #### Statistics
/// - [Common operations <span style="float:right;">`row_sum`, `column_mean`, `variance`…</span>](#common-statistics-operations)
/// - [Find the min and max components <span style="float:right;">`min`, `max`, `amin`, `amax`, `camin`, `cmax`…</span>](#find-the-min-and-max-components)
/// - [Find the min and max components (vector-specific methods) <span style="float:right;">`argmin`, `argmax`, `icamin`, `icamax`…</span>](#find-the-min-and-max-components-vector-specific-methods)
///
/// #### Iteration, map, and fold
/// - [Iteration on components, rows, and columns <span style="float:right;">`iter`, `column_iter`…</span>](#iteration-on-components-rows-and-columns)
/// - [Elementwise mapping and folding <span style="float:right;">`map`, `fold`, `zip_map`…</span>](#elementwise-mapping-and-folding)
/// - [Folding or columns and rows <span style="float:right;">`compress_rows`, `compress_columns`…</span>](#folding-on-columns-and-rows)
///
/// #### Vector and matrix slicing
/// - [Creating matrix slices from `&[T]` <span style="float:right;">`from_slice`, `from_slice_with_strides`…</span>](#creating-matrix-slices-from-t)
/// - [Creating mutable matrix slices from `&mut [T]` <span style="float:right;">`from_slice_mut`, `from_slice_with_strides_mut`…</span>](#creating-mutable-matrix-slices-from-mut-t)
/// - [Slicing based on index and length <span style="float:right;">`row`, `columns`, `slice`…</span>](#slicing-based-on-index-and-length)
/// - [Mutable slicing based on index and length <span style="float:right;">`row_mut`, `columns_mut`, `slice_mut`…</span>](#mutable-slicing-based-on-index-and-length)
/// - [Slicing based on ranges <span style="float:right;">`rows_range`, `columns_range`…</span>](#slicing-based-on-ranges)
/// - [Mutable slicing based on ranges <span style="float:right;">`rows_range_mut`, `columns_range_mut`…</span>](#mutable-slicing-based-on-ranges)
///
/// #### In-place modification of a single matrix or vector
/// - [In-place filling <span style="float:right;">`fill`, `fill_diagonal`, `fill_with_identity`…</span>](#in-place-filling)
/// - [In-place swapping <span style="float:right;">`swap`, `swap_columns`…</span>](#in-place-swapping)
/// - [Set rows, columns, and diagonal <span style="float:right;">`set_column`, `set_diagonal`…</span>](#set-rows-columns-and-diagonal)
///
/// #### Vector and matrix size modification
/// - [Rows and columns insertion <span style="float:right;">`insert_row`, `insert_column`…</span>](#rows-and-columns-insertion)
/// - [Rows and columns removal <span style="float:right;">`remove_row`, `remove column`…</span>](#rows-and-columns-removal)
/// - [Rows and columns extraction <span style="float:right;">`select_rows`, `select_columns`…</span>](#rows-and-columns-extraction)
/// - [Resizing and reshaping <span style="float:right;">`resize`, `reshape_generic`…</span>](#resizing-and-reshaping)
/// - [In-place resizing <span style="float:right;">`resize_mut`, `resize_vertically_mut`…</span>](#in-place-resizing)
///
/// #### Matrix decomposition
/// - [Rectangular matrix decomposition <span style="float:right;">`qr`, `lu`, `svd`…</span>](#rectangular-matrix-decomposition)
/// - [Square matrix decomposition <span style="float:right;">`cholesky`, `symmetric_eigen`…</span>](#square-matrix-decomposition)
///
/// #### Vector basis computation
/// - [Basis and orthogonalization <span style="float:right;">`orthonormal_subspace_basis`, `orthonormalize`…</span>](#basis-and-orthogonalization)
///
/// # Type parameters
/// The generic `Matrix` type has four type parameters:
/// - `T`: for the matrix components scalar type.
/// - `R`: for the matrix number of rows.
/// - `C`: for the matrix number of columns.
/// - `S`: for the matrix data storage, i.e., the buffer that actually contains the matrix
/// components.
///
/// The matrix dimensions parameters `R` and `C` can either be:
/// - type-level unsigned integer constants (e.g. `U1`, `U124`) from the `nalgebra::` root module.
/// All numbers from 0 to 127 are defined that way.
/// - type-level unsigned integer constants (e.g. `U1024`, `U10000`) from the `typenum::` crate.
/// Using those, you will not get error messages as nice as for numbers smaller than 128 defined on
/// the `nalgebra::` module.
/// - the special value `Dynamic` from the `nalgebra::` root module. This indicates that the
/// specified dimension is not known at compile-time. Note that this will generally imply that the
/// matrix data storage `S` performs a dynamic allocation and contains extra metadata for the
/// matrix shape.
///
/// Note that mixing `Dynamic` with type-level unsigned integers is allowed. Actually, a
/// dynamically-sized column vector should be represented as a `Matrix<T, Dynamic, U1, S>` (given
/// some concrete types for `T` and a compatible data storage type `S`).
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct Matrix<T, R, C, S> {
/// The data storage that contains all the matrix components. Disappointed?
///
/// Well, if you came here to see how you can access the matrix components,
/// you may be in luck: you can access the individual components of all vectors with compile-time
/// dimensions <= 6 using field notation like this:
/// `vec.x`, `vec.y`, `vec.z`, `vec.w`, `vec.a`, `vec.b`. Reference and assignation work too:
/// ```
/// # use nalgebra::Vector3;
/// let mut vec = Vector3::new(1.0, 2.0, 3.0);
/// vec.x = 10.0;
/// vec.y += 30.0;
/// assert_eq!(vec.x, 10.0);
/// assert_eq!(vec.y + 100.0, 132.0);
/// ```
/// Similarly, for matrices with compile-time dimensions <= 6, you can use field notation
/// like this: `mat.m11`, `mat.m42`, etc. The first digit identifies the row to address
/// and the second digit identifies the column to address. So `mat.m13` identifies the component
/// at the first row and third column (note that the count of rows and columns start at 1 instead
/// of 0 here. This is so we match the mathematical notation).
///
/// For all matrices and vectors, independently from their size, individual components can
/// be accessed and modified using indexing: `vec[20]`, `mat[(20, 19)]`. Here the indexing
/// starts at 0 as you would expect.
pub data: S,
// NOTE: the fact that this field is private is important because
// this prevents the user from constructing a matrix with
// dimensions R, C that don't match the dimension of the
// storage S. Instead they have to use the unsafe function
// from_data_statically_unchecked.
// Note that it would probably make sense to just have
// the type `Matrix<S>`, and have `T, R, C` be associated-types
// of the `Storage` trait. However, because we don't have
// specialization, this is not possible because these `T, R, C`
// allows us to disambiguate a lot of configurations.
_phantoms: PhantomData<(T, R, C)>,
}
impl<T, R: Dim, C: Dim, S> Default for Matrix<T, R, C, S>
where
S: Storage<T, R, C> + Default,
{
fn default() -> Self {
Matrix::from_data(Default::default())
}
}
#[cfg(feature = "serde-serialize-no-std")]
impl<T, R: Dim, C: Dim, S: Serialize> Serialize for Matrix<T, R, C, S> {
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
where
Ser: Serializer,
{
self.data.serialize(serializer)
}
}
#[cfg(feature = "serde-serialize-no-std")]
impl<'de, T, R: Dim, C, S: Serialize<'de>> Deserialize<'de> for Matrix<T, R, C, S> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
S::deserialize(deserializer).map(|x| Matrix {
data: x,
_phantoms: PhantomData,
})
}
}
#[cfg(feature = "abomonation-serialize")]
impl<T, R: Dim, C: Dim, S: Abomonation> Abomonation for Matrix<T, R, C, S> {
unsafe fn entomb<W: Write>(&self, writer: &mut W) -> IOResult<()> {
self.data.entomb(writer)
}
unsafe fn exhume<'a, 'b>(&'a mut self, bytes: &'b mut [u8]) -> Option<&'b mut [u8]> {
self.data.exhume(bytes)
}
fn extent(&self) -> usize {
self.data.extent()
}
}
#[cfg(feature = "compare")]
impl<T: Clone, R: Dim, C: Dim, S: Storage<T, R, C>> matrixcompare_core::Matrix<T>
for Matrix<T, R, C, S>
{
fn rows(&self) -> usize {
self.nrows()
}
fn cols(&self) -> usize {
self.ncols()
}
fn access(&self) -> matrixcompare_core::Access<T> {
matrixcompare_core::Access::Dense(self)
}
}
#[cfg(feature = "compare")]
impl<T: Clone, R: Dim, C: Dim, S: Storage<T, R, C>> matrixcompare_core::DenseAccess<T>
for Matrix<T, R, C, S>
{
fn fetch_single(&self, row: usize, col: usize) -> T {
self.index((row, col)).clone()
}
}
#[cfg(feature = "bytemuck")]
unsafe impl<T, R: Dim, C: Dim, S: Storage<T, R, C>> bytemuck::Zeroable for Matrix<T, R, C, S> where
S: bytemuck::Zeroable
{
}
#[cfg(feature = "bytemuck")]
unsafe impl<T, R: Dim, C: Dim, S: Storage<T, R, C>> bytemuck::Pod for Matrix<T, R, C, S>
where
S: bytemuck::Pod,
Self: Copy,
{
}
#[cfg(feature = "rkyv-serialize-no-std")]
mod rkyv_impl {
use super::Matrix;
use core::marker::PhantomData;
use rkyv::{offset_of, project_struct, Archive, Deserialize, Fallible, Serialize};
impl<T: Archive, R: Archive, C: Archive, S: Archive> Archive for Matrix<T, R, C, S> {
type Archived = Matrix<T::Archived, R::Archived, C::Archived, S::Archived>;
type Resolver = S::Resolver;
fn resolve(
&self,
pos: usize,
resolver: Self::Resolver,
out: &mut core::mem::MaybeUninit<Self::Archived>,
) {
self.data.resolve(
pos + offset_of!(Self::Archived, data),
resolver,
project_struct!(out: Self::Archived => data),
);
}
}
impl<T: Archive, R: Archive, C: Archive, S: Serialize<_S>, _S: Fallible + ?Sized> Serialize<_S>
for Matrix<T, R, C, S>
{
fn serialize(&self, serializer: &mut _S) -> Result<Self::Resolver, _S::Error> {
self.data.serialize(serializer)
}
}
impl<T: Archive, R: Archive, C: Archive, S: Archive, D: Fallible + ?Sized>
Deserialize<Matrix<T, R, C, S>, D>
for Matrix<T::Archived, R::Archived, C::Archived, S::Archived>
where
S::Archived: Deserialize<S, D>,
{
fn deserialize(&self, deserializer: &mut D) -> Result<Matrix<T, R, C, S>, D::Error> {
Ok(Matrix {
data: self.data.deserialize(deserializer)?,
_phantoms: PhantomData,
})
}
}
}
impl<T, R, C, S> Matrix<T, R, C, S> {
/// Creates a new matrix with the given data without statically checking
/// that the matrix dimension matches the storage dimension.
///
/// There's only two instances in which you should use this method instead
/// of the safe counterpart [`from_data`]:
/// - You can't get the type checker to validate your matrices, even though
/// you're **certain** that they're of the right dimensions.
/// - You want to declare a matrix in a `const` context.
///
/// # Safety
/// If the storage dimension does not match the matrix dimension, any other
/// method called on this matrix may behave erroneously, panic, or cause
/// Undefined Behavior.
#[inline(always)]
pub const unsafe fn from_data_statically_unchecked(data: S) -> Matrix<T, R, C, S> {
Matrix {
data,
_phantoms: PhantomData,
}
}
}
/// # Memory manipulation methods.
impl<T, R: Dim, C: Dim> OMatrix<T, R, C>
where
DefaultAllocator: Allocator<T, R, C>,
{
/// Allocates a matrix with the given number of rows and columns without initializing its content.
pub fn new_uninitialized_generic(nrows: R, ncols: C) -> OMatrix<MaybeUninit<T>, R, C> {
OMatrix::from_data(
<DefaultAllocator as Allocator<T, R, C>>::allocate_uninitialized(nrows, ncols),
)
}
/// Converts this matrix into one whose entries need to be manually dropped. This should be
/// near zero-cost.
pub fn manually_drop(self) -> OMatrix<ManuallyDrop<T>, R, C> {
OMatrix::from_data(<DefaultAllocator as Allocator<T, R, C>>::manually_drop(
self.data,
))
}
}
/// # More memory manipulation methods.
impl<T, R: Dim, C: Dim> OMatrix<MaybeUninit<T>, R, C>
where
DefaultAllocator: Allocator<T, R, C>,
{
/// Assumes a matrix's entries to be initialized. This operation should be near zero-cost.
///
/// For the similar method that operates on matrix slices, see [`slice_assume_init`].
///
/// # Safety
/// The user must make sure that every single entry of the buffer has been initialized,
/// or Undefined Behavior will immediately occur.
pub unsafe fn assume_init(self) -> OMatrix<T, R, C> {
OMatrix::from_data(<DefaultAllocator as Allocator<T, R, C>>::assume_init(
self.data,
))
}
/// Assumes a matrix's entries to be initialized, and drops them in place.
/// This allows the buffer to be safely reused.
///
/// # Safety
/// All of the matrix's entries need to be uninitialized. Otherwise,
/// Undefined Behavior will be triggered.
pub unsafe fn reinitialize(&mut self) {
for i in 0..self.nrows() {
for j in 0..self.ncols() {
ptr::drop_in_place(self.get_unchecked_mut((i, j)));
}
}
}
}
impl<T, R: Dim, C: Dim, S> Matrix<MaybeUninit<T>, R, C, S> {
/// Creates a full slice from `self` and assumes it to be initialized.
///
/// # Safety
/// The user must make sure that every single entry of the buffer has been initialized,
/// or Undefined Behavior will immediately occur.
pub unsafe fn assume_init_ref(&self) -> MatrixSlice<T, R, C, S::RStride, S::CStride>
where
S: Storage<MaybeUninit<T>, R, C>,
{
self.full_slice().slice_assume_init()
}
/// Creates a full mutable slice from `self` and assumes it to be initialized.
///
/// # Safety
/// The user must make sure that every single entry of the buffer has been initialized,
/// or Undefined Behavior will immediately occur.
pub unsafe fn assume_init_mut(&mut self) -> MatrixSliceMut<T, R, C, S::RStride, S::CStride>
where
S: StorageMut<MaybeUninit<T>, R, C>,
{
self.full_slice_mut().slice_assume_init()
}
}
impl<T, const R: usize, const C: usize> SMatrix<T, R, C> {
/// Creates a new statically-allocated matrix from the given [ArrayStorage].
///
/// This method exists primarily as a workaround for the fact that `from_data` can not
/// work in `const fn` contexts.
#[inline(always)]
pub const fn from_array_storage(storage: ArrayStorage<T, R, C>) -> Self {
// Safety: This is sound because the row and column types are exactly
// the same as that of the storage, so there can be no mismatch.
unsafe { Self::from_data_statically_unchecked(storage) }
}
}
// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make
// `from_data` const fn compatible
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> DMatrix<T> {
/// Creates a new heap-allocated matrix from the given [VecStorage].
///
/// This method exists primarily as a workaround for the fact that `from_data` can not
/// work in `const fn` contexts.
pub const fn from_vec_storage(storage: VecStorage<T, Dynamic, Dynamic>) -> Self {
// Safety: This is sound because the dimensions of the matrix and the
// storage are guaranteed to be the same.
unsafe { Self::from_data_statically_unchecked(storage) }
}
}
// TODO: Consider removing/deprecating `from_vec_storage` once we are able to make
// `from_data` const fn compatible
#[cfg(any(feature = "std", feature = "alloc"))]
impl<T> DVector<T> {
/// Creates a new heap-allocated matrix from the given [VecStorage].
///
/// This method exists primarily as a workaround for the fact that `from_data` can not
/// work in `const fn` contexts.
pub const fn from_vec_storage(storage: VecStorage<T, Dynamic, U1>) -> Self {
// Safety: This is sound because the dimensions of the matrix and the
// storage are guaranteed to be the same.
unsafe { Self::from_data_statically_unchecked(storage) }
}
}
impl<T, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// Creates a new matrix with the given data.
#[inline(always)]
pub fn from_data(data: S) -> Self {
// Safety: This is sound because the dimensions of the matrix and the
// storage are guaranteed to be the same.
unsafe { Self::from_data_statically_unchecked(data) }
}
/// The shape of this matrix returned as the tuple (number of rows, number of columns).
///
/// # Examples:
///
/// ```
/// # use nalgebra::Matrix3x4;
/// let mat = Matrix3x4::<f32>::zeros();
/// assert_eq!(mat.shape(), (3, 4));
#[inline]
#[must_use]
pub fn shape(&self) -> (usize, usize) {
let (nrows, ncols) = self.data.shape();
(nrows.value(), ncols.value())
}
/// The number of rows of this matrix.
///
/// # Examples:
///
/// ```
/// # use nalgebra::Matrix3x4;
/// let mat = Matrix3x4::<f32>::zeros();
/// assert_eq!(mat.nrows(), 3);
#[inline]
#[must_use]
pub fn nrows(&self) -> usize {
self.shape().0
}
/// The number of columns of this matrix.
///
/// # Examples:
///
/// ```
/// # use nalgebra::Matrix3x4;
/// let mat = Matrix3x4::<f32>::zeros();
/// assert_eq!(mat.ncols(), 4);
#[inline]
#[must_use]
pub fn ncols(&self) -> usize {
self.shape().1
}
/// The strides (row stride, column stride) of this matrix.
///
/// # Examples:
///
/// ```
/// # use nalgebra::DMatrix;
/// let mat = DMatrix::<f32>::zeros(10, 10);
/// let slice = mat.slice_with_steps((0, 0), (5, 3), (1, 2));
/// // The column strides is the number of steps (here 2) multiplied by the corresponding dimension.
/// assert_eq!(mat.strides(), (1, 10));
/// ```
#[inline]
#[must_use]
pub fn strides(&self) -> (usize, usize) {
let (srows, scols) = self.data.strides();
(srows.value(), scols.value())
}
/// Computes the row and column coordinates of the i-th element of this matrix seen as a
/// vector.
///
/// # Example
/// ```
/// # use nalgebra::Matrix2;
/// let m = Matrix2::new(1, 2,
/// 3, 4);
/// let i = m.vector_to_matrix_index(3);
/// assert_eq!(i, (1, 1));
/// assert_eq!(m[i], m[3]);
/// ```
#[inline]
#[must_use]
pub fn vector_to_matrix_index(&self, i: usize) -> (usize, usize) {
let (nrows, ncols) = self.shape();
// Two most common uses that should be optimized by the compiler for statically-sized
// matrices.
if nrows == 1 {
(0, i)
} else if ncols == 1 {
(i, 0)
} else {
(i % nrows, i / nrows)
}
}
/// Returns a pointer to the start of the matrix.
///
/// If the matrix is not empty, this pointer is guaranteed to be aligned
/// and non-null.
///
/// # Example
/// ```
/// # use nalgebra::Matrix2;
/// let m = Matrix2::new(1, 2,
/// 3, 4);
/// let ptr = m.as_ptr();
/// assert_eq!(unsafe { *ptr }, m[0]);
/// ```
#[inline]
#[must_use]
pub fn as_ptr(&self) -> *const T {
self.data.ptr()
}
/// Tests whether `self` and `rhs` are equal up to a given epsilon.
///
/// See `relative_eq` from the `RelativeEq` trait for more details.
#[inline]
#[must_use]
pub fn relative_eq<R2: Dim, C2: Dim, SB>(
&self,
other: &Matrix<T, R2, C2, SB>,
eps: T::Epsilon,
max_relative: T::Epsilon,
) -> bool
where
T: RelativeEq,
SB: Storage<T, R2, C2>,
T::Epsilon: Copy,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
assert!(self.shape() == other.shape());
self.iter()
.zip(other.iter())
.all(|(a, b)| a.relative_eq(b, eps, max_relative))
}
/// Tests whether `self` and `rhs` are exactly equal.
#[inline]
#[must_use]
#[allow(clippy::should_implement_trait)]
pub fn eq<R2: Dim, C2: Dim, SB>(&self, other: &Matrix<T, R2, C2, SB>) -> bool
where
T: PartialEq,
SB: Storage<T, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
assert!(self.shape() == other.shape());
self.iter().zip(other.iter()).all(|(a, b)| *a == *b)
}
/// Moves this matrix into one that owns its data.
#[inline]
pub fn into_owned(self) -> OMatrix<T, R, C>
where
T: Clone,
DefaultAllocator: Allocator<T, R, C>,
{
Matrix::from_data(self.data.into_owned().0)
}
// TODO: this could probably benefit from specialization.
// XXX: bad name.
/// Moves this matrix into one that owns its data. The actual type of the result depends on
/// matrix storage combination rules for addition.
#[inline]
pub fn into_owned_sum<R2: Dim, C2: Dim>(self) -> MatrixSum<T, R, C, R2, C2>
where
T: Clone,
DefaultAllocator: SameShapeAllocator<T, R, C, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
// If both storages are the same, we can just return `self.into_owned()`.
// Unfortunately, it's not trivial to convince the compiler of this.
if TypeId::of::<SameShapeR<R, R2>>() == TypeId::of::<R>()
&& TypeId::of::<SameShapeC<C, C2>>() == TypeId::of::<C>()
{
// Safety: we're transmuting from a type into itself, and we make
// sure not to leak anything.
unsafe {
let mat = self.into_owned();
let mat_copy = mem::transmute_copy(&mat);
mem::forget(mat);
mat_copy
}
} else {
self.clone_owned_sum()
}
}
/// Clones this matrix to one that owns its data.
#[inline]
#[must_use]
pub fn clone_owned(&self) -> OMatrix<T, R, C>
where
T: Clone,
DefaultAllocator: Allocator<T, R, C>,
{
Matrix::from_data(self.data.clone_owned().0)
}
/// Clones this matrix into one that owns its data. The actual type of the result depends on
/// matrix storage combination rules for addition.
#[inline]
#[must_use]
pub fn clone_owned_sum<R2: Dim, C2: Dim>(&self) -> MatrixSum<T, R, C, R2, C2>
where
T: Clone,
DefaultAllocator: SameShapeAllocator<T, R, C, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
let (nrows, ncols) = self.shape();
let nrows: SameShapeR<R, R2> = Dim::from_usize(nrows);
let ncols: SameShapeC<C, C2> = Dim::from_usize(ncols);
let mut res = Matrix::new_uninitialized_generic(nrows, ncols);
// TODO: use copy_from
for j in 0..res.ncols() {
for i in 0..res.nrows() {
unsafe {
*res.get_unchecked_mut((i, j)) =
MaybeUninit::new(self.get_unchecked((i, j)).clone());
}
}
}
unsafe { res.assume_init() }
}
/// Transposes `self` and store the result into `out`, which will become
/// fully initialized.
#[inline]
pub fn transpose_to<R2: Dim, C2: Dim, SB>(&self, out: &mut Matrix<MaybeUninit<T>, R2, C2, SB>)
where
T: Clone,
SB: StorageMut<MaybeUninit<T>, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, C2> + SameNumberOfColumns<C, R2>,
{
let (nrows, ncols) = self.shape();
assert!(
(ncols, nrows) == out.shape(),
"Incompatible shape for transpose-copy."
);
// TODO: optimize that.
for i in 0..nrows {
for j in 0..ncols {
unsafe {
*out.get_unchecked_mut((j, i)) =
MaybeUninit::new(self.get_unchecked((i, j)).clone());
}
}
}
}
/// Transposes `self`.
#[inline]
#[must_use = "Did you mean to use transpose_mut()?"]
pub fn transpose(&self) -> OMatrix<T, C, R>
where
T: Clone,
DefaultAllocator: Allocator<T, C, R>,
{
let (nrows, ncols) = self.data.shape();
let mut res = OMatrix::new_uninitialized_generic(ncols, nrows);
self.transpose_to(&mut res);
unsafe {
// Safety: res is now fully initialized due to the guarantees of transpose_to.
res.assume_init()
}
}
}
impl<T, R: Dim, C: Dim> OMatrix<T, R, C>
where
DefaultAllocator: Allocator<T, C, R> + Allocator<T, R, C>,
{
/// Transposes `self`. Does not require `T: Clone` like its other counterparts.
pub fn transpose_into(self) -> OMatrix<T, C, R> {
let (nrows, ncols) = self.data.shape();
let mut res = OMatrix::new_uninitialized_generic(ncols, nrows);
let mut md = self.manually_drop();
let (nrows, ncols) = res.shape();
// TODO: optimize that.
for i in 0..nrows {
for j in 0..ncols {
// Safety: the indices are within range, and since the indices
// don't repeat, we don't do any double-drops.
unsafe {
*res.get_unchecked_mut((j, i)) =
MaybeUninit::new(ManuallyDrop::take(md.get_unchecked_mut((i, j))));
}
}
}
unsafe {
// Safety: res is now fully initialized, since we've initialized
// every single entry.
res.assume_init()
}
}
}
/// # Elementwise mapping and folding
// Todo: maybe make ref versions of these methods that can be used when T is expensive to clone?
impl<T, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// Returns a matrix containing the result of `f` applied to each of its entries.
#[inline]
#[must_use]
pub fn map<T2, F: FnMut(T) -> T2>(&self, mut f: F) -> OMatrix<T2, R, C>
where
T: Clone,
DefaultAllocator: Allocator<T2, R, C>,
{
let (nrows, ncols) = self.data.shape();
let mut res = OMatrix::new_uninitialized_generic(nrows, ncols);
for j in 0..ncols.value() {
for i in 0..nrows.value() {
unsafe {
let a = self.data.get_unchecked(i, j).clone();
*res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a));
}
}
}
// Safety: all entries have been initialized.
unsafe { res.assume_init() }
}
/// Cast the components of `self` to another type.
///
/// # Example
/// ```
/// # use nalgebra::Vector3;
/// let q = Vector3::new(1.0f64, 2.0, 3.0);
/// let q2 = q.cast::<f32>();
/// assert_eq!(q2, Vector3::new(1.0f32, 2.0, 3.0));
/// ```
pub fn cast<T2>(self) -> OMatrix<T2, R, C>
where
OMatrix<T2, R, C>: SupersetOf<Self>,
DefaultAllocator: Allocator<T2, R, C>,
{
crate::convert(self)
}
/// 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]
#[must_use]
pub fn fold_with<T2>(
&self,
init_f: impl FnOnce(Option<&T>) -> T2,
f: impl FnMut(T2, &T) -> T2,
) -> T2 {
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]
#[must_use]
pub fn map_with_location<T2: Clone, F: FnMut(usize, usize, T) -> T2>(
&self,
mut f: F,
) -> OMatrix<T2, R, C>
where
T: Clone,
DefaultAllocator: Allocator<T2, R, C>,
{
let (nrows, ncols) = self.data.shape();
let mut res = OMatrix::new_uninitialized_generic(nrows, ncols);
for j in 0..ncols.value() {
for i in 0..nrows.value() {
unsafe {
let a = self.data.get_unchecked(i, j).clone();
*res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(i, j, a));
}
}
}
// Safety: all entries have been initialized.
unsafe { res.assume_init() }
}
/// Returns a matrix containing the result of `f` applied to each entries of `self` and
/// `rhs`.
#[inline]
#[must_use]
pub fn zip_map<T2: Clone, N3, S2, F>(
&self,
rhs: &Matrix<T2, R, C, S2>,
mut f: F,
) -> OMatrix<N3, R, C>
where
T: Clone,
S2: Storage<T2, R, C>,
F: FnMut(T, T2) -> N3,
DefaultAllocator: Allocator<N3, R, C>,
{
let (nrows, ncols) = self.data.shape();
let mut res = OMatrix::new_uninitialized_generic(nrows, ncols);
assert_eq!(
(nrows.value(), ncols.value()),
rhs.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
for j in 0..ncols.value() {
for i in 0..nrows.value() {
unsafe {
let a = self.data.get_unchecked(i, j).clone();
let b = rhs.data.get_unchecked(i, j).clone();
*res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a, b));
}
}
}
// Safety: all entries have been initialized.
unsafe { res.assume_init() }
}
/// Returns a matrix containing the result of `f` applied to each entries of `self` and
/// `b`, and `c`.
#[inline]
#[must_use]
pub fn zip_zip_map<T2: Clone, N3: Clone, N4, S2, S3, F>(
&self,
b: &Matrix<T2, R, C, S2>,
c: &Matrix<N3, R, C, S3>,
mut f: F,
) -> OMatrix<N4, R, C>
where
T: Clone,
S2: Storage<T2, R, C>,
S3: Storage<N3, R, C>,
F: FnMut(T, T2, N3) -> N4,
DefaultAllocator: Allocator<N4, R, C>,
{
let (nrows, ncols) = self.data.shape();
let mut res = OMatrix::new_uninitialized_generic(nrows, ncols);
assert_eq!(
(nrows.value(), ncols.value()),
b.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
assert_eq!(
(nrows.value(), ncols.value()),
c.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
for j in 0..ncols.value() {
for i in 0..nrows.value() {
unsafe {
let a = self.data.get_unchecked(i, j).clone();
let b = b.data.get_unchecked(i, j).clone();
let c = c.data.get_unchecked(i, j).clone();
*res.data.get_unchecked_mut(i, j) = MaybeUninit::new(f(a, b, c));
}
}
}
// Safety: all entries have been initialized.
unsafe { res.assume_init() }
}
/// Folds a function `f` on each entry of `self`.
#[inline]
#[must_use]
pub fn fold<Acc>(&self, mut init: Acc, mut f: impl FnMut(Acc, T) -> Acc) -> Acc
where
T: Clone,
{
let (nrows, ncols) = self.data.shape();
for j in 0..ncols.value() {
for i in 0..nrows.value() {
unsafe {
let a = self.data.get_unchecked(i, j).clone();
init = f(init, a)
}
}
}
init
}
/// Folds a function `f` on each pairs of entries from `self` and `rhs`.
#[inline]
#[must_use]
pub fn zip_fold<T2: Clone, R2: Dim, C2: Dim, S2, Acc>(
&self,
rhs: &Matrix<T2, R2, C2, S2>,
mut init: Acc,
mut f: impl FnMut(Acc, T, T2) -> Acc,
) -> Acc
where
T: Clone,
S2: Storage<T2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
let (nrows, ncols) = self.data.shape();
assert_eq!(
(nrows.value(), ncols.value()),
rhs.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
for j in 0..ncols.value() {
for i in 0..nrows.value() {
unsafe {
let a = self.data.get_unchecked(i, j).clone();
let b = rhs.data.get_unchecked(i, j).clone();
init = f(init, a, b)
}
}
}
init
}
/// Replaces each component of `self` by the result of a closure `f` applied on it.
#[inline]
pub fn apply<F: FnMut(T) -> T>(&mut self, mut f: F)
where
T: Clone, // This could be removed by changing the function signature.
S: StorageMut<T, R, C>,
{
let (nrows, ncols) = self.shape();
for j in 0..ncols {
for i in 0..nrows {
unsafe {
let e = self.data.get_unchecked_mut(i, j);
*e = f(e.clone())
}
}
}
}
/// 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<T2: Clone, R2: Dim, C2: Dim, S2>(
&mut self,
rhs: &Matrix<T2, R2, C2, S2>,
mut f: impl FnMut(T, T2) -> T,
) where
T: Clone, // This could be removed by changing the function signature.
S: StorageMut<T, R, C>,
S2: Storage<T2, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
let (nrows, ncols) = self.shape();
assert_eq!(
(nrows, ncols),
rhs.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
for j in 0..ncols {
for i in 0..nrows {
unsafe {
let e = self.data.get_unchecked_mut(i, j);
let rhs = rhs.get_unchecked((i, j)).clone();
*e = f(e.clone(), rhs)
}
}
}
}
/// 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<T2: Clone, R2: Dim, C2: Dim, S2, N3: Clone, R3: Dim, C3: Dim, S3>(
&mut self,
b: &Matrix<T2, R2, C2, S2>,
c: &Matrix<N3, R3, C3, S3>,
mut f: impl FnMut(T, T2, N3) -> T,
) where
T: Clone, // This could be removed by changing the function signature.
S: StorageMut<T, R, C>,
S2: Storage<T2, R2, C2>,
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_eq!(
(nrows, ncols),
b.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
assert_eq!(
(nrows, ncols),
c.shape(),
"Matrix simultaneous traversal error: dimension mismatch."
);
for j in 0..ncols {
for i in 0..nrows {
unsafe {
let e = self.data.get_unchecked_mut(i, j);
let b = b.get_unchecked((i, j)).clone();
let c = c.get_unchecked((i, j)).clone();
*e = f(e.clone(), b, c)
}
}
}
}
}
/// # Iteration on components, rows, and columns
impl<T, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// Iterates through this matrix coordinates in column-major order.
///
/// # Examples:
///
/// ```
/// # use nalgebra::Matrix2x3;
/// let mat = Matrix2x3::new(11, 12, 13,
/// 21, 22, 23);
/// let mut it = mat.iter();
/// assert_eq!(*it.next().unwrap(), 11);
/// assert_eq!(*it.next().unwrap(), 21);
/// assert_eq!(*it.next().unwrap(), 12);
/// assert_eq!(*it.next().unwrap(), 22);
/// assert_eq!(*it.next().unwrap(), 13);
/// assert_eq!(*it.next().unwrap(), 23);
/// assert!(it.next().is_none());
#[inline]
pub fn iter(&self) -> MatrixIter<T, R, C, S> {
MatrixIter::new(&self.data)
}
/// Iterate through the rows of this matrix.
///
/// # Example
/// ```
/// # use nalgebra::Matrix2x3;
/// let mut a = Matrix2x3::new(1, 2, 3,
/// 4, 5, 6);
/// for (i, row) in a.row_iter().enumerate() {
/// assert_eq!(row, a.row(i))
/// }
/// ```
#[inline]
pub fn row_iter(&self) -> RowIter<T, R, C, S> {
RowIter::new(self)
}
/// Iterate through the columns of this matrix.
/// # Example
/// ```
/// # use nalgebra::Matrix2x3;
/// let mut a = Matrix2x3::new(1, 2, 3,
/// 4, 5, 6);
/// for (i, column) in a.column_iter().enumerate() {
/// assert_eq!(column, a.column(i))
/// }
/// ```
#[inline]
pub fn column_iter(&self) -> ColumnIter<T, R, C, S> {
ColumnIter::new(self)
}
/// Mutably iterates through this matrix coordinates.
#[inline]
pub fn iter_mut(&mut self) -> MatrixIterMut<T, R, C, S>
where
S: StorageMut<T, R, C>,
{
MatrixIterMut::new(&mut self.data)
}
/// Mutably iterates through this matrix rows.
///
/// # Example
/// ```
/// # use nalgebra::Matrix2x3;
/// let mut a = Matrix2x3::new(1, 2, 3,
/// 4, 5, 6);
/// for (i, mut row) in a.row_iter_mut().enumerate() {
/// row *= (i + 1) * 10;
/// }
///
/// let expected = Matrix2x3::new(10, 20, 30,
/// 80, 100, 120);
/// assert_eq!(a, expected);
/// ```
#[inline]
pub fn row_iter_mut(&mut self) -> RowIterMut<T, R, C, S>
where
S: StorageMut<T, R, C>,
{
RowIterMut::new(self)
}
/// Mutably iterates through this matrix columns.
///
/// # Example
/// ```
/// # use nalgebra::Matrix2x3;
/// let mut a = Matrix2x3::new(1, 2, 3,
/// 4, 5, 6);
/// for (i, mut col) in a.column_iter_mut().enumerate() {
/// col *= (i + 1) * 10;
/// }
///
/// let expected = Matrix2x3::new(10, 40, 90,
/// 40, 100, 180);
/// assert_eq!(a, expected);
/// ```
#[inline]
pub fn column_iter_mut(&mut self) -> ColumnIterMut<T, R, C, S>
where
S: StorageMut<T, R, C>,
{
ColumnIterMut::new(self)
}
}
impl<T, R: Dim, C: Dim, S: StorageMut<T, R, C>> Matrix<T, R, C, S> {
/// Returns a mutable pointer to the start of the matrix.
///
/// If the matrix is not empty, this pointer is guaranteed to be aligned
/// and non-null.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.data.ptr_mut()
}
/// Swaps two entries without bound-checking.
#[inline]
pub unsafe fn swap_unchecked(&mut self, row_cols1: (usize, usize), row_cols2: (usize, usize)) {
debug_assert!(row_cols1.0 < self.nrows() && row_cols1.1 < self.ncols());
debug_assert!(row_cols2.0 < self.nrows() && row_cols2.1 < self.ncols());
self.data.swap_unchecked(row_cols1, row_cols2)
}
/// Swaps two entries.
#[inline]
pub fn swap(&mut self, row_cols1: (usize, usize), row_cols2: (usize, usize)) {
let (nrows, ncols) = self.shape();
assert!(
row_cols1.0 < nrows && row_cols1.1 < ncols,
"Matrix elements swap index out of bounds."
);
assert!(
row_cols2.0 < nrows && row_cols2.1 < ncols,
"Matrix elements swap index out of bounds."
);
unsafe { self.swap_unchecked(row_cols1, row_cols2) }
}
/// Fills this matrix with the content of a slice. Both must hold the same number of elements.
///
/// The components of the slice are assumed to be ordered in column-major order.
#[inline]
pub fn copy_from_slice(&mut self, slice: &[T])
where
T: Clone,
{
let (nrows, ncols) = self.shape();
assert!(
nrows * ncols == slice.len(),
"The slice must contain the same number of elements as the matrix."
);
for j in 0..ncols {
for i in 0..nrows {
unsafe {
*self.get_unchecked_mut((i, j)) = slice.get_unchecked(i + j * nrows).clone();
}
}
}
}
/// Fills this matrix with the content of another one via clones. Both must have the same shape.
#[inline]
pub fn copy_from<R2: Dim, C2: Dim, SB>(&mut self, other: &Matrix<T, R2, C2, SB>)
where
T: Clone,
SB: Storage<T, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
self.copy_from_fn(other, T::clone)
}
/// Fills this matrix with the content of another one, after applying a function to
/// the references of the entries of the other matrix. Both must have the same shape.
#[inline]
pub fn copy_from_fn<U, R2: Dim, C2: Dim, SB, F>(
&mut self,
other: &Matrix<U, R2, C2, SB>,
mut f: F,
) where
SB: Storage<U, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
F: FnMut(&U) -> T,
{
assert!(
self.shape() == other.shape(),
"Unable to copy from a matrix with a different shape."
);
for j in 0..self.ncols() {
for i in 0..self.nrows() {
unsafe {
*self.get_unchecked_mut((i, j)) = f(other.get_unchecked((i, j)));
}
}
}
}
/// Fills this matrix with the content of another one, after applying a function to
/// the entries of the other matrix. Both must have the same shape.
#[inline]
pub fn move_from<R2: Dim, C2: Dim>(&mut self, other: OMatrix<T, R2, C2>)
where
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
DefaultAllocator: Allocator<T, R2, C2>,
{
self.move_from_fn(other, |e| e)
}
/// Fills this matrix with the content of another one via moves. Both must have the same shape.
#[inline]
pub fn move_from_fn<U, R2: Dim, C2: Dim, F>(&mut self, other: OMatrix<U, R2, C2>, mut f: F)
where
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
DefaultAllocator: Allocator<U, R2, C2>,
F: FnMut(U) -> T,
{
assert!(
self.shape() == other.shape(),
"Unable to move from a matrix with a different shape."
);
let mut md = other.manually_drop();
for j in 0..self.ncols() {
for i in 0..self.nrows() {
unsafe {
*self.get_unchecked_mut((i, j)) =
f(ManuallyDrop::take(md.get_unchecked_mut((i, j))));
}
}
}
}
/// Fills this matrix with the content of the transpose another one via clones.
#[inline]
pub fn tr_copy_from<R2: Dim, C2: Dim, SB>(&mut self, other: &Matrix<T, R2, C2, SB>)
where
T: Clone,
SB: Storage<T, R2, C2>,
ShapeConstraint: DimEq<R, C2> + SameNumberOfColumns<C, R2>,
{
self.tr_copy_from_fn(other, T::clone)
}
/// Fills this matrix with the content of the transpose of another one, after applying
/// a function to the references of the entries of the other matrix. Both must have the
/// same shape.
#[inline]
pub fn tr_copy_from_fn<U, R2: Dim, C2: Dim, SB, F>(
&mut self,
other: &Matrix<U, R2, C2, SB>,
mut f: F,
) where
SB: Storage<U, R2, C2>,
ShapeConstraint: DimEq<R, C2> + SameNumberOfColumns<C, R2>,
F: FnMut(&U) -> T,
{
let (nrows, ncols) = self.shape();
assert!(
(ncols, nrows) == other.shape(),
"Unable to copy from a matrix with incompatible shape."
);
for j in 0..ncols {
for i in 0..nrows {
unsafe {
*self.get_unchecked_mut((i, j)) = f(other.get_unchecked((j, i)));
}
}
}
}
/// Fills this matrix with the content of the transpose another one via moves.
#[inline]
pub fn tr_move_from<R2: Dim, C2: Dim>(&mut self, other: OMatrix<T, R2, C2>)
where
DefaultAllocator: Allocator<T, R2, C2>,
ShapeConstraint: DimEq<R, C2> + SameNumberOfColumns<C, R2>,
{
self.tr_move_from_fn(other, |e| e)
}
/// Fills this matrix with the content of the transpose of another one, after applying
/// a function to the entries of the other matrix. Both must have the same shape.
#[inline]
pub fn tr_move_from_fn<U, R2: Dim, C2: Dim, F>(&mut self, other: OMatrix<U, R2, C2>, mut f: F)
where
ShapeConstraint: DimEq<R, C2> + SameNumberOfColumns<C, R2>,
DefaultAllocator: Allocator<U, R2, C2>,
F: FnMut(U) -> T,
{
let (nrows, ncols) = self.shape();
assert!(
(ncols, nrows) == other.shape(),
"Unable to move from a matrix with incompatible shape."
);
let mut md = other.manually_drop();
for j in 0..ncols {
for i in 0..nrows {
unsafe {
*self.get_unchecked_mut((i, j)) =
f(ManuallyDrop::take(md.get_unchecked_mut((j, i))));
}
}
}
}
// TODO: 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(T) -> T>(mut self, f: F) -> Self
where
T: Clone,
{
self.apply(f);
self
}
}
impl<T, R: Dim, C: Dim, S: StorageMut<MaybeUninit<T>, R, C>> Matrix<MaybeUninit<T>, R, C, S> {
/// Initializes this matrix with the content of another one via clones. Both must have the same shape.
#[inline]
pub fn copy_init_from<R2: Dim, C2: Dim, SB>(&mut self, other: &Matrix<T, R2, C2, SB>)
where
T: Clone,
SB: Storage<T, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
self.copy_from_fn(other, |e| MaybeUninit::new(e.clone()))
}
/// Initializes this matrix with the content of another one, after applying a function to
/// the entries of the other matrix. Both must have the same shape.
#[inline]
pub fn move_init_from<R2: Dim, C2: Dim, SB>(&mut self, other: OMatrix<T, R2, C2>)
where
SB: Storage<T, R2, C2>,
DefaultAllocator: Allocator<T, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
self.move_from_fn(other, MaybeUninit::new)
}
/// Initializes this matrix with the content of the transpose another one via clones.
#[inline]
pub fn tr_copy_init_from<R2: Dim, C2: Dim, SB>(&mut self, other: &Matrix<T, R2, C2, SB>)
where
T: Clone,
SB: Storage<T, R2, C2>,
ShapeConstraint: DimEq<R, C2> + SameNumberOfColumns<C, R2>,
{
self.tr_copy_from_fn(other, |e| MaybeUninit::new(e.clone()))
}
/// Initializes this matrix with the content of the transpose another one via moves.
#[inline]
pub fn tr_move_init_from<R2: Dim, C2: Dim>(&mut self, other: OMatrix<T, R2, C2>)
where
DefaultAllocator: Allocator<T, R2, C2>,
ShapeConstraint: DimEq<R, C2> + SameNumberOfColumns<C, R2>,
{
self.tr_move_from_fn(other, MaybeUninit::new)
}
}
impl<T, D: Dim, S: Storage<T, D>> Vector<T, D, S> {
/// Gets a reference to the i-th element of this column vector without bound checking.
#[inline]
#[must_use]
pub unsafe fn vget_unchecked(&self, i: usize) -> &T {
debug_assert!(i < self.nrows(), "Vector index out of bounds.");
let i = i * self.strides().0;
self.data.get_unchecked_linear(i)
}
}
impl<T, D: Dim, S: StorageMut<T, D>> Vector<T, D, S> {
/// Gets a mutable reference to the i-th element of this column vector without bound checking.
#[inline]
#[must_use]
pub unsafe fn vget_unchecked_mut(&mut self, i: usize) -> &mut T {
debug_assert!(i < self.nrows(), "Vector index out of bounds.");
let i = i * self.strides().0;
self.data.get_unchecked_linear_mut(i)
}
}
impl<T, R: Dim, C: Dim, S: ContiguousStorage<T, R, C>> Matrix<T, R, C, S> {
/// Extracts a slice containing the entire matrix entries ordered column-by-columns.
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[T] {
self.data.as_slice()
}
}
impl<T, R: Dim, C: Dim, S: ContiguousStorageMut<T, R, C>> Matrix<T, R, C, S> {
/// Extracts a mutable slice containing the entire matrix entries ordered column-by-columns.
#[inline]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.data.as_mut_slice()
}
}
impl<T, D: Dim, S: StorageMut<T, D, D>> Matrix<T, D, D, S> {
/// Transposes the square matrix `self` in-place.
pub fn transpose_mut(&mut self) {
assert!(
self.is_square(),
"Unable to transpose a non-square matrix in-place."
);
let dim = self.shape().0;
for i in 1..dim {
for j in 0..i {
unsafe { self.swap_unchecked((i, j), (j, i)) }
}
}
}
}
impl<T: SimdComplexField, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// Takes the adjoint (aka. conjugate-transpose) of `self` and store the result into `out`.
#[inline]
pub fn adjoint_to<R2: Dim, C2: Dim, SB>(&self, out: &mut Matrix<MaybeUninit<T>, R2, C2, SB>)
where
SB: StorageMut<MaybeUninit<T>, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, C2> + SameNumberOfColumns<C, R2>,
{
let (nrows, ncols) = self.shape();
assert!(
(ncols, nrows) == out.shape(),
"Incompatible shape for transpose-copy."
);
// TODO: optimize that.
for i in 0..nrows {
for j in 0..ncols {
unsafe {
*out.get_unchecked_mut((j, i)) =
MaybeUninit::new(self.get_unchecked((i, j)).simd_conjugate());
}
}
}
}
/// The adjoint (aka. conjugate-transpose) of `self`.
#[inline]
#[must_use = "Did you mean to use adjoint_mut()?"]
pub fn adjoint(&self) -> OMatrix<T, C, R>
where
DefaultAllocator: Allocator<T, C, R>,
{
let (nrows, ncols) = self.data.shape();
let mut res = OMatrix::new_uninitialized_generic(ncols, nrows);
self.adjoint_to(&mut res);
unsafe { res.assume_init() }
}
/// 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: Dim, C2: Dim, SB>(
&self,
out: &mut Matrix<MaybeUninit<T>, R2, C2, SB>,
) where
SB: StorageMut<MaybeUninit<T>, 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) -> OMatrix<T, C, R>
where
DefaultAllocator: Allocator<T, C, R>,
{
self.adjoint()
}
/// The conjugate of `self`.
#[inline]
#[must_use = "Did you mean to use conjugate_mut()?"]
pub fn conjugate(&self) -> OMatrix<T, R, C>
where
DefaultAllocator: Allocator<T, 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: T::SimdRealField) -> OMatrix<T, R, C>
where
DefaultAllocator: Allocator<T, 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: T::SimdRealField) -> OMatrix<T, R, C>
where
DefaultAllocator: Allocator<T, R, C>,
{
self.map(|e| e.simd_scale(real))
}
}
impl<T: SimdComplexField, R: Dim, C: Dim, S: StorageMut<T, R, C>> Matrix<T, R, C, S> {
/// The conjugate of the complex matrix `self` computed in-place.
#[inline]
pub fn conjugate_mut(&mut self) {
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: T::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: T::SimdRealField) {
self.apply(|e| e.simd_scale(real))
}
}
impl<T: SimdComplexField, D: Dim, S: StorageMut<T, D, D>> Matrix<T, D, D, S> {
/// Sets `self` to its adjoint.
#[deprecated(note = "Renamed to `self.adjoint_mut()`.")]
pub fn conjugate_transform_mut(&mut self) {
self.adjoint_mut()
}
/// Sets `self` to its adjoint (aka. conjugate-transpose).
pub fn adjoint_mut(&mut self) {
assert!(
self.is_square(),
"Unable to transpose a non-square matrix in-place."
);
let dim = self.shape().0;
for i in 0..dim {
for j in 0..i {
unsafe {
let ref_ij = self.get_unchecked_mut((i, j)) as *mut T;
let ref_ji = self.get_unchecked_mut((j, i)) as *mut T;
let conj_ij = (*ref_ij).simd_conjugate();
let conj_ji = (*ref_ji).simd_conjugate();
*ref_ij = conj_ji;
*ref_ji = conj_ij;
}
}
{
let diag = unsafe { self.get_unchecked_mut((i, i)) };
*diag = diag.simd_conjugate();
}
}
}
}
impl<T, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
/// The diagonal of this matrix.
#[inline]
#[must_use]
pub fn diagonal(&self) -> OVector<T, D>
where
T: Clone,
DefaultAllocator: Allocator<T, D>,
{
self.map_diagonal(|e| e)
}
/// Apply the given function to this matrix's diagonal and returns it.
///
/// This is a more efficient version of `self.diagonal().map(f)` since this
/// allocates only once.
#[must_use]
pub fn map_diagonal<T2: Clone>(&self, mut f: impl FnMut(T) -> T2) -> OVector<T2, D>
where
T: Clone,
DefaultAllocator: Allocator<T2, D>,
{
assert!(
self.is_square(),
"Unable to get the diagonal of a non-square matrix."
);
let dim = self.data.shape().0;
let mut res = OVector::new_uninitialized_generic(dim, Const::<1>);
for i in 0..dim.value() {
unsafe {
*res.vget_unchecked_mut(i) =
MaybeUninit::new(f(self.get_unchecked((i, i)).clone()));
}
}
// Safety: we have initialized all entries.
unsafe { res.assume_init() }
}
/// Computes a trace of a square matrix, i.e., the sum of its diagonal elements.
#[inline]
#[must_use]
pub fn trace(&self) -> T
where
T: Scalar + Zero + ClosedAdd,
{
assert!(
self.is_square(),
"Cannot compute the trace of non-square matrix."
);
let dim = self.data.shape().0;
let mut res = T::zero();
for i in 0..dim.value() {
res += unsafe { self.get_unchecked((i, i)).inlined_clone() };
}
res
}
}
impl<T: SimdComplexField, D: Dim, S: Storage<T, D, D>> SquareMatrix<T, D, S> {
/// The symmetric part of `self`, i.e., `0.5 * (self + self.transpose())`.
#[inline]
#[must_use]
pub fn symmetric_part(&self) -> OMatrix<T, D, D>
where
DefaultAllocator: Allocator<T, 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::<_, T>(0.5);
tr
}
/// The hermitian part of `self`, i.e., `0.5 * (self + self.adjoint())`.
#[inline]
#[must_use]
pub fn hermitian_part(&self) -> OMatrix<T, D, D>
where
DefaultAllocator: Allocator<T, D, D>,
{
assert!(
self.is_square(),
"Cannot compute the hermitian part of a non-square matrix."
);
let mut tr = self.adjoint();
tr += self;
tr *= crate::convert::<_, T>(0.5);
tr
}
}
impl<T: Scalar + Zero + One, D: DimAdd<U1> + IsNotStaticOne, S: Storage<T, D, D>>
Matrix<T, 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]
#[must_use]
pub fn to_homogeneous(&self) -> OMatrix<T, DimSum<D, U1>, DimSum<D, U1>>
where
DefaultAllocator: Allocator<T, DimSum<D, U1>, DimSum<D, U1>>,
{
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 = OMatrix::identity_generic(dim, dim);
res.generic_slice_mut::<D, D>((0, 0), self.data.shape())
.copy_from(self);
res
}
}
impl<T: Scalar + Zero, D: DimAdd<U1>, S: Storage<T, D>> Vector<T, D, S> {
/// Computes the coordinates in projective space of this vector, i.e., appends a `0` to its
/// coordinates.
#[inline]
#[must_use]
pub fn to_homogeneous(&self) -> OVector<T, DimSum<D, U1>>
where
DefaultAllocator: Allocator<T, DimSum<D, U1>>,
{
self.push(T::zero())
}
/// Constructs a vector from coordinates in projective space, i.e., removes a `0` at the end of
/// `self`. Returns `None` if this last component is not zero.
#[inline]
pub fn from_homogeneous<SB>(v: Vector<T, DimSum<D, U1>, SB>) -> Option<OVector<T, D>>
where
SB: Storage<T, DimSum<D, U1>>,
DefaultAllocator: Allocator<T, D>,
{
if v[v.len() - 1].is_zero() {
let nrows = D::from_usize(v.len() - 1);
Some(v.generic_slice((0, 0), (nrows, Const::<1>)).into_owned())
} else {
None
}
}
}
impl<T: Clone + Zero, D: DimAdd<U1>, S: Storage<T, D>> Vector<T, D, S> {
/// Constructs a new vector of higher dimension by appending `element` to the end of `self`.
#[inline]
#[must_use]
pub fn push(&self, element: T) -> OVector<T, DimSum<D, U1>>
where
DefaultAllocator: Allocator<T, DimSum<D, U1>>,
{
let len = self.len();
let hnrows = DimSum::<D, U1>::from_usize(len + 1);
let mut res = OVector::new_uninitialized_generic(hnrows, Const::<1>);
res.generic_slice_mut((0, 0), self.data.shape())
.copy_from_fn(self, |e| MaybeUninit::new(e.clone()));
res[(len, 0)] = MaybeUninit::new(element);
unsafe { res.assume_init() }
}
}
impl<T, R: Dim, C: Dim, S> AbsDiffEq for Matrix<T, R, C, S>
where
T: AbsDiffEq,
S: Storage<T, R, C>,
T::Epsilon: Copy,
{
type Epsilon = T::Epsilon;
#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.iter()
.zip(other.iter())
.all(|(a, b)| a.abs_diff_eq(b, epsilon))
}
}
impl<T, R: Dim, C: Dim, S> RelativeEq for Matrix<T, R, C, S>
where
T: RelativeEq,
S: Storage<T, R, C>,
T::Epsilon: Copy,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.relative_eq(other, epsilon, max_relative)
}
}
impl<T, R: Dim, C: Dim, S> UlpsEq for Matrix<T, R, C, S>
where
T: UlpsEq,
S: Storage<T, R, C>,
T::Epsilon: Copy,
{
#[inline]
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
#[inline]
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
assert!(self.shape() == other.shape());
self.iter()
.zip(other.iter())
.all(|(a, b)| a.ulps_eq(b, epsilon, max_ulps))
}
}
impl<T: PartialOrd, R: Dim, C: Dim, S> PartialOrd for Matrix<T, R, C, S>
where
S: Storage<T, R, C>,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.shape() != other.shape() {
return None;
}
if self.nrows() == 0 || self.ncols() == 0 {
return Some(Ordering::Equal);
}
let mut first_ord = unsafe {
self.data
.get_unchecked_linear(0)
.partial_cmp(other.data.get_unchecked_linear(0))
};
if let Some(first_ord) = first_ord.as_mut() {
let mut it = self.iter().zip(other.iter());
let _ = it.next(); // Drop the first elements (we already tested it).
for (left, right) in it {
if let Some(ord) = left.partial_cmp(right) {
match ord {
Ordering::Equal => { /* Does not change anything. */ }
Ordering::Less => {
if *first_ord == Ordering::Greater {
return None;
}
*first_ord = ord
}
Ordering::Greater => {
if *first_ord == Ordering::Less {
return None;
}
*first_ord = ord
}
}
} else {
return None;
}
}
}
first_ord
}
#[inline]
fn lt(&self, right: &Self) -> bool {
assert_eq!(
self.shape(),
right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.lt(b))
}
#[inline]
fn le(&self, right: &Self) -> bool {
assert_eq!(
self.shape(),
right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.le(b))
}
#[inline]
fn gt(&self, right: &Self) -> bool {
assert_eq!(
self.shape(),
right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.gt(b))
}
#[inline]
fn ge(&self, right: &Self) -> bool {
assert_eq!(
self.shape(),
right.shape(),
"Matrix comparison error: dimensions mismatch."
);
self.iter().zip(right.iter()).all(|(a, b)| a.ge(b))
}
}
impl<T: Eq, R: Dim, C: Dim, S> Eq for Matrix<T, R, C, S> where S: Storage<T, R, C> {}
impl<T: PartialEq, R: Dim, R2: Dim, C: Dim, C2: Dim, S, S2> PartialEq<Matrix<T, R2, C2, S2>>
for Matrix<T, R, C, S>
where
S: Storage<T, R, C>,
S2: Storage<T, R2, C2>,
{
#[inline]
fn eq(&self, right: &Matrix<T, R2, C2, S2>) -> bool {
self.shape() == right.shape() && self.iter().zip(right.iter()).all(|(l, r)| l == r)
}
}
macro_rules! impl_fmt {
($trait: path, $fmt_str_without_precision: expr, $fmt_str_with_precision: expr) => {
impl<T, R: Dim, C: Dim, S> $trait for Matrix<T, R, C, S>
where
T: Scalar + $trait,
S: Storage<T, R, C>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[cfg(feature = "std")]
fn val_width<T: Scalar + $trait>(val: &T, f: &mut fmt::Formatter) -> usize {
match f.precision() {
Some(precision) => format!($fmt_str_with_precision, val, precision)
.chars()
.count(),
None => format!($fmt_str_without_precision, val).chars().count(),
}
}
#[cfg(not(feature = "std"))]
fn val_width<T: Scalar + $trait>(_: &T, _: &mut fmt::Formatter) -> usize {
4
}
let (nrows, ncols) = self.shape();
if nrows == 0 || ncols == 0 {
return write!(f, "[ ]");
}
let mut max_length = 0;
for i in 0..nrows {
for j in 0..ncols {
max_length = crate::max(max_length, val_width(&self[(i, j)], f));
}
}
let max_length_with_space = max_length + 1;
writeln!(f)?;
writeln!(
f,
" ┌ {:>width$} ┐",
"",
width = max_length_with_space * ncols - 1
)?;
for i in 0..nrows {
write!(f, "")?;
for j in 0..ncols {
let number_length = val_width(&self[(i, j)], f) + 1;
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)?
}
None => write!(f, $fmt_str_without_precision, (*self)[(i, j)])?,
}
}
writeln!(f, "")?;
}
writeln!(
f,
" └ {:>width$} ┘",
"",
width = max_length_with_space * ncols - 1
)?;
writeln!(f)
}
}
};
}
impl_fmt!(fmt::Display, "{}", "{:.1$}");
impl_fmt!(fmt::LowerExp, "{:e}", "{:.1$e}");
impl_fmt!(fmt::UpperExp, "{:E}", "{:.1$E}");
impl_fmt!(fmt::Octal, "{:o}", "{:1$o}");
impl_fmt!(fmt::LowerHex, "{:x}", "{:1$x}");
impl_fmt!(fmt::UpperHex, "{:X}", "{:1$X}");
impl_fmt!(fmt::Binary, "{:b}", "{:.1$b}");
impl_fmt!(fmt::Pointer, "{:p}", "{:.1$p}");
#[cfg(test)]
mod tests {
#[test]
fn empty_display() {
let vec: Vec<f64> = Vec::new();
let dvector = crate::DVector::from_vec(vec);
assert_eq!(format!("{}", dvector), "[ ]")
}
#[test]
fn lower_exp() {
let test = crate::Matrix2::new(1e6, 2e5, 2e-5, 1.);
assert_eq!(
format!("{:e}", test),
r"
┌ ┐
│ 1e6 2e5 │
│ 2e-5 1e0 │
└ ┘
"
)
}
}
/// # Cross product
impl<T: Scalar + ClosedAdd + ClosedSub + ClosedMul, R: Dim, C: Dim, S: Storage<T, R, C>>
Matrix<T, R, C, S>
{
/// The perpendicular product between two 2D column vectors, i.e. `a.x * b.y - a.y * b.x`.
#[inline]
#[must_use]
pub fn perp<R2, C2, SB>(&self, b: &Matrix<T, R2, C2, SB>) -> T
where
R2: Dim,
C2: Dim,
SB: Storage<T, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, U2>
+ SameNumberOfColumns<C, U1>
+ SameNumberOfRows<R2, U2>
+ SameNumberOfColumns<C2, U1>,
{
assert!(
self.shape() == (2, 1),
"2D perpendicular product requires (2, 1) vector but found {:?}",
self.shape()
);
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()
}
}
// TODO: use specialization instead of an assertion.
/// The 3D cross product between two vectors.
///
/// Panics if the shape is not 3D vector. In the future, this will be implemented only for
/// dynamically-sized matrices and statically-sized 3D matrices.
#[inline]
#[must_use]
pub fn cross<R2: Dim, C2: Dim, SB>(
&self,
b: &Matrix<T, R2, C2, SB>,
) -> MatrixCross<T, R, C, R2, C2>
where
SB: Storage<T, R2, C2>,
DefaultAllocator: SameShapeAllocator<T, R, C, R2, C2>,
ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
{
let shape = self.shape();
assert_eq!(shape, b.shape(), "Vector cross product dimension mismatch.");
assert!(
(shape.0 == 3 && shape.1 == 1) || (shape.0 == 1 && shape.1 == 3),
"Vector cross product dimension mismatch: must be (3, 1) or (1, 3) but found {:?}.",
shape
);
if shape.0 == 3 {
unsafe {
// TODO: soooo ugly!
let nrows = SameShapeR::<R, R2>::from_usize(3);
let ncols = SameShapeC::<C, C2>::from_usize(1);
let mut res = Matrix::new_uninitialized_generic(nrows, ncols);
let ax = self.get_unchecked((0, 0));
let ay = self.get_unchecked((1, 0));
let az = self.get_unchecked((2, 0));
let bx = b.get_unchecked((0, 0));
let by = b.get_unchecked((1, 0));
let bz = b.get_unchecked((2, 0));
*res.get_unchecked_mut((0, 0)) = MaybeUninit::new(
ay.inlined_clone() * bz.inlined_clone()
- az.inlined_clone() * by.inlined_clone(),
);
*res.get_unchecked_mut((1, 0)) = MaybeUninit::new(
az.inlined_clone() * bx.inlined_clone()
- ax.inlined_clone() * bz.inlined_clone(),
);
*res.get_unchecked_mut((2, 0)) = MaybeUninit::new(
ax.inlined_clone() * by.inlined_clone()
- ay.inlined_clone() * bx.inlined_clone(),
);
res.assume_init()
}
} else {
unsafe {
// TODO: ugly!
let nrows = SameShapeR::<R, R2>::from_usize(1);
let ncols = SameShapeC::<C, C2>::from_usize(3);
let mut res = Matrix::new_uninitialized_generic(nrows, ncols);
let ax = self.get_unchecked((0, 0));
let ay = self.get_unchecked((0, 1));
let az = self.get_unchecked((0, 2));
let bx = b.get_unchecked((0, 0));
let by = b.get_unchecked((0, 1));
let bz = b.get_unchecked((0, 2));
*res.get_unchecked_mut((0, 0)) = MaybeUninit::new(
ay.inlined_clone() * bz.inlined_clone()
- az.inlined_clone() * by.inlined_clone(),
);
*res.get_unchecked_mut((0, 1)) = MaybeUninit::new(
az.inlined_clone() * bx.inlined_clone()
- ax.inlined_clone() * bz.inlined_clone(),
);
*res.get_unchecked_mut((0, 2)) = MaybeUninit::new(
ax.inlined_clone() * by.inlined_clone()
- ay.inlined_clone() * bx.inlined_clone(),
);
res.assume_init()
}
}
}
}
impl<T: Scalar + Field, S: Storage<T, U3>> Vector<T, U3, S> {
/// Computes the matrix `M` such that for all vector `v` we have `M * v == self.cross(&v)`.
#[inline]
#[must_use]
pub fn cross_matrix(&self) -> OMatrix<T, U3, U3> {
OMatrix::<T, U3, U3>::new(
T::zero(),
-self[2].inlined_clone(),
self[1].inlined_clone(),
self[2].inlined_clone(),
T::zero(),
-self[0].inlined_clone(),
-self[1].inlined_clone(),
self[0].inlined_clone(),
T::zero(),
)
}
}
impl<T: SimdComplexField, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
/// The smallest angle between two vectors.
#[inline]
#[must_use]
pub fn angle<R2: Dim, C2: Dim, SB>(&self, other: &Matrix<T, R2, C2, SB>) -> T::SimdRealField
where
SB: Storage<T, R2, C2>,
ShapeConstraint: DimEq<R, R2> + DimEq<C, C2>,
{
let prod = self.dotc(other);
let n1 = self.norm();
let n2 = other.norm();
if n1.is_zero() || n2.is_zero() {
T::SimdRealField::zero()
} else {
let cang = prod.simd_real() / (n1 * n2);
cang.simd_clamp(-T::SimdRealField::one(), T::SimdRealField::one())
.simd_acos()
}
}
}
impl<T: AbsDiffEq, R: Dim, C: Dim, S> AbsDiffEq for Unit<Matrix<T, R, C, S>>
where
S: Storage<T, R, C>,
T::Epsilon: Copy,
{
type Epsilon = T::Epsilon;
#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.as_ref().abs_diff_eq(other.as_ref(), epsilon)
}
}
impl<T: RelativeEq, R: Dim, C: Dim, S> RelativeEq for Unit<Matrix<T, R, C, S>>
where
S: Storage<T, R, C>,
T::Epsilon: Copy,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.as_ref()
.relative_eq(other.as_ref(), epsilon, max_relative)
}
}
impl<T: UlpsEq, R: Dim, C: Dim, S> UlpsEq for Unit<Matrix<T, R, C, S>>
where
S: Storage<T, R, C>,
T::Epsilon: Copy,
{
#[inline]
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
#[inline]
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
self.as_ref().ulps_eq(other.as_ref(), epsilon, max_ulps)
}
}
impl<T: Hash, R: Dim, C: Dim, S> Hash for Matrix<T, R, C, S>
where
S: Storage<T, R, C>,
{
fn hash<H: Hasher>(&self, state: &mut H) {
let (nrows, ncols) = self.shape();
(nrows, ncols).hash(state);
for j in 0..ncols {
for i in 0..nrows {
unsafe {
self.get_unchecked((i, j)).hash(state);
}
}
}
}
}