This is an automated email from the ASF dual-hosted git repository.

nevime pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new 599a63e  ARROW-11428: [Rust] Add power_scalar kernel
599a63e is described below

commit 599a63ed25cfc2ff59415ec4d04e9ee90c022e7e
Author: Ritchie Vink <[email protected]>
AuthorDate: Tue Feb 9 10:12:40 2021 +0200

    ARROW-11428: [Rust] Add power_scalar kernel
    
    Adds a SISD and SIMD kernel to raise a `Float32/64` array to a power of a 
`scalar` of the same type. We could also make a thin `sqrt` wrapper.
    
    I also added a `unary_op` fn to `ArrowNumeric` type as this seemed the most 
generic way to implement this. Next PR I could add support for a binary version 
of this (e.g. array to the power of array).
    
    _edit_:
    The `ArrowFloatNumericType` trait was added because the 
[Simd::powf](https://rust-lang.github.io/packed_simd/packed_simd_2/struct.Simd.html#method.powf-6)
 is only available for float arrays (e.g. `[f32, N]`, `[f64, N]`). However, the 
*packed_simd* crate doesn't expose this functionality via a trait, but directly 
on the type, hence the extra trait.
    
    Closes #9361 from ritchie46/power_kernel
    
    Authored-by: Ritchie Vink <[email protected]>
    Signed-off-by: Neville Dipale <[email protected]>
---
 rust/arrow/src/compute/kernels/arithmetic.rs | 99 +++++++++++++++++++++-------
 rust/arrow/src/datatypes.rs                  | 36 ++++++++++
 2 files changed, 112 insertions(+), 23 deletions(-)

diff --git a/rust/arrow/src/compute/kernels/arithmetic.rs 
b/rust/arrow/src/compute/kernels/arithmetic.rs
index 06d7c90..0677566 100644
--- a/rust/arrow/src/compute/kernels/arithmetic.rs
+++ b/rust/arrow/src/compute/kernels/arithmetic.rs
@@ -30,34 +30,53 @@ use num::{One, Zero};
 use crate::buffer::Buffer;
 #[cfg(simd)]
 use crate::buffer::MutableBuffer;
-use crate::compute::util::combine_option_bitmap;
+use crate::compute::{kernels::arity::unary, util::combine_option_bitmap};
 use crate::datatypes;
 use crate::datatypes::ArrowNumericType;
 use crate::error::{ArrowError, Result};
 use crate::{array::*, util::bit_util};
+use num::traits::Pow;
 #[cfg(simd)]
 use std::borrow::BorrowMut;
 #[cfg(simd)]
 use std::slice::{ChunksExact, ChunksExactMut};
 
-/// Helper function to perform math lambda function on values from single 
array of signed numeric
-/// type. If value is null then the output value is also null, so `-null` is 
`null`.
-pub fn signed_unary_math_op<T, F>(
+/// SIMD vectorized version of `unary_math_op` above specialized for signed 
numerical values.
+#[cfg(simd)]
+fn simd_signed_unary_math_op<T, SIMD_OP, SCALAR_OP>(
     array: &PrimitiveArray<T>,
-    op: F,
+    simd_op: SIMD_OP,
+    scalar_op: SCALAR_OP,
 ) -> Result<PrimitiveArray<T>>
 where
     T: datatypes::ArrowSignedNumericType,
-    T::Native: Neg<Output = T::Native>,
-    F: Fn(T::Native) -> T::Native,
+    SIMD_OP: Fn(T::SignedSimd) -> T::SignedSimd,
+    SCALAR_OP: Fn(T::Native) -> T::Native,
 {
-    let values = array.values().iter().map(|v| op(*v));
-    // JUSTIFICATION
-    //  Benefit
-    //      ~60% speedup
-    //  Soundness
-    //      `values` is an iterator with a known size.
-    let buffer = unsafe { Buffer::from_trusted_len_iter(values) };
+    let lanes = T::lanes();
+    let buffer_size = array.len() * std::mem::size_of::<T::Native>();
+    let mut result = MutableBuffer::new(buffer_size).with_bitset(buffer_size, 
false);
+
+    let mut result_chunks = result.typed_data_mut().chunks_exact_mut(lanes);
+    let mut array_chunks = array.values().chunks_exact(lanes);
+
+    result_chunks
+        .borrow_mut()
+        .zip(array_chunks.borrow_mut())
+        .for_each(|(result_slice, input_slice)| {
+            let simd_input = T::load_signed(input_slice);
+            let simd_result = T::signed_unary_op(simd_input, &simd_op);
+            T::write_signed(simd_result, result_slice);
+        });
+
+    let result_remainder = result_chunks.into_remainder();
+    let array_remainder = array_chunks.remainder();
+
+    result_remainder.into_iter().zip(array_remainder).for_each(
+        |(scalar_result, scalar_input)| {
+            *scalar_result = scalar_op(*scalar_input);
+        },
+    );
 
     let data = ArrayData::new(
         T::DATA_TYPE,
@@ -65,26 +84,26 @@ where
         None,
         array.data_ref().null_buffer().cloned(),
         0,
-        vec![buffer],
+        vec![result.into()],
         vec![],
     );
     Ok(PrimitiveArray::<T>::from(Arc::new(data)))
 }
 
-/// SIMD vectorized version of `signed_unary_math_op` above.
 #[cfg(simd)]
-fn simd_signed_unary_math_op<T, SIMD_OP, SCALAR_OP>(
+fn simd_float_unary_math_op<T, SIMD_OP, SCALAR_OP>(
     array: &PrimitiveArray<T>,
     simd_op: SIMD_OP,
     scalar_op: SCALAR_OP,
 ) -> Result<PrimitiveArray<T>>
 where
-    T: datatypes::ArrowSignedNumericType,
-    SIMD_OP: Fn(T::SignedSimd) -> T::SignedSimd,
+    T: datatypes::ArrowFloatNumericType,
+    SIMD_OP: Fn(T::Simd) -> T::Simd,
     SCALAR_OP: Fn(T::Native) -> T::Native,
 {
     let lanes = T::lanes();
     let buffer_size = array.len() * std::mem::size_of::<T::Native>();
+
     let mut result = MutableBuffer::new(buffer_size).with_bitset(buffer_size, 
false);
 
     let mut result_chunks = result.typed_data_mut().chunks_exact_mut(lanes);
@@ -94,9 +113,9 @@ where
         .borrow_mut()
         .zip(array_chunks.borrow_mut())
         .for_each(|(result_slice, input_slice)| {
-            let simd_input = T::load_signed(input_slice);
-            let simd_result = T::signed_unary_op(simd_input, &simd_op);
-            T::write_signed(simd_result, result_slice);
+            let simd_input = T::load(input_slice);
+            let simd_result = T::unary_op(simd_input, &simd_op);
+            T::write(simd_result, result_slice);
         });
 
     let result_remainder = result_chunks.into_remainder();
@@ -536,7 +555,29 @@ where
     #[cfg(simd)]
     return simd_signed_unary_math_op(array, |x| -x, |x| -x);
     #[cfg(not(simd))]
-    return signed_unary_math_op(array, |x| -x);
+    return Ok(unary(array, |x| -x));
+}
+
+/// Raise array with floating point values to the power of a scalar.
+pub fn powf_scalar<T>(
+    array: &PrimitiveArray<T>,
+    raise: T::Native,
+) -> Result<PrimitiveArray<T>>
+where
+    T: datatypes::ArrowFloatNumericType,
+    T::Native: Pow<T::Native, Output = T::Native>,
+{
+    #[cfg(simd)]
+    {
+        let raise_vector = T::init(raise);
+        return simd_float_unary_math_op(
+            array,
+            |x| T::pow(x, raise_vector),
+            |x| x.pow(raise),
+        );
+    }
+    #[cfg(not(simd))]
+    return Ok(unary(array, |x| x.pow(raise)));
 }
 
 /// Perform `left * right` operation on two arrays. If either left or right 
value is null
@@ -808,4 +849,16 @@ mod tests {
             .collect();
         assert_eq!(expected, actual);
     }
+
+    #[test]
+    fn test_primitive_array_raise_power_scalar() {
+        let a = Float64Array::from(vec![1.0, 2.0, 3.0]);
+        let actual = powf_scalar(&a, 2.0).unwrap();
+        let expected = Float64Array::from(vec![1.0, 4.0, 9.0]);
+        assert_eq!(expected, actual);
+        let a = Float64Array::from(vec![Some(1.0), None, Some(3.0)]);
+        let actual = powf_scalar(&a, 2.0).unwrap();
+        let expected = Float64Array::from(vec![Some(1.0), None, Some(9.0)]);
+        assert_eq!(expected, actual);
+    }
 }
diff --git a/rust/arrow/src/datatypes.rs b/rust/arrow/src/datatypes.rs
index 2231da8..096a930 100644
--- a/rust/arrow/src/datatypes.rs
+++ b/rust/arrow/src/datatypes.rs
@@ -605,6 +605,8 @@ where
 
     /// Writes a SIMD result back to a slice
     fn write(simd_result: Self::Simd, slice: &mut [Self::Native]);
+
+    fn unary_op<F: Fn(Self::Simd) -> Self::Simd>(a: Self::Simd, op: F) -> 
Self::Simd;
 }
 
 #[cfg(not(simd))]
@@ -806,6 +808,14 @@ macro_rules! make_numeric_type {
             fn write(simd_result: Self::Simd, slice: &mut [Self::Native]) {
                 unsafe { simd_result.write_to_slice_unaligned_unchecked(slice) 
};
             }
+
+            #[inline]
+            fn unary_op<F: Fn(Self::Simd) -> Self::Simd>(
+                a: Self::Simd,
+                op: F,
+            ) -> Self::Simd {
+                op(a)
+            }
         }
 
         #[cfg(not(simd))]
@@ -909,6 +919,32 @@ make_signed_numeric_type!(Int64Type, i64x8);
 make_signed_numeric_type!(Float32Type, f32x16);
 make_signed_numeric_type!(Float64Type, f64x8);
 
+#[cfg(simd)]
+pub trait ArrowFloatNumericType: ArrowNumericType {
+    fn pow(base: Self::Simd, raise: Self::Simd) -> Self::Simd;
+}
+
+#[cfg(not(simd))]
+pub trait ArrowFloatNumericType: ArrowNumericType {}
+
+macro_rules! make_float_numeric_type {
+    ($impl_ty:ty, $simd_ty:ident) => {
+        #[cfg(simd)]
+        impl ArrowFloatNumericType for $impl_ty {
+            #[inline]
+            fn pow(base: Self::Simd, raise: Self::Simd) -> Self::Simd {
+                base.powf(raise)
+            }
+        }
+
+        #[cfg(not(simd))]
+        impl ArrowFloatNumericType for $impl_ty {}
+    };
+}
+
+make_float_numeric_type!(Float32Type, f32x16);
+make_float_numeric_type!(Float64Type, f64x8);
+
 /// A subtype of primitive type that represents temporal values.
 pub trait ArrowTemporalType: ArrowPrimitiveType {}
 

Reply via email to