From bd1d494a7767b30da4e3aa92c12c5dbcfa64472e Mon Sep 17 00:00:00 2001 From: William Gurecky Date: Sun, 9 Mar 2025 21:30:35 -0400 Subject: [PATCH 1/5] adds into_pyarray to faer mat --- Cargo.toml | 4 ++++ src/convert.rs | 27 ++++++++++++++++++++++++++- src/slice_container.rs | 27 ++++++++++++++++++++++++++- tests/to_py.rs | 26 ++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5054b0082..bcade9709 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ license = "BSD-2-Clause" half = { version = "2.0", default-features = false, optional = true } libc = "0.2" nalgebra = { version = ">=0.30, <0.34", default-features = false, optional = true } +faer = { version = "0.21", optional = true } num-complex = ">= 0.2, < 0.5" num-integer = "0.1" num-traits = "0.2" @@ -25,6 +26,9 @@ ndarray = ">= 0.15, < 0.17" pyo3 = { version = "0.23.4", default-features = false, features = ["macros"] } rustc-hash = "2.0" +[features] +faer = ["dep:faer"] + [dev-dependencies] pyo3 = { version = "0.23.3", default-features = false, features = ["auto-initialize"] } nalgebra = { version = ">=0.30, <0.34", default-features = false, features = ["std"] } diff --git a/src/convert.rs b/src/convert.rs index 66c557b1b..acaae8ee0 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -2,7 +2,7 @@ use std::{mem, os::raw::c_int, ptr}; -use ndarray::{ArrayBase, Data, Dim, Dimension, IntoDimension, Ix1, OwnedRepr}; +use ndarray::{ArrayBase, Data, Dim, Dimension, IntoDimension, Ix1, Ix2, OwnedRepr}; use pyo3::{Bound, Python}; use crate::array::{PyArray, PyArrayMethods}; @@ -90,6 +90,31 @@ impl IntoPyArray for Vec { } } +#[cfg(feature = "faer")] +impl IntoPyArray for faer::Mat { + type Item = T; + type Dim = Ix2; + + fn into_pyarray<'py>(mut self, py: Python<'py>) -> Bound<'py, PyArray> { + let dims = Dim([self.nrows(), self.ncols()]); + let rstride = self.row_stride(); + let cstride = self.col_stride(); + // let strides = [mem::size_of::() as npy_intp, mem::size_of::() as npy_intp]; + let strides = [rstride*mem::size_of::() as npy_intp, cstride*mem::size_of::() as npy_intp]; + let data_ptr = self.as_ptr_mut(); + unsafe { + PyArray::from_raw_parts( + py, + dims, + strides.as_ptr(), + data_ptr, + PySliceContainer::from(self), + ) + } + } +} + + impl IntoPyArray for ArrayBase, D> where A: Element, diff --git a/src/slice_container.rs b/src/slice_container.rs index 0c29eae61..64f9340f3 100644 --- a/src/slice_container.rs +++ b/src/slice_container.rs @@ -1,4 +1,4 @@ -use std::{mem, ptr}; +use std::{mem, ptr, slice::from_raw_parts_mut}; use ndarray::{ArrayBase, Dimension, OwnedRepr}; use pyo3::pyclass; @@ -71,6 +71,31 @@ impl From> for PySliceContainer { } } +#[cfg(feature = "faer")] +impl From> for PySliceContainer { + fn from(data: faer::Mat) -> Self { + unsafe fn drop_faer_mat(ptr: *mut u8, len: usize, _cap: usize) { + faer::mat::MatMut::from_raw_parts_mut(ptr as *mut T, len, 1, 1, 1); + } + + // FIXME(adamreichold): Use `Vec::into_raw_parts` + // when it becomes stable and compatible with our MSRV. + let mut data = mem::ManuallyDrop::new(data); + + let ptr = data.as_ptr_mut() as *mut u8; + let len = data.nrows() * data.ncols(); + let cap = 0; + let drop = drop_faer_mat::; + + Self { + ptr, + len, + cap, + drop, + } + } +} + impl From, D>> for PySliceContainer where A: Send + Sync, diff --git a/tests/to_py.rs b/tests/to_py.rs index c18d2d6db..2ab458feb 100644 --- a/tests/to_py.rs +++ b/tests/to_py.rs @@ -287,6 +287,32 @@ fn slice_container_type_confusion() { let _py_arr = vec![1, 2, 3].into_pyarray(py); }); } +#[cfg(feature = "faer")] +#[test] +fn faer_mat_to_numpy() { + let faer_mat: faer::Mat = faer::Scale(2.0)*faer::mat::Mat::::identity(2, 2); + let faer_mat_wide: faer::Mat = faer::mat![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]; + let faer_mat_tall: faer::Mat = faer_mat_wide.transpose().to_owned(); + Python::with_gil(|py| { + let mat_pyarray = faer_mat.into_pyarray(py); + let mat_wide_pyarray = faer_mat_wide.into_pyarray(py); + let mat_tall_pyarray = faer_mat_tall.into_pyarray(py); + assert_eq!( + mat_pyarray.readonly().as_array(), + array![[2.0f64, 0.0f64], [0.0f64, 2.0f64]] + ); + assert_eq!( + mat_wide_pyarray.readonly().as_array(), + array![[1.0f64, 2.0, 3.0], [4.0, 5.0, 6.0]] + ); + assert_eq!( + mat_tall_pyarray.readonly().as_array(), + array![[1.0f64, 4.0], + [2.0, 5.0], + [3.0, 6.0]] + ); + }); +} #[cfg(feature = "nalgebra")] #[test] From e0ec945d2517fd46c446f817aa8203b6075fb9c0 Mon Sep 17 00:00:00 2001 From: William Gurecky Date: Sun, 9 Mar 2025 21:46:22 -0400 Subject: [PATCH 2/5] drop_faer_mat update --- src/slice_container.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slice_container.rs b/src/slice_container.rs index 64f9340f3..034200b6f 100644 --- a/src/slice_container.rs +++ b/src/slice_container.rs @@ -75,7 +75,7 @@ impl From> for PySliceContainer { impl From> for PySliceContainer { fn from(data: faer::Mat) -> Self { unsafe fn drop_faer_mat(ptr: *mut u8, len: usize, _cap: usize) { - faer::mat::MatMut::from_raw_parts_mut(ptr as *mut T, len, 1, 1, 1); + let _ = faer::mat::MatMut::from_raw_parts_mut(ptr as *mut T, len, 1, 1, 1); } // FIXME(adamreichold): Use `Vec::into_raw_parts` From d6d942f1dde83be77bd0cab7e18bbfacccbf2a0d Mon Sep 17 00:00:00 2001 From: William Gurecky Date: Tue, 11 Mar 2025 21:02:05 -0400 Subject: [PATCH 3/5] cleanup and apply suggested lint fixes --- src/convert.rs | 7 ++++--- src/slice_container.rs | 13 ++++++------- tests/to_py.rs | 7 +++---- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index acaae8ee0..16ca0255e 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -99,8 +99,10 @@ impl IntoPyArray for faer::Mat { let dims = Dim([self.nrows(), self.ncols()]); let rstride = self.row_stride(); let cstride = self.col_stride(); - // let strides = [mem::size_of::() as npy_intp, mem::size_of::() as npy_intp]; - let strides = [rstride*mem::size_of::() as npy_intp, cstride*mem::size_of::() as npy_intp]; + let strides = [ + rstride * mem::size_of::() as npy_intp, + cstride * mem::size_of::() as npy_intp, + ]; let data_ptr = self.as_ptr_mut(); unsafe { PyArray::from_raw_parts( @@ -114,7 +116,6 @@ impl IntoPyArray for faer::Mat { } } - impl IntoPyArray for ArrayBase, D> where A: Element, diff --git a/src/slice_container.rs b/src/slice_container.rs index 034200b6f..9501fe693 100644 --- a/src/slice_container.rs +++ b/src/slice_container.rs @@ -1,4 +1,4 @@ -use std::{mem, ptr, slice::from_raw_parts_mut}; +use std::{mem, ptr}; use ndarray::{ArrayBase, Dimension, OwnedRepr}; use pyo3::pyclass; @@ -74,17 +74,16 @@ impl From> for PySliceContainer { #[cfg(feature = "faer")] impl From> for PySliceContainer { fn from(data: faer::Mat) -> Self { - unsafe fn drop_faer_mat(ptr: *mut u8, len: usize, _cap: usize) { - let _ = faer::mat::MatMut::from_raw_parts_mut(ptr as *mut T, len, 1, 1, 1); + unsafe fn drop_faer_mat(ptr: *mut u8, len_nrows: usize, cap_ncols: usize) { + let _ = faer::mat::MatMut::from_raw_parts_mut( + ptr as *mut T, len_nrows, cap_ncols, 1, cap_ncols as isize); } - // FIXME(adamreichold): Use `Vec::into_raw_parts` - // when it becomes stable and compatible with our MSRV. let mut data = mem::ManuallyDrop::new(data); let ptr = data.as_ptr_mut() as *mut u8; - let len = data.nrows() * data.ncols(); - let cap = 0; + let len = data.nrows(); + let cap = data.ncols(); let drop = drop_faer_mat::; Self { diff --git a/tests/to_py.rs b/tests/to_py.rs index 2ab458feb..aece50b25 100644 --- a/tests/to_py.rs +++ b/tests/to_py.rs @@ -287,10 +287,11 @@ fn slice_container_type_confusion() { let _py_arr = vec![1, 2, 3].into_pyarray(py); }); } + #[cfg(feature = "faer")] #[test] fn faer_mat_to_numpy() { - let faer_mat: faer::Mat = faer::Scale(2.0)*faer::mat::Mat::::identity(2, 2); + let faer_mat: faer::Mat = faer::Scale(2.0) * faer::mat::Mat::::identity(2, 2); let faer_mat_wide: faer::Mat = faer::mat![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]; let faer_mat_tall: faer::Mat = faer_mat_wide.transpose().to_owned(); Python::with_gil(|py| { @@ -307,9 +308,7 @@ fn faer_mat_to_numpy() { ); assert_eq!( mat_tall_pyarray.readonly().as_array(), - array![[1.0f64, 4.0], - [2.0, 5.0], - [3.0, 6.0]] + array![[1.0f64, 4.0], [2.0, 5.0], [3.0, 6.0]] ); }); } From cd050090c8608281c30895e168b2edf1aabc9b54 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Thu, 13 Mar 2025 21:27:43 +0000 Subject: [PATCH 4/5] formatting --- src/slice_container.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slice_container.rs b/src/slice_container.rs index 9501fe693..8df0eb32a 100644 --- a/src/slice_container.rs +++ b/src/slice_container.rs @@ -76,7 +76,12 @@ impl From> for PySliceContainer { fn from(data: faer::Mat) -> Self { unsafe fn drop_faer_mat(ptr: *mut u8, len_nrows: usize, cap_ncols: usize) { let _ = faer::mat::MatMut::from_raw_parts_mut( - ptr as *mut T, len_nrows, cap_ncols, 1, cap_ncols as isize); + ptr as *mut T, + len_nrows, + cap_ncols, + 1, + cap_ncols as isize, + ); } let mut data = mem::ManuallyDrop::new(data); From 9e6045f042bd7e6f3a5f9a17b6b78f1d0a365a2a Mon Sep 17 00:00:00 2001 From: William Gurecky Date: Tue, 18 Mar 2025 14:18:21 -0400 Subject: [PATCH 5/5] bump faer version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6e3114cc5..54626ea62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ license = "BSD-2-Clause" half = { version = "2.0", default-features = false, optional = true } libc = "0.2" nalgebra = { version = ">=0.30, <0.34", default-features = false, optional = true } -faer = { version = "0.21", optional = true } +faer = { version = "0.21.9", optional = true } num-complex = ">= 0.2, < 0.5" num-integer = "0.1" num-traits = "0.2"