Path: blob/main/crates/polars-expr/src/reduce/bitwise.rs
8412 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<()> {161let &[values] = values else { unreachable!() };162assert!(values.dtype() == &DataType::Boolean);163let values = values.as_materialized_series_maintain_scalar();164let ca: &BooleanChunked = values.as_ref().as_ref();165if let Some(value) = ca.xor_reduce() {166// SAFETY: indices are in-bounds guaranteed by trait167unsafe {168self.values.xor_pos_unchecked(group_idx as usize, value);169}170}171if ca.len() != ca.null_count() {172self.mask.set(group_idx as usize, true);173}174Ok(())175}176177unsafe fn update_groups_while_evicting(178&mut self,179values: &[&Column],180subset: &[IdxSize],181group_idxs: &[EvictIdx],182_seq_id: u64,183) -> PolarsResult<()> {184let &[values] = values else { unreachable!() };185assert!(values.dtype() == &DataType::Boolean);186assert!(subset.len() == group_idxs.len());187let values = values.as_materialized_series(); // @scalar-opt188let ca: &BooleanChunked = values.as_ref().as_ref();189let arr = ca.downcast_as_array();190unsafe {191// SAFETY: indices are in-bounds guaranteed by trait.192for (i, g) in subset.iter().zip(group_idxs) {193let ov = arr.get_unchecked(*i as usize);194if g.should_evict() {195self.evicted_values.push(self.values.get_unchecked(g.idx()));196self.evicted_mask.push(self.mask.get_unchecked(g.idx()));197self.values.set_unchecked(g.idx(), ov.unwrap_or(false));198self.mask.set_unchecked(g.idx(), ov.is_some());199} else {200self.values.xor_pos_unchecked(g.idx(), ov.unwrap_or(false));201self.mask.or_pos_unchecked(g.idx(), ov.is_some());202}203}204}205Ok(())206}207208unsafe fn combine_subset(209&mut self,210other: &dyn GroupedReduction,211subset: &[IdxSize],212group_idxs: &[IdxSize],213) -> PolarsResult<()> {214let other = other.as_any().downcast_ref::<Self>().unwrap();215assert!(subset.len() == group_idxs.len());216unsafe {217// SAFETY: indices are in-bounds guaranteed by trait.218for (i, g) in subset.iter().zip(group_idxs) {219self.values220.xor_pos_unchecked(*g as usize, other.values.get_unchecked(*i as usize));221self.mask222.or_pos_unchecked(*g as usize, other.mask.get_unchecked(*i as usize));223}224}225Ok(())226}227228fn take_evictions(&mut self) -> Box<dyn GroupedReduction> {229Box::new(Self {230values: core::mem::take(&mut self.evicted_values).into_mut(),231mask: core::mem::take(&mut self.evicted_mask).into_mut(),232evicted_values: BitmapBuilder::new(),233evicted_mask: BitmapBuilder::new(),234})235}236237fn finalize(&mut self) -> PolarsResult<Series> {238let v = core::mem::take(&mut self.values);239let m = core::mem::take(&mut self.mask);240let arr = BooleanArray::from(v.freeze()).with_validity(Some(m.freeze()));241Ok(Series::from_array(PlSmallStr::EMPTY, arr))242}243244fn as_any(&self) -> &dyn Any {245self246}247}248249250