Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/crates/polars-expr/src/reduce/bitwise.rs
6940 views
1
use std::ops::{BitAnd, BitOr, BitXor, Not};
2
3
use arrow::array::BooleanArray;
4
use arrow::types::NativeType;
5
use num_traits::Zero;
6
use polars_compute::bitwise::BitwiseKernel;
7
use polars_core::with_match_physical_integer_polars_type;
8
9
use super::*;
10
use crate::reduce::min_max::{BoolMaxGroupedReduction, BoolMinGroupedReduction};
11
12
pub fn new_bitwise_and_reduction(dtype: DataType) -> Box<dyn GroupedReduction> {
13
use DataType::*;
14
use VecMaskGroupedReduction as VMGR;
15
match dtype {
16
Boolean => Box::new(BoolMinGroupedReduction::default()),
17
_ if dtype.is_integer() => {
18
with_match_physical_integer_polars_type!(dtype.to_physical(), |$T| {
19
Box::new(VMGR::new(dtype, NumReducer::<BitwiseAnd<$T>>::new()))
20
})
21
},
22
_ => unimplemented!(),
23
}
24
}
25
26
pub fn new_bitwise_or_reduction(dtype: DataType) -> Box<dyn GroupedReduction> {
27
use DataType::*;
28
use VecMaskGroupedReduction as VMGR;
29
match dtype {
30
Boolean => Box::new(BoolMaxGroupedReduction::default()),
31
_ if dtype.is_integer() => {
32
with_match_physical_integer_polars_type!(dtype.to_physical(), |$T| {
33
Box::new(VMGR::new(dtype, NumReducer::<BitwiseOr<$T>>::new()))
34
})
35
},
36
_ => unimplemented!(),
37
}
38
}
39
40
pub fn new_bitwise_xor_reduction(dtype: DataType) -> Box<dyn GroupedReduction> {
41
use DataType::*;
42
use VecMaskGroupedReduction as VMGR;
43
match dtype {
44
Boolean => Box::new(BoolXorGroupedReduction::default()),
45
_ if dtype.is_integer() => {
46
with_match_physical_integer_polars_type!(dtype.to_physical(), |$T| {
47
Box::new(VMGR::new(dtype, NumReducer::<BitwiseXor<$T>>::new()))
48
})
49
},
50
_ => unimplemented!(),
51
}
52
}
53
54
struct BitwiseAnd<T>(PhantomData<T>);
55
struct BitwiseOr<T>(PhantomData<T>);
56
struct BitwiseXor<T>(PhantomData<T>);
57
58
impl<T> NumericReduction for BitwiseAnd<T>
59
where
60
T: PolarsNumericType,
61
T::Native: BitAnd<Output = T::Native> + Not<Output = T::Native> + Zero,
62
T::Native: NativeType,
63
PrimitiveArray<T::Native>: BitwiseKernel<Scalar = T::Native>,
64
{
65
type Dtype = T;
66
67
#[inline(always)]
68
fn init() -> T::Native {
69
!T::Native::zero() // all_ones(), the identity element
70
}
71
72
#[inline(always)]
73
fn combine(a: T::Native, b: T::Native) -> T::Native {
74
a & b
75
}
76
77
#[inline(always)]
78
fn reduce_ca(ca: &ChunkedArray<Self::Dtype>) -> Option<T::Native> {
79
ca.and_reduce()
80
}
81
}
82
83
impl<T> NumericReduction for BitwiseOr<T>
84
where
85
T: PolarsNumericType,
86
T::Native: BitOr<Output = T::Native> + Zero,
87
T::Native: NativeType,
88
PrimitiveArray<T::Native>: BitwiseKernel<Scalar = T::Native>,
89
{
90
type Dtype = T;
91
92
#[inline(always)]
93
fn init() -> T::Native {
94
T::Native::zero() // all_zeroes(), the identity element
95
}
96
97
#[inline(always)]
98
fn combine(a: T::Native, b: T::Native) -> T::Native {
99
a | b
100
}
101
102
#[inline(always)]
103
fn reduce_ca(ca: &ChunkedArray<Self::Dtype>) -> Option<T::Native> {
104
ca.or_reduce()
105
}
106
}
107
108
impl<T> NumericReduction for BitwiseXor<T>
109
where
110
T: PolarsNumericType,
111
T::Native: BitXor<Output = T::Native> + Zero,
112
T::Native: NativeType,
113
PrimitiveArray<T::Native>: BitwiseKernel<Scalar = T::Native>,
114
{
115
type Dtype = T;
116
117
#[inline(always)]
118
fn init() -> T::Native {
119
T::Native::zero() // all_zeroes(), the identity element
120
}
121
122
#[inline(always)]
123
fn combine(a: T::Native, b: T::Native) -> T::Native {
124
a ^ b
125
}
126
127
#[inline(always)]
128
fn reduce_ca(ca: &ChunkedArray<Self::Dtype>) -> Option<T::Native> {
129
ca.xor_reduce()
130
}
131
}
132
133
#[derive(Default)]
134
struct BoolXorGroupedReduction {
135
values: MutableBitmap,
136
mask: MutableBitmap,
137
evicted_values: BitmapBuilder,
138
evicted_mask: BitmapBuilder,
139
}
140
141
impl GroupedReduction for BoolXorGroupedReduction {
142
fn new_empty(&self) -> Box<dyn GroupedReduction> {
143
Box::new(Self::default())
144
}
145
146
fn reserve(&mut self, additional: usize) {
147
self.values.reserve(additional);
148
self.mask.reserve(additional)
149
}
150
151
fn resize(&mut self, num_groups: IdxSize) {
152
self.values.resize(num_groups as usize, false);
153
self.mask.resize(num_groups as usize, false);
154
}
155
156
fn update_group(
157
&mut self,
158
values: &Column,
159
group_idx: IdxSize,
160
_seq_id: u64,
161
) -> PolarsResult<()> {
162
assert!(values.dtype() == &DataType::Boolean);
163
let values = values.as_materialized_series_maintain_scalar();
164
let ca: &BooleanChunked = values.as_ref().as_ref();
165
if let Some(value) = ca.xor_reduce() {
166
// SAFETY: indices are in-bounds guaranteed by trait
167
unsafe {
168
self.values.xor_pos_unchecked(group_idx as usize, value);
169
}
170
}
171
if ca.len() != ca.null_count() {
172
self.mask.set(group_idx as usize, true);
173
}
174
Ok(())
175
}
176
177
unsafe fn update_groups_while_evicting(
178
&mut self,
179
values: &Column,
180
subset: &[IdxSize],
181
group_idxs: &[EvictIdx],
182
_seq_id: u64,
183
) -> PolarsResult<()> {
184
assert!(values.dtype() == &DataType::Boolean);
185
assert!(subset.len() == group_idxs.len());
186
let values = values.as_materialized_series(); // @scalar-opt
187
let ca: &BooleanChunked = values.as_ref().as_ref();
188
let arr = ca.downcast_as_array();
189
unsafe {
190
// SAFETY: indices are in-bounds guaranteed by trait.
191
for (i, g) in subset.iter().zip(group_idxs) {
192
let ov = arr.get_unchecked(*i as usize);
193
if g.should_evict() {
194
self.evicted_values.push(self.values.get_unchecked(g.idx()));
195
self.evicted_mask.push(self.mask.get_unchecked(g.idx()));
196
self.values.set_unchecked(g.idx(), ov.unwrap_or(false));
197
self.mask.set_unchecked(g.idx(), ov.is_some());
198
} else {
199
self.values.xor_pos_unchecked(g.idx(), ov.unwrap_or(false));
200
self.mask.or_pos_unchecked(g.idx(), ov.is_some());
201
}
202
}
203
}
204
Ok(())
205
}
206
207
unsafe fn combine_subset(
208
&mut self,
209
other: &dyn GroupedReduction,
210
subset: &[IdxSize],
211
group_idxs: &[IdxSize],
212
) -> PolarsResult<()> {
213
let other = other.as_any().downcast_ref::<Self>().unwrap();
214
assert!(subset.len() == group_idxs.len());
215
unsafe {
216
// SAFETY: indices are in-bounds guaranteed by trait.
217
for (i, g) in subset.iter().zip(group_idxs) {
218
self.values
219
.xor_pos_unchecked(*g as usize, other.values.get_unchecked(*i as usize));
220
self.mask
221
.or_pos_unchecked(*g as usize, other.mask.get_unchecked(*i as usize));
222
}
223
}
224
Ok(())
225
}
226
227
fn take_evictions(&mut self) -> Box<dyn GroupedReduction> {
228
Box::new(Self {
229
values: core::mem::take(&mut self.evicted_values).into_mut(),
230
mask: core::mem::take(&mut self.evicted_mask).into_mut(),
231
evicted_values: BitmapBuilder::new(),
232
evicted_mask: BitmapBuilder::new(),
233
})
234
}
235
236
fn finalize(&mut self) -> PolarsResult<Series> {
237
let v = core::mem::take(&mut self.values);
238
let m = core::mem::take(&mut self.mask);
239
let arr = BooleanArray::from(v.freeze()).with_validity(Some(m.freeze()));
240
Ok(Series::from_array(PlSmallStr::EMPTY, arr))
241
}
242
243
fn as_any(&self) -> &dyn Any {
244
self
245
}
246
}
247
248