diff --git a/Cargo.toml b/Cargo.toml index 579192d3..8473744e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,20 +40,14 @@ anyhow = "1.0" paste = "1.0" #num-complex = "0.3" netcdf = { version = "0.7", optional = true, default-features = false } -pyo3 = { version = "0.22", optional = true, features = [ - "auto-initialize", - "gil-refs", -] } +pyo3 = { version = "0.22", optional = true, features = ["auto-initialize", "gil-refs"] } blas = { version = "0.22", optional = true } lapack = { version = "0.19", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } json = { version = "0.12", optional = true } -arrow2 = { version = "0.18", features = [ - "io_parquet", - "io_parquet_compression", -], optional = true } +arrow2 = { version = "0.18", features = ["io_parquet", "io_parquet_compression"], optional = true } num-complex = { version = "0.4", optional = true } -rayon = "1.10" +rayon = { version = "1.10", optional = true } [package.metadata.docs.rs] rustdoc-args = ["--html-in-header", "katex-header.html", "--cfg", "docsrs"] @@ -65,6 +59,7 @@ plot = ["pyo3"] nc = ["netcdf"] parquet = ["arrow2"] complex = ["num-complex", "matrixmultiply/cgemm"] +parallel = ["rayon"] [[bench]] path = "benches/parallel_rayon/matrix_benchmark.rs" diff --git a/src/fuga/mod.rs b/src/fuga/mod.rs index 216dcbd8..5b4942d2 100644 --- a/src/fuga/mod.rs +++ b/src/fuga/mod.rs @@ -164,6 +164,13 @@ pub use crate::traits::{ sugar::{Scalable, ScalableMut, VecOps, ConvToMat}, }; +#[cfg(feature = "parallel")] +pub use crate::traits::{ + fp::{ParallelFPVector, ParallelFPMatrix}, + math::{ParallelInnerProduct, ParallelMatrixProduct, ParallelNormed, ParallelVector, ParallelVectorProduct}, + mutable::ParallelMutFP, +}; + #[allow(unused_imports)] pub use crate::structure::{ matrix::*, diff --git a/src/lib.rs b/src/lib.rs index 916878b5..4ee752c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -209,4 +209,5 @@ pub mod util; #[cfg(feature = "complex")] pub mod complex; +#[cfg(feature = "parallel")] extern crate rayon; diff --git a/src/structure/matrix.rs b/src/structure/matrix.rs index 9cad5cfe..66d82553 100644 --- a/src/structure/matrix.rs +++ b/src/structure/matrix.rs @@ -610,6 +610,7 @@ extern crate lapack; use blas::{daxpy, dgemm, dgemv}; #[cfg(feature = "O3")] use lapack::{dgecon, dgeqrf, dgesvd, dgetrf, dgetri, dgetrs, dorgqr, dpotrf}; +#[cfg(feature = "parallel")] use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -617,7 +618,6 @@ use serde::{Deserialize, Serialize}; pub use self::Shape::{Col, Row}; use crate::numerical::eigen::{eigen, EigenMethod}; use crate::structure::dataframe::{Series, TypedVector}; -use crate::traits::math::{ParallelInnerProduct, ParallelNormed}; use crate::traits::sugar::ScalableMut; use crate::traits::{ fp::{FPMatrix, FPVector}, @@ -625,6 +625,8 @@ use crate::traits::{ math::{InnerProduct, LinearOp, MatrixProduct, Norm, Normed, Vector}, mutable::MutMatrix, }; +#[cfg(feature = "parallel")] +use crate::traits::math::{ParallelInnerProduct, ParallelNormed}; use crate::util::{ low_level::{copy_vec_ptr, swap_vec_ptr}, non_macro::{cbind, eye, rbind, zeros}, @@ -1342,27 +1344,6 @@ impl Matrix { mat } - /// From index operations in parallel - pub fn par_from_index(f: F, size: (usize, usize)) -> Matrix - where - F: Fn(usize, usize) -> G + Copy + Send + Sync, - G: Into, - { - let row = size.0; - let col = size.1; - - let data = (0..row) - .into_par_iter() - .flat_map(|i| { - (0..col) - .into_par_iter() - .map(|j| f(i, j).into()) - .collect::>() - }) - .collect::>(); - matrix(data, row, col, Row) - } - /// Matrix to `Vec>` /// /// To send `Matrix` to `inline-python` @@ -1480,6 +1461,28 @@ impl Matrix { let v: Vec = series.to_vec(); matrix(v, row, col, shape) } + + /// From index operations in parallel + #[cfg(feature = "parallel")] + fn par_from_index(f: F, size: (usize, usize)) -> Matrix + where + F: Fn(usize, usize) -> G + Copy + Send + Sync, + G: Into, + { + let row = size.0; + let col = size.1; + + let data = (0..row) + .into_par_iter() + .flat_map(|i| { + (0..col) + .into_par_iter() + .map(|j| f(i, j).into()) + .collect::>() + }) + .collect::>(); + matrix(data, row, col, Row) + } } // ============================================================================= @@ -1573,6 +1576,16 @@ impl Vector for Matrix { impl Normed for Matrix { type UnsignedScalar = f64; + + /// Norm of Matrix + /// + /// # Example + /// ``` + /// use peroxide::fuga::*; + /// + /// let a = ml_matrix("1 2;3 4"); + /// assert_eq!(a.norm(Norm::F), (1f64 + 4f64 + 9f64 + 16f64).sqrt()); + /// ``` fn norm(&self, kind: Norm) -> f64 { match kind { Norm::F => { @@ -1639,9 +1652,20 @@ impl Normed for Matrix { } } +#[cfg(feature = "parallel")] impl ParallelNormed for Matrix { type UnsignedScalar = f64; + /// Parallel version of norm + /// + /// # Example + /// ``` + /// use peroxide::fuga::*; + /// + /// let a = ml_matrix("1 2;3 4"); + /// assert_eq!(a.par_norm(Norm::F), (1f64 + 4f64 + 9f64 + 16f64).sqrt()); + /// ``` + /// fn par_norm(&self, kind: Norm) -> f64 { match kind { Norm::F => { @@ -1716,6 +1740,7 @@ impl InnerProduct for Matrix { } /// Frobenius inner product in parallel +#[cfg(feature = "parallel")] impl ParallelInnerProduct for Matrix { fn par_dot(&self, rhs: &Self) -> f64 { if self.shape == rhs.shape { diff --git a/src/structure/vector.rs b/src/structure/vector.rs index 3f5e4630..33ff4e79 100644 --- a/src/structure/vector.rs +++ b/src/structure/vector.rs @@ -266,13 +266,16 @@ extern crate blas; #[cfg(feature = "O3")] use blas::{daxpy, ddot, dnrm2, idamax}; - -use crate::rayon::prelude::*; +#[cfg(feature = "parallel")] +use crate::rayon::iter::{ParallelIterator, IntoParallelIterator, IntoParallelRefIterator, IndexedParallelIterator, IntoParallelRefMutIterator}; use crate::structure::matrix::{matrix, Matrix, Row}; -use crate::traits::fp::ParallelFPVector; -use crate::traits::math::{ParallelInnerProduct, ParallelNormed, ParallelVectorProduct}; -use crate::traits::mutable::ParallelMutFP; +#[cfg(feature = "parallel")] +use crate::traits::{ + fp::ParallelFPVector, + math::{ParallelInnerProduct, ParallelNormed, ParallelVectorProduct}, + mutable::ParallelMutFP, +}; use crate::traits::{ fp::FPVector, general::Algorithm, @@ -414,6 +417,7 @@ impl FPVector for Vec { } } +#[cfg(feature = "parallel")] impl ParallelFPVector for Vec { type Scalar = f64; @@ -516,6 +520,7 @@ where } /// Explicit version of `map` in parallel +#[cfg(feature = "parallel")] pub fn par_map(f: F, xs: &[T]) -> Vec where F: Fn(T) -> T + Send + Sync, @@ -537,7 +542,6 @@ where s = f(s, x); } s - // Parallel version: !unimplemented() } /// Explicit version of `zip_with` @@ -555,6 +559,7 @@ where } /// Explicit version of `zip_with` in parallel +#[cfg(feature = "parallel")] pub fn par_zip_with(f: F, xs: &[T], ys: &[T]) -> Vec where F: Fn(T, T) -> T + Send + Sync, @@ -588,6 +593,7 @@ impl MutFP for Vec { } } +#[cfg(feature = "parallel")] impl ParallelMutFP for Vec { type Scalar = f64; @@ -831,6 +837,7 @@ impl Normed for Vec { } } +#[cfg(feature = "parallel")] impl ParallelNormed for Vec { type UnsignedScalar = f64; @@ -896,6 +903,7 @@ impl InnerProduct for Vec { } } +#[cfg(feature = "parallel")] impl ParallelInnerProduct for Vec { fn par_dot(&self, rhs: &Self) -> f64 { #[cfg(feature = "O3")] @@ -959,6 +967,7 @@ impl VectorProduct for Vec { } } +#[cfg(feature = "parallel")] impl ParallelVectorProduct for Vec { fn par_cross(&self, other: &Self) -> Self { assert_eq!(self.len(), other.len()); diff --git a/src/traits/fp.rs b/src/traits/fp.rs index a0c58609..2a2489ae 100644 --- a/src/traits/fp.rs +++ b/src/traits/fp.rs @@ -60,6 +60,7 @@ pub trait FPMatrix { } /// Functional Programming tools for Vector in Parallel (Uses Rayon crate) +#[cfg(feature = "parallel")] pub trait ParallelFPVector { type Scalar; @@ -79,21 +80,24 @@ pub trait ParallelFPVector { } /// Functional Programming for Matrix in Parallel (Uses Rayon crate) +#[cfg(feature = "parallel")] pub trait ParallelFPMatrix { - fn par_fmap(&self, f: F) -> Matrix + type Scalar; + + fn par_fmap(&self, f: F) -> Self where - F: Fn(f64) -> f64 + Send + Sync; - fn par_reduce(&self, init: T, f: F) -> f64 + F: Fn(Self::Scalar) -> Self::Scalar + Send + Sync; + fn par_reduce(&self, init: T, f: F) -> Self::Scalar where - F: Fn(f64, f64) -> f64 + Send + Sync, - T: Into + Copy + Clone + Send + Sync; - fn par_zip_with(&self, f: F, other: &Matrix) -> Matrix + F: Fn(Self::Scalar, Self::Scalar) -> Self::Scalar + Send + Sync, + T: Into + Copy + Clone + Send + Sync; + fn par_zip_with(&self, f: F, other: &Self) -> Self where - F: Fn(f64, f64) -> f64 + Send + Sync; - fn par_col_reduce(&self, f: F) -> Vec + F: Fn(Self::Scalar, Self::Scalar) -> Self::Scalar + Send + Sync; + fn par_col_reduce(&self, f: F) -> Vec where - F: Fn(Vec) -> f64 + Send + Sync; - fn par_row_reduce(&self, f: F) -> Vec + F: Fn(Vec) -> Self::Scalar + Send + Sync; + fn par_row_reduce(&self, f: F) -> Vec where - F: Fn(Vec) -> f64 + Send + Sync; + F: Fn(Vec) -> Self::Scalar + Send + Sync; } diff --git a/src/traits/math.rs b/src/traits/math.rs index 59c38975..ce10bddb 100644 --- a/src/traits/math.rs +++ b/src/traits/math.rs @@ -103,6 +103,7 @@ impl Normed for f64 { // ============================================================================= /// Mathematical Vector in Parallel +#[cfg(feature = "parallel")] pub trait ParallelVector { type Scalar; fn par_add_vec(&self, rhs: &Self) -> Self; @@ -111,22 +112,26 @@ pub trait ParallelVector { } /// Normed Vector in Parallel +#[cfg(feature = "parallel")] pub trait ParallelNormed: Vector { type UnsignedScalar; fn par_norm(&self, kind: Norm) -> Self::UnsignedScalar; } /// Inner product Vector in Parallel +#[cfg(feature = "parallel")] pub trait ParallelInnerProduct: ParallelNormed { fn par_dot(&self, rhs: &Self) -> Self::Scalar; } /// Matrix Products in Parallel +#[cfg(feature = "parallel")] pub trait ParallelMatrixProduct { - fn par_hadamard(&self, other: &Self) -> Matrix; + fn par_hadamard(&self, other: &Self) -> Self; } /// Vector Products in Parallel +#[cfg(feature = "parallel")] pub trait ParallelVectorProduct: Vector { fn par_cross(&self, other: &Self) -> Self; }