vortex_fastlanes/for/array/
for_compress.rs1use num_traits::PrimInt;
5use num_traits::WrappingSub;
6use vortex_array::ExecutionCtx;
7use vortex_array::IntoArray;
8use vortex_array::arrays::PrimitiveArray;
9use vortex_array::dtype::NativePType;
10use vortex_array::expr::stats::Stat;
11use vortex_array::match_each_integer_ptype;
12use vortex_error::VortexResult;
13use vortex_error::vortex_err;
14
15use crate::FoR;
16use crate::FoRArray;
17use crate::FoRData;
18
19impl FoRData {
20 pub fn encode(array: PrimitiveArray, ctx: &mut ExecutionCtx) -> VortexResult<FoRArray> {
21 let array_ref = array.clone().into_array();
22 let min = array_ref
23 .statistics()
24 .compute_stat(Stat::Min, ctx)?
25 .ok_or_else(|| vortex_err!("Min stat not found"))?;
26
27 let encoded = match_each_integer_ptype!(array.ptype(), |T| {
28 compress_primitive::<T>(array, T::try_from(&min)?, ctx)?.into_array()
29 });
30 FoR::try_new(encoded, min)
31 }
32}
33
34fn compress_primitive<T: NativePType + WrappingSub + PrimInt>(
35 parray: PrimitiveArray,
36 min: T,
37 ctx: &mut ExecutionCtx,
38) -> VortexResult<PrimitiveArray> {
39 let encoded = parray.map_each_with_validity::<T, _, _>(ctx, |(v, bool)| {
42 if bool {
43 v.wrapping_sub(&min)
44 } else {
45 T::zero()
46 }
47 })?;
48 Ok(encoded)
49}
50
51#[cfg(test)]
52mod test {
53 use std::sync::LazyLock;
54
55 use itertools::Itertools;
56 use vortex_array::VortexSessionExecute;
57 use vortex_array::array_session;
58 use vortex_array::arrays::primitive::PrimitiveArrayExt;
59 use vortex_array::assert_arrays_eq;
60 use vortex_array::dtype::PType;
61 use vortex_array::expr::stats::StatsProvider;
62 use vortex_array::scalar::Scalar;
63 use vortex_array::validity::Validity;
64 use vortex_buffer::Buffer;
65 use vortex_buffer::buffer;
66 use vortex_session::VortexSession;
67
68 use super::*;
69 use crate::BitPackedData;
70 use crate::r#for::array::FoRArrayExt;
71 use crate::r#for::array::FoRArraySlotsExt;
72 use crate::r#for::array::for_decompress::decompress;
73 use crate::r#for::array::for_decompress::fused_decompress;
74
75 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
76 let session = array_session();
77 crate::initialize(&session);
78 session
79 });
80
81 #[test]
82 fn test_compress_round_trip_small() {
83 let mut ctx = SESSION.create_execution_ctx();
84 let array = PrimitiveArray::new((1i32..10).collect::<Buffer<_>>(), Validity::NonNullable);
85 let compressed = FoRData::encode(array.clone(), &mut ctx).unwrap();
86 assert_eq!(i32::try_from(compressed.reference_scalar()).unwrap(), 1);
87
88 assert_arrays_eq!(compressed, array, &mut ctx);
89 }
90
91 #[test]
92 fn test_compress() {
93 let mut ctx = SESSION.create_execution_ctx();
94 let array = PrimitiveArray::new(
96 (0u32..10_000).map(|v| v + 1_000_000).collect::<Buffer<_>>(),
97 Validity::NonNullable,
98 );
99 let compressed = FoRData::encode(array, &mut ctx).unwrap();
100 assert_eq!(
101 u32::try_from(compressed.reference_scalar()).unwrap(),
102 1_000_000u32
103 );
104 }
105
106 #[test]
107 fn test_zeros() {
108 let mut ctx = SESSION.create_execution_ctx();
109 let array = PrimitiveArray::new(buffer![0i32; 100], Validity::NonNullable);
110 assert_eq!(array.statistics().len(), 0);
111
112 let dtype = array.dtype().clone();
113 let compressed = FoRData::encode(array, &mut ctx).unwrap();
114 assert_eq!(compressed.reference_scalar().dtype(), &dtype);
115 assert!(compressed.reference_scalar().dtype().is_signed_int());
116 assert!(compressed.encoded().dtype().is_signed_int());
117
118 let encoded = compressed.encoded().execute_scalar(0, &mut ctx).unwrap();
119 assert_eq!(encoded, Scalar::from(0i32));
120 }
121
122 #[test]
123 fn test_decompress() {
124 let mut ctx = SESSION.create_execution_ctx();
125 let array = PrimitiveArray::from_iter((0u32..100_000).step_by(1024).map(|v| v + 1_000_000));
127 let compressed = FoRData::encode(array.clone(), &mut ctx).unwrap();
128 assert_arrays_eq!(compressed, array, &mut ctx);
129 }
130
131 #[test]
132 fn test_decompress_fused() {
133 let mut ctx = SESSION.create_execution_ctx();
134 let expect = PrimitiveArray::from_iter((0u32..1024).map(|x| x % 7 + 10));
136 let array = PrimitiveArray::from_iter((0u32..1024).map(|x| x % 7));
137 let bp = BitPackedData::encode(&array.into_array(), 3, &mut ctx).unwrap();
138 let compressed = FoR::try_new(bp.into_array(), 10u32.into()).unwrap();
139 assert_arrays_eq!(compressed, expect, &mut ctx);
140 }
141
142 #[test]
143 fn test_decompress_fused_patches() -> VortexResult<()> {
144 let mut ctx = SESSION.create_execution_ctx();
145 let expect = PrimitiveArray::from_iter((0u32..1024).map(|x| x % 7 + 10));
147 let array = PrimitiveArray::from_iter((0u32..1024).map(|x| x % 7));
148 let bp = BitPackedData::encode(&array.into_array(), 2, &mut ctx)?;
149 let compressed = FoR::try_new(bp.clone().into_array(), 10u32.into())?;
150 let decompressed = fused_decompress::<u32>(&compressed, bp.as_view(), &mut ctx)?;
151 assert_arrays_eq!(decompressed, expect, &mut ctx);
152 Ok(())
153 }
154
155 #[test]
156 fn test_overflow() -> VortexResult<()> {
157 let mut ctx = SESSION.create_execution_ctx();
158 let array = PrimitiveArray::from_iter(i8::MIN..=i8::MAX);
159 let compressed = FoRData::encode(array.clone(), &mut ctx)?;
160 assert_eq!(
161 i8::MIN,
162 compressed
163 .reference_scalar()
164 .as_primitive()
165 .typed_value::<i8>()
166 .unwrap()
167 );
168
169 let encoded = compressed
170 .encoded()
171 .clone()
172 .execute::<PrimitiveArray>(&mut ctx)?
173 .reinterpret_cast(PType::U8);
174 let unsigned: Vec<u8> = (0..=u8::MAX).collect_vec();
175 let expected_unsigned = PrimitiveArray::from_iter(unsigned);
176 assert_eq!(encoded.as_slice::<u8>(), expected_unsigned.as_slice::<u8>());
177
178 let decompressed = decompress(&compressed, &mut ctx)?;
179 array
180 .as_slice::<i8>()
181 .iter()
182 .enumerate()
183 .for_each(|(i, v)| {
184 assert_eq!(
185 *v,
186 i8::try_from(&compressed.execute_scalar(i, &mut ctx).unwrap()).unwrap()
187 );
188 });
189 assert_arrays_eq!(decompressed, array, &mut ctx);
190 Ok(())
191 }
192}