vortex_btrblocks/schemes/integer/
for_.rs1use vortex_array::ArrayId;
7use vortex_array::ArrayRef;
8use vortex_array::Canonical;
9use vortex_array::ExecutionCtx;
10use vortex_array::IntoArray;
11use vortex_array::VTable;
12use vortex_array::arrays::PrimitiveArray;
13use vortex_compressor::builtins::BinaryDictScheme;
14use vortex_compressor::builtins::FloatDictScheme;
15use vortex_compressor::builtins::IntDictScheme;
16use vortex_compressor::builtins::StringDictScheme;
17use vortex_compressor::scheme::AncestorExclusion;
18use vortex_compressor::scheme::ChildSelection;
19use vortex_compressor::scheme::CompressionEstimate;
20use vortex_compressor::scheme::EstimateVerdict;
21use vortex_error::VortexExpect;
22use vortex_error::VortexResult;
23use vortex_fastlanes::FoR;
24use vortex_fastlanes::FoRArrayExt;
25use vortex_fastlanes::FoRArraySlotsExt;
26
27use super::BitPackingScheme;
28use crate::ArrayAndStats;
29use crate::CascadingCompressor;
30use crate::CompressorContext;
31use crate::Scheme;
32use crate::SchemeExt;
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
36pub struct FoRScheme;
37
38impl Scheme for FoRScheme {
39 fn scheme_name(&self) -> &'static str {
40 "vortex.int.for"
41 }
42
43 fn matches(&self, canonical: &Canonical) -> bool {
44 canonical.dtype().is_int()
45 }
46
47 fn produced_encodings(&self) -> Vec<ArrayId> {
48 vec![FoR.id()]
49 }
50
51 fn ancestor_exclusions(&self) -> Vec<AncestorExclusion> {
53 vec![
54 AncestorExclusion {
55 ancestor: IntDictScheme.id(),
56 children: ChildSelection::One(1),
57 },
58 AncestorExclusion {
59 ancestor: FloatDictScheme.id(),
60 children: ChildSelection::One(1),
61 },
62 AncestorExclusion {
63 ancestor: StringDictScheme.id(),
64 children: ChildSelection::One(1),
65 },
66 AncestorExclusion {
67 ancestor: BinaryDictScheme.id(),
68 children: ChildSelection::One(1),
69 },
70 ]
71 }
72
73 fn expected_compression_ratio(
74 &self,
75 data: &ArrayAndStats,
76 compress_ctx: CompressorContext,
77 exec_ctx: &mut ExecutionCtx,
78 ) -> CompressionEstimate {
79 if compress_ctx.finished_cascading() {
82 return CompressionEstimate::Verdict(EstimateVerdict::Skip);
83 }
84 let stats = data.integer_stats(exec_ctx);
85
86 if stats.erased().min_is_zero() {
88 return CompressionEstimate::Verdict(EstimateVerdict::Skip);
89 }
90
91 let for_bitwidth = match stats.erased().max_minus_min().checked_ilog2() {
93 Some(l) => l + 1,
94 None => return CompressionEstimate::Verdict(EstimateVerdict::Skip),
96 };
97
98 if let Some(max_log) = stats
102 .erased()
103 .max_ilog2()
104 .filter(|_| !stats.erased().min_is_negative())
106 {
107 let bitpack_bitwidth = max_log + 1;
108 if for_bitwidth >= bitpack_bitwidth {
109 return CompressionEstimate::Verdict(EstimateVerdict::Skip);
110 }
111 }
112
113 let full_width: u32 = data
114 .array_as_primitive()
115 .ptype()
116 .bit_width()
117 .try_into()
118 .vortex_expect("bit width must fit in u32");
119
120 CompressionEstimate::Verdict(EstimateVerdict::Ratio(
121 full_width as f64 / for_bitwidth as f64,
122 ))
123 }
124
125 fn compress(
126 &self,
127 compressor: &CascadingCompressor,
128 data: &ArrayAndStats,
129 compress_ctx: CompressorContext,
130 exec_ctx: &mut ExecutionCtx,
131 ) -> VortexResult<ArrayRef> {
132 let primitive = data.array().clone().execute::<PrimitiveArray>(exec_ctx)?;
133 let for_array = FoR::encode(primitive, exec_ctx)?;
134 let biased = for_array
135 .encoded()
136 .clone()
137 .execute::<PrimitiveArray>(exec_ctx)?;
138
139 let leaf_ctx = compress_ctx.clone().as_leaf();
144 let biased_data =
145 ArrayAndStats::new(biased.into_array(), compress_ctx.merged_stats_options());
146 let compressed = BitPackingScheme.compress(compressor, &biased_data, leaf_ctx, exec_ctx)?;
147
148 let for_compressed = FoR::try_new(compressed, for_array.reference_scalar().clone())?;
150 for_compressed
151 .as_ref()
152 .statistics()
153 .inherit_from(for_array.as_ref().statistics());
154
155 Ok(for_compressed.into_array())
156 }
157}