Add methods for in-place reshaping of matrices

There are two major additions in this commit. The first is a new storage
trait, `ReshapableStorage`, that can be implemented for storage types
that can be reshaped in-place. I have implemented this for both the
`ArrayStorage` and `VecStorage` types, as they are the most common and
they are just interpretations of a flat list.

The second is a `Matrix::reshape_generic` method that allows matrices to
be in-place reshaped provided that the underlying storage can handle it.
In practice, this means that the standard matrix types (`MatrixMN` and
`DMatrix`) can be resized to any size that has the same element count.
Resizing between array and vector storage is not implemented due to
`Storage` only being implemented for `VecStorage` variants where at
least one dimension is `Dynamic`.

Additionally, only the generic reshape function is added as it can be a
basis for other reshaping functions (see the resizing functions) and I
am not particularly in the mood to implement a variety of reshaping
methods.
This commit is contained in:
Nathan Kent 2020-08-19 01:52:26 -04:00 committed by Crozet Sébastien
parent f0e29ba39f
commit 4a6022d9bf
6 changed files with 176 additions and 7 deletions

View File

@ -1,6 +1,6 @@
extern crate nalgebra as na;
use na::{DMatrix, Matrix2x3, RowVector3, Vector2};
use na::{DMatrix, Matrix2x3, Matrix3x2, RowVector3, Vector2};
fn main() {
// All the following matrices are equal but constructed in different ways.

33
examples/reshaping.rs Normal file
View File

@ -0,0 +1,33 @@
extern crate nalgebra as na;
use na::{DMatrix, Dynamic, Matrix2x3, Matrix3x2, U2, U3};
fn main() {
// Matrices can be reshaped in-place without moving or copying values.
let m1 = Matrix2x3::new(1.1, 1.2, 1.3, 2.1, 2.2, 2.3);
let m2 = Matrix3x2::new(1.1, 2.2, 2.1, 1.3, 1.2, 2.3);
let m3 = m1.reshape_generic(U3, U2);
assert_eq!(m3, m2);
// Note that, for statically sized matrices, invalid reshapes will not compile:
//let m4 = m3.reshape_generic(U3, U3);
// If dynamically sized matrices are used, the reshaping is checked at run-time.
let dm1 = DMatrix::from_vec(
4,
3,
vec![1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
);
let dm2 = DMatrix::from_vec(
6,
2,
vec![1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
);
let dm3 = dm1.reshape_generic(Dynamic::new(6), Dynamic::new(2));
assert_eq!(dm3, dm2);
// Invalid reshapings of dynamic matrices will panic at run-time.
//let dm4 = dm3.reshape_generic(Dynamic::new(6), Dynamic::new(6));
}

View File

@ -24,7 +24,9 @@ use typenum::Prod;
use crate::base::allocator::Allocator;
use crate::base::default_allocator::DefaultAllocator;
use crate::base::dimension::{DimName, U1};
use crate::base::storage::{ContiguousStorage, ContiguousStorageMut, Owned, Storage, StorageMut};
use crate::base::storage::{
ContiguousStorage, ContiguousStorageMut, Owned, ReshapableStorage, Storage, StorageMut,
};
use crate::base::Scalar;
/*
@ -267,6 +269,25 @@ where
{
}
impl<N, R1, C1, R2, C2> ReshapableStorage<N, R1, C1, R2, C2> for ArrayStorage<N, R1, C1>
where
N: Scalar,
R1: DimName,
C1: DimName,
R1::Value: Mul<C1::Value>,
Prod<R1::Value, C1::Value>: ArrayLength<N>,
R2: DimName,
C2: DimName,
R2::Value: Mul<C2::Value, Output = Prod<R1::Value, C1::Value>>,
Prod<R2::Value, C2::Value>: ArrayLength<N>,
{
type Output = ArrayStorage<N, R2, C2>;
fn reshape_generic(self, _: R2, _: C2) -> Self::Output {
ArrayStorage { data: self.data }
}
}
/*
*
* Allocation-less serde impls.

View File

@ -13,7 +13,7 @@ use crate::base::dimension::Dynamic;
use crate::base::dimension::{
Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimName, DimSub, DimSum, U1,
};
use crate::base::storage::{Storage, StorageMut};
use crate::base::storage::{ReshapableStorage, Storage, StorageMut};
#[cfg(any(feature = "std", feature = "alloc"))]
use crate::base::DMatrix;
use crate::base::{DefaultAllocator, Matrix, MatrixMN, RowVector, Scalar, Vector};
@ -745,7 +745,7 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
self.resize_generic(R2::name(), C2::name(), val)
}
/// Resizes `self` such that it has dimensions `new_nrows × now_ncols`.
/// Resizes `self` such that it has dimensions `new_nrows × new_ncols`.
///
/// The values are copied such that `self[(i, j)] == result[(i, j)]`. If the result has more
/// rows and/or columns than `self`, then the extra rows or columns are filled with `val`.
@ -813,6 +813,31 @@ impl<N: Scalar, R: Dim, C: Dim, S: Storage<N, R, C>> Matrix<N, R, C, S> {
}
}
impl<N, R, C, S> Matrix<N, R, C, S>
where
N: Scalar,
R: Dim,
C: Dim,
{
/// Reshapes `self` in-place such that it has dimensions `new_nrows × new_ncols`.
///
/// The values are not copied or moved. This function will panic if dynamic sizes are provided
/// and not compatible.
pub fn reshape_generic<R2, C2>(
self,
new_nrows: R2,
new_ncols: C2,
) -> Matrix<N, R2, C2, S::Output>
where
R2: Dim,
C2: Dim,
S: ReshapableStorage<N, R, C, R2, C2>,
{
let data = self.data.reshape_generic(new_nrows, new_ncols);
Matrix::from_data(data)
}
}
#[cfg(any(feature = "std", feature = "alloc"))]
impl<N: Scalar> DMatrix<N> {
/// Resizes this matrix in-place.

View File

@ -171,7 +171,7 @@ pub unsafe trait StorageMut<N: Scalar, R: Dim, C: Dim = U1>: Storage<N, R, C> {
/// A matrix storage that is stored contiguously in memory.
///
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols[`, the value
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols]`, the value
/// `.get_unchecked_linear` returns one of the matrix component. This trait is unsafe because
/// failing to comply to this may cause Undefined Behaviors.
pub unsafe trait ContiguousStorage<N: Scalar, R: Dim, C: Dim = U1>:
@ -181,10 +181,26 @@ pub unsafe trait ContiguousStorage<N: Scalar, R: Dim, C: Dim = U1>:
/// A mutable matrix storage that is stored contiguously in memory.
///
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols[`, the value
/// The storage requirement means that for any value of `i` in `[0, nrows * ncols]`, the value
/// `.get_unchecked_linear` returns one of the matrix component. This trait is unsafe because
/// failing to comply to this may cause Undefined Behaviors.
pub unsafe trait ContiguousStorageMut<N: Scalar, R: Dim, C: Dim = U1>:
ContiguousStorage<N, R, C> + StorageMut<N, R, C>
{
}
/// A matrix storage that can be reshaped in-place.
pub trait ReshapableStorage<N, R1, C1, R2, C2>: Storage<N, R1, C1>
where
N: Scalar,
R1: Dim,
C1: Dim,
R2: Dim,
C2: Dim,
{
/// The reshaped storage type.
type Output: Storage<N, R2, C2>;
/// Reshapes the storage into the output storage type.
fn reshape_generic(self, nrows: R2, ncols: C2) -> Self::Output;
}

View File

@ -8,7 +8,9 @@ use crate::base::allocator::Allocator;
use crate::base::constraint::{SameNumberOfRows, ShapeConstraint};
use crate::base::default_allocator::DefaultAllocator;
use crate::base::dimension::{Dim, DimName, Dynamic, U1};
use crate::base::storage::{ContiguousStorage, ContiguousStorageMut, Owned, Storage, StorageMut};
use crate::base::storage::{
ContiguousStorage, ContiguousStorageMut, Owned, ReshapableStorage, Storage, StorageMut,
};
use crate::base::{Scalar, Vector};
#[cfg(feature = "abomonation-serialize")]
@ -225,6 +227,42 @@ unsafe impl<N: Scalar, C: Dim> ContiguousStorageMut<N, Dynamic, C> for VecStorag
{
}
impl<N, C1, C2> ReshapableStorage<N, Dynamic, C1, Dynamic, C2> for VecStorage<N, Dynamic, C1>
where
N: Scalar,
C1: Dim,
C2: Dim,
{
type Output = VecStorage<N, Dynamic, C2>;
fn reshape_generic(self, nrows: Dynamic, ncols: C2) -> Self::Output {
assert_eq!(nrows.value() * ncols.value(), self.data.len());
VecStorage {
data: self.data,
nrows,
ncols,
}
}
}
impl<N, C1, R2> ReshapableStorage<N, Dynamic, C1, R2, Dynamic> for VecStorage<N, Dynamic, C1>
where
N: Scalar,
C1: Dim,
R2: DimName,
{
type Output = VecStorage<N, R2, Dynamic>;
fn reshape_generic(self, nrows: R2, ncols: Dynamic) -> Self::Output {
assert_eq!(nrows.value() * ncols.value(), self.data.len());
VecStorage {
data: self.data,
nrows,
ncols,
}
}
}
unsafe impl<N: Scalar, R: DimName> StorageMut<N, R, Dynamic> for VecStorage<N, R, Dynamic>
where
DefaultAllocator: Allocator<N, R, Dynamic, Buffer = Self>,
@ -240,6 +278,42 @@ where
}
}
impl<N, R1, C2> ReshapableStorage<N, R1, Dynamic, Dynamic, C2> for VecStorage<N, R1, Dynamic>
where
N: Scalar,
R1: DimName,
C2: Dim,
{
type Output = VecStorage<N, Dynamic, C2>;
fn reshape_generic(self, nrows: Dynamic, ncols: C2) -> Self::Output {
assert_eq!(nrows.value() * ncols.value(), self.data.len());
VecStorage {
data: self.data,
nrows,
ncols,
}
}
}
impl<N, R1, R2> ReshapableStorage<N, R1, Dynamic, R2, Dynamic> for VecStorage<N, R1, Dynamic>
where
N: Scalar,
R1: DimName,
R2: DimName,
{
type Output = VecStorage<N, R2, Dynamic>;
fn reshape_generic(self, nrows: R2, ncols: Dynamic) -> Self::Output {
assert_eq!(nrows.value() * ncols.value(), self.data.len());
VecStorage {
data: self.data,
nrows,
ncols,
}
}
}
#[cfg(feature = "abomonation-serialize")]
impl<N: Abomonation, R: Dim, C: Dim> Abomonation for VecStorage<N, R, C> {
unsafe fn entomb<W: Write>(&self, writer: &mut W) -> IOResult<()> {