Path: blob/main/crates/polars-expr/src/reduce/bitwise.rs
6940 views
use std::ops::{BitAnd, BitOr, BitXor, Not};12use arrow::array::BooleanArray;3use arrow::types::NativeType;4use num_traits::Zero;5use polars_compute::bitwise::BitwiseKernel;6use polars_core::with_match_physical_integer_polars_type;78use super::*;9use crate::reduce::min_max::{BoolMaxGroupedReduction, BoolMinGroupedReduction};1011pub fn new_bitwise_and_reduction(dtype: DataType) -> Box<dyn GroupedReduction> {12use DataType::*;13use VecMaskGroupedReduction as VMGR;14match dtype {15Boolean => Box::new(BoolMinGroupedReduction::default()),16_ if dtype.is_integer() => {17with_match_physical_integer_polars_type!(dtype.to_physical(), |$T| {18Box::new(VMGR::new(dtype, NumReducer::<BitwiseAnd<$T>>::new()))19})20},21_ => unimplemented!(),22}23}2425pub fn new_bitwise_or_reduction(dtype: DataType) -> Box<dyn GroupedReduction> {26use DataType::*;27use VecMaskGroupedReduction as VMGR;28match dtype {29Boolean => Box::new(BoolMaxGroupedReduction::default()),30_ if dtype.is_integer() => {31with_match_physical_integer_polars_type!(dtype.to_physical(), |$T| {32Box::new(VMGR::new(dtype, NumReducer::<BitwiseOr<$T>>::new()))33})34},35_ => unimplemented!(),36}37}3839pub fn new_bitwise_xor_reduction(dtype: DataType) -> Box<dyn GroupedReduction> {40use DataType::*;41use VecMaskGroupedReduction as VMGR;42match dtype {43Boolean => Box::new(BoolXorGroupedReduction::default()),44_ if dtype.is_integer() => {45with_match_physical_integer_polars_type!(dtype.to_physical(), |$T| {46Box::new(VMGR::new(dtype, NumReducer::<BitwiseXor<$T>>::new()))47})48},49_ => unimplemented!(),50}51}5253struct BitwiseAnd<T>(PhantomData<T>);54struct BitwiseOr<T>(PhantomData<T>);55struct BitwiseXor<T>(PhantomData<T>);5657impl<T> NumericReduction for BitwiseAnd<T>58where59T: PolarsNumericType,60T::Native: BitAnd<Output = T::Native> + Not<Output = T::Native> + Zero,61T::Native: NativeType,62PrimitiveArray<T::Native>: BitwiseKernel<Scalar = T::Native>,63{64type Dtype = T;6566#[inline(always)]67fn init() -> T::Native {68!T::Native::zero() // all_ones(), the identity element69}7071#[inline(always)]72fn combine(a: T::Native, b: T::Native) -> T::Native {73a & b74}7576#[inline(always)]77fn reduce_ca(ca: &ChunkedArray<Self::Dtype>) -> Option<T::Native> {78ca.and_reduce()79}80}8182impl<T> NumericReduction for BitwiseOr<T>83where84T: PolarsNumericType,85T::Native: BitOr<Output = T::Native> + Zero,86T::Native: NativeType,87PrimitiveArray<T::Native>: BitwiseKernel<Scalar = T::Native>,88{89type Dtype = T;9091#[inline(always)]92fn init() -> T::Native {93T::Native::zero() // all_zeroes(), the identity element94}9596#[inline(always)]97fn combine(a: T::Native, b: T::Native) -> T::Native {98a | b99}100101#[inline(always)]102fn reduce_ca(ca: &ChunkedArray<Self::Dtype>) -> Option<T::Native> {103ca.or_reduce()104}105}106107impl<T> NumericReduction for BitwiseXor<T>108where109T: PolarsNumericType,110T::Native: BitXor<Output = T::Native> + Zero,111T::Native: NativeType,112PrimitiveArray<T::Native>: BitwiseKernel<Scalar = T::Native>,113{114type Dtype = T;115116#[inline(always)]117fn init() -> T::Native {118T::Native::zero() // all_zeroes(), the identity element119}120121#[inline(always)]122fn combine(a: T::Native, b: T::Native) -> T::Native {123a ^ b124}125126#[inline(always)]127fn reduce_ca(ca: &ChunkedArray<Self::Dtype>) -> Option<T::Native> {128ca.xor_reduce()129}130}131132#[derive(Default)]133struct BoolXorGroupedReduction {134values: MutableBitmap,135mask: MutableBitmap,136evicted_values: BitmapBuilder,137evicted_mask: BitmapBuilder,138}139140impl GroupedReduction for BoolXorGroupedReduction {141fn new_empty(&self) -> Box<dyn GroupedReduction> {142Box::new(Self::default())143}144145fn reserve(&mut self, additional: usize) {146self.values.reserve(additional);147self.mask.reserve(additional)148}149150fn resize(&mut self, num_groups: IdxSize) {151self.values.resize(num_groups as usize, false);152self.mask.resize(num_groups as usize, false);153}154155fn update_group(156&mut self,157values: &Column,158group_idx: IdxSize,159_seq_id: u64,160) -> PolarsResult<()> {161assert!(values.dtype() == &DataType::Boolean);162let values = values.as_materialized_series_maintain_scalar();163let ca: &BooleanChunked = values.as_ref().as_ref();164if let Some(value) = ca.xor_reduce() {165// SAFETY: indices are in-bounds guaranteed by trait166unsafe {167self.values.xor_pos_unchecked(group_idx as usize, value);168}169}170if ca.len() != ca.null_count() {171self.mask.set(group_idx as usize, true);172}173Ok(())174}175176unsafe fn update_groups_while_evicting(177&mut self,178values: &Column,179subset: &[IdxSize],180group_idxs: &[EvictIdx],181_seq_id: u64,182) -> PolarsResult<()> {183assert!(values.dtype() == &DataType::Boolean);184assert!(subset.len() == group_idxs.len());185let values = values.as_materialized_series(); // @scalar-opt186let ca: &BooleanChunked = values.as_ref().as_ref();187let arr = ca.downcast_as_array();188unsafe {189// SAFETY: indices are in-bounds guaranteed by trait.190for (i, g) in subset.iter().zip(group_idxs) {191let ov = arr.get_unchecked(*i as usize);192if g.should_evict() {193self.evicted_values.push(self.values.get_unchecked(g.idx()));194self.evicted_mask.push(self.mask.get_unchecked(g.idx()));195self.values.set_unchecked(g.idx(), ov.unwrap_or(false));196self.mask.set_unchecked(g.idx(), ov.is_some());197} else {198self.values.xor_pos_unchecked(g.idx(), ov.unwrap_or(false));199self.mask.or_pos_unchecked(g.idx(), ov.is_some());200}201}202}203Ok(())204}205206unsafe fn combine_subset(207&mut self,208other: &dyn GroupedReduction,209subset: &[IdxSize],210group_idxs: &[IdxSize],211) -> PolarsResult<()> {212let other = other.as_any().downcast_ref::<Self>().unwrap();213assert!(subset.len() == group_idxs.len());214unsafe {215// SAFETY: indices are in-bounds guaranteed by trait.216for (i, g) in subset.iter().zip(group_idxs) {217self.values218.xor_pos_unchecked(*g as usize, other.values.get_unchecked(*i as usize));219self.mask220.or_pos_unchecked(*g as usize, other.mask.get_unchecked(*i as usize));221}222}223Ok(())224}225226fn take_evictions(&mut self) -> Box<dyn GroupedReduction> {227Box::new(Self {228values: core::mem::take(&mut self.evicted_values).into_mut(),229mask: core::mem::take(&mut self.evicted_mask).into_mut(),230evicted_values: BitmapBuilder::new(),231evicted_mask: BitmapBuilder::new(),232})233}234235fn finalize(&mut self) -> PolarsResult<Series> {236let v = core::mem::take(&mut self.values);237let m = core::mem::take(&mut self.mask);238let arr = BooleanArray::from(v.freeze()).with_validity(Some(m.freeze()));239Ok(Series::from_array(PlSmallStr::EMPTY, arr))240}241242fn as_any(&self) -> &dyn Any {243self244}245}246247248