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

Maintainance #17

Merged
merged 5 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ license = "MIT"
readme = "README.md"

[features]
default = ["nalgebra", "nalgebra_std", "ndarray", "image"]
nalgebra_std = ["nalgebra/std"]
default = ["alloc", "nalgebra", "ndarray", "image"]
alloc = ["nalgebra?/alloc"]
nalgebra = ["dep:nalgebra"]
ndarray = ["dep:ndarray", "alloc"]
image = ["dep:image", "alloc"]

[dependencies]
ndarray = { version = "0.15.4", default-features = false, optional = true }
nalgebra = { version = "0.30.1", default-features = false, optional = true }
image = { version = "0.24.0", default-features = false, optional = true }
ndarray = { version = "0.16", default-features = false, optional = true }
nalgebra = { version = "0.33", default-features = false, optional = true }
image = { version = "0.25", default-features = false, optional = true }

[package.metadata.docs.rs]
all-features = true
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,16 @@

Provides traits that allow conversion between n-dimensional types in different Rust crates

**NOTE**: By default, this crate includes no conversions. You must choose which crates you want to use using the features:
**NOTE**: By default, this crate includes conversions for all supported crates. If you want to limit compilation, use `no-default-features = true` enable the corresponding feature for each dependency:

* `ndarray`
* `nalgebra`
* `ndarray`
* `image`

When crates are included, any available conversions between the enabled crates are turned on.
When two crate features are enabled, any available conversions between the two crates are turned on.

## Limitations

Right now this crate really only provides conversions to owned and borrowed ndarray types. Some limitations exist with `nalgebra`, as it only utilizes positive strides, while `ndarray` supports negative strides as well. The `image` crate has no concept of strides. Due to this, the `ndarray` crate is the most flexible, and is ideal for interoperability between these various crates.

## Supported Crates
* `image`
* `ndarray`
* `nalgebra`
`nalgebra` currently does not offer a solution to directly pass it an owned vector from `ndarray`, so `into` conversions do perform a copy. It is recommended to create the owned copy in `nalgebra` and then borrow a mutable array view of it using ndarray. You can then populate it accordingly without any copies of the data.
2 changes: 1 addition & 1 deletion src/toimage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
///
/// This uses an associated type to avoid ambiguity for the compiler.
/// By calling this, the compiler always knows the returned type.
pub trait ToImageLuma {
pub trait IntoImageLuma {
type Out;

fn into_image_luma(self) -> Self::Out;
Expand Down
4 changes: 2 additions & 2 deletions src/tonalgebra.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#[cfg(all(feature = "ndarray", feature = "nalgebra_std"))]
#[cfg(feature = "ndarray")]
mod ndarray_impl;

/// Converts a 1 or 2 dimensional type to a nalgebra type.
///
/// This uses an associated type to avoid ambiguity for the compiler.
/// By calling this, the compiler always knows the returned type.
pub trait ToNalgebra {
pub trait IntoNalgebra {
type Out;

fn into_nalgebra(self) -> Self::Out;
Expand Down
120 changes: 58 additions & 62 deletions src/tonalgebra/ndarray_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,189 +3,185 @@
use super::*;

use core::convert::TryFrom;
use nalgebra::Dynamic as Dy;
use nalgebra::Dyn;

/// ```
/// use nshare::ToNalgebra;
/// use nshare::IntoNalgebra;
///
/// let arr = ndarray::arr1(&[0.1, 0.2, 0.3, 0.4]);
/// let arr = ndarray::array![0.1, 0.2, 0.3, 0.4];
/// let m = arr.view().into_nalgebra();
/// assert!(m.iter().eq(&[0.1, 0.2, 0.3, 0.4]));
/// assert_eq!(m.shape(), (4, 1));
/// ```
impl<'a, T> ToNalgebra for ndarray::ArrayView1<'a, T>
impl<'a, T> IntoNalgebra for ndarray::ArrayView1<'a, T>
where
T: nalgebra::Scalar,
{
type Out = nalgebra::DVectorSlice<'a, T>;
type Out = nalgebra::DVectorView<'a, T>;
fn into_nalgebra(self) -> Self::Out {
let len = Dy::new(self.len());
let len = Dyn(self.len());
let ptr = self.as_ptr();
let stride: usize = TryFrom::try_from(self.strides()[0]).expect("Negative stride");
let storage = unsafe {
nalgebra::SliceStorage::from_raw_parts(
nalgebra::ViewStorage::from_raw_parts(
ptr,
(len, nalgebra::Const::<1>),
(nalgebra::Const::<1>, Dy::new(stride)),
(nalgebra::Const::<1>, Dyn(stride)),
)
};
nalgebra::Matrix::from_data(storage)
}
}
/// ```
/// use nshare::ToNalgebra;
/// use nshare::IntoNalgebra;
///
/// let mut arr = ndarray::arr1(&[0.1, 0.2, 0.3, 0.4]);
/// let mut arr = ndarray::array![0.1, 0.2, 0.3, 0.4];
/// let m = arr.view_mut().into_nalgebra();
/// assert!(m.iter().eq(&[0.1, 0.2, 0.3, 0.4]));
/// assert_eq!(m.shape(), (4, 1));
/// ```
impl<'a, T> ToNalgebra for ndarray::ArrayViewMut1<'a, T>
impl<'a, T> IntoNalgebra for ndarray::ArrayViewMut1<'a, T>
where
T: nalgebra::Scalar,
{
type Out = nalgebra::DVectorSliceMut<'a, T>;
type Out = nalgebra::DVectorViewMut<'a, T>;
fn into_nalgebra(mut self) -> Self::Out {
let len = Dy::new(self.len());
let len = Dyn(self.len());
let stride: usize = TryFrom::try_from(self.strides()[0]).expect("Negative stride");
let ptr = self.as_mut_ptr();
let storage = unsafe {
// Drop to not have simultaneously the ndarray and nalgebra valid.
drop(self);
nalgebra::SliceStorageMut::from_raw_parts(
nalgebra::ViewStorageMut::from_raw_parts(
ptr,
(len, nalgebra::Const::<1>),
(nalgebra::Const::<1>, Dy::new(stride)),
(nalgebra::Const::<1>, Dyn(stride)),
)
};
nalgebra::Matrix::from_data(storage)
}
}

/// ```
/// use nshare::ToNalgebra;
/// use nshare::IntoNalgebra;
///
/// let arr = ndarray::arr1(&[0.1, 0.2, 0.3, 0.4]);
/// let arr = ndarray::array![0.1, 0.2, 0.3, 0.4];
/// let m = arr.into_nalgebra();
/// assert!(m.iter().eq(&[0.1, 0.2, 0.3, 0.4]));
/// assert_eq!(m.shape(), (4, 1));
/// ```
impl<T> ToNalgebra for ndarray::Array1<T>
impl<T> IntoNalgebra for ndarray::Array1<T>
where
T: nalgebra::Scalar,
{
type Out = nalgebra::DVector<T>;
fn into_nalgebra(self) -> Self::Out {
let len = Dy::new(self.len());
Self::Out::from_vec_generic(len, nalgebra::Const::<1>, self.into_raw_vec())
let len = Dyn(self.len());
// There is no method to give nalgebra the vector directly where it isn't allocated. If you call
// from_vec_generic, it simply calls from_iterator_generic which uses Iterator::collect(). Due to this,
// the simplest solution is to just pass an iterator over the values. If you come across this because you
// have a performance issue, I would recommend creating the owned data using naglebra and borrowing it with
// ndarray to perform operations on it instead of the other way around.
Self::Out::from_iterator_generic(len, nalgebra::Const::<1>, self.iter().cloned())
}
}

/// ```
/// use nshare::ToNalgebra;
/// use nshare::IntoNalgebra;
///
/// let arr = ndarray::arr2(&[
/// let arr = ndarray::array![
/// [0.1, 0.2, 0.3, 0.4],
/// [0.5, 0.6, 0.7, 0.8],
/// [1.1, 1.2, 1.3, 1.4],
/// [1.5, 1.6, 1.7, 1.8],
/// ]);
/// ];
/// let m = arr.view().into_nalgebra();
/// assert!(m.row(1).iter().eq(&[0.5, 0.6, 0.7, 0.8]));
/// assert_eq!(m.shape(), (4, 4));
/// assert!(arr.t().into_nalgebra().column(1).iter().eq(&[0.5, 0.6, 0.7, 0.8]));
/// ```
impl<'a, T> ToNalgebra for ndarray::ArrayView2<'a, T>
impl<'a, T> IntoNalgebra for ndarray::ArrayView2<'a, T>
where
T: nalgebra::Scalar,
{
type Out = nalgebra::DMatrixSlice<'a, T, Dy, Dy>;
type Out = nalgebra::DMatrixView<'a, T, Dyn, Dyn>;
fn into_nalgebra(self) -> Self::Out {
let nrows = Dy::new(self.nrows());
let ncols = Dy::new(self.ncols());
let nrows = Dyn(self.nrows());
let ncols = Dyn(self.ncols());
let ptr = self.as_ptr();
let stride_row: usize = TryFrom::try_from(self.strides()[0]).expect("Negative row stride");
let stride_col: usize =
TryFrom::try_from(self.strides()[1]).expect("Negative column stride");
let stride_row: usize = TryFrom::try_from(self.strides()[0])
.expect("can only convert positive row stride to nalgebra");
let stride_col: usize = TryFrom::try_from(self.strides()[1])
.expect("can only convert positive col stride to nalgebra");
let storage = unsafe {
nalgebra::SliceStorage::from_raw_parts(
nalgebra::ViewStorage::from_raw_parts(
ptr,
(nrows, ncols),
(Dy::new(stride_row), Dy::new(stride_col)),
(Dyn(stride_row), Dyn(stride_col)),
)
};
nalgebra::Matrix::from_data(storage)
}
}

/// ```
/// use nshare::ToNalgebra;
/// use nshare::IntoNalgebra;
///
/// let mut arr = ndarray::arr2(&[
/// let mut arr = ndarray::array![
/// [0.1, 0.2, 0.3, 0.4],
/// [0.5, 0.6, 0.7, 0.8],
/// [1.1, 1.2, 1.3, 1.4],
/// [1.5, 1.6, 1.7, 1.8],
/// ]);
/// ];
/// let m = arr.view_mut().into_nalgebra();
/// assert!(m.row(1).iter().eq(&[0.5, 0.6, 0.7, 0.8]));
/// assert_eq!(m.shape(), (4, 4));
/// assert!(arr.view_mut().reversed_axes().into_nalgebra().column(1).iter().eq(&[0.5, 0.6, 0.7, 0.8]));
/// ```
impl<'a, T> ToNalgebra for ndarray::ArrayViewMut2<'a, T>
impl<'a, T> IntoNalgebra for ndarray::ArrayViewMut2<'a, T>
where
T: nalgebra::Scalar,
{
type Out = nalgebra::DMatrixSliceMut<'a, T, Dy, Dy>;
type Out = nalgebra::DMatrixViewMut<'a, T, Dyn, Dyn>;
fn into_nalgebra(mut self) -> Self::Out {
let nrows = Dy::new(self.nrows());
let ncols = Dy::new(self.ncols());
let stride_row: usize = TryFrom::try_from(self.strides()[0]).expect("Negative row stride");
let stride_col: usize =
TryFrom::try_from(self.strides()[1]).expect("Negative column stride");
let nrows = Dyn(self.nrows());
let ncols = Dyn(self.ncols());
let stride_row: usize = TryFrom::try_from(self.strides()[0])
.expect("can only convert positive row stride to nalgebra");
let stride_col: usize = TryFrom::try_from(self.strides()[1])
.expect("can only convert positive col stride to nalgebra");
let ptr = self.as_mut_ptr();
let storage = unsafe {
// Drop to not have simultaneously the ndarray and nalgebra valid.
drop(self);
nalgebra::SliceStorageMut::from_raw_parts(
nalgebra::ViewStorageMut::from_raw_parts(
ptr,
(nrows, ncols),
(Dy::new(stride_row), Dy::new(stride_col)),
(Dyn(stride_row), Dyn(stride_col)),
)
};
nalgebra::Matrix::from_data(storage)
}
}

/// ```
/// use nshare::ToNalgebra;
/// use nshare::IntoNalgebra;
///
/// let mut arr = ndarray::arr2(&[
/// let mut arr = ndarray::array![
/// [0.1, 0.2, 0.3, 0.4],
/// [0.5, 0.6, 0.7, 0.8],
/// [1.1, 1.2, 1.3, 1.4],
/// [1.5, 1.6, 1.7, 1.8],
/// ]);
/// ];
/// let m = arr.clone().into_nalgebra();
/// assert!(m.row(1).iter().eq(&[0.5, 0.6, 0.7, 0.8]));
/// assert_eq!(m.shape(), (4, 4));
/// assert!(arr.reversed_axes().into_nalgebra().column(1).iter().eq(&[0.5, 0.6, 0.7, 0.8]));
/// ```
impl<T> ToNalgebra for ndarray::Array2<T>
impl<T> IntoNalgebra for ndarray::Array2<T>
where
T: nalgebra::Scalar,
{
type Out = nalgebra::DMatrix<T>;
fn into_nalgebra(self) -> Self::Out {
let std_layout = self.is_standard_layout();
let nrows = Dy::new(self.nrows());
let ncols = Dy::new(self.ncols());
let mut res = Self::Out::from_vec_generic(nrows, ncols, self.into_raw_vec());
if std_layout {
// This can be expensive, but we have no choice since nalgebra VecStorage is always
// column-based.
res.transpose_mut();
}
res
let nrows = Dyn(self.nrows());
let ncols = Dyn(self.ncols());
Self::Out::from_iterator_generic(nrows, ncols, self.t().iter().cloned())
}
}
Loading
Loading