vortex_btrblocks/schemes/integer/
delta.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::DeferredEstimate;
21use vortex_compressor::scheme::DescendantExclusion;
22use vortex_compressor::scheme::EstimateScore;
23use vortex_compressor::scheme::EstimateVerdict;
24use vortex_error::VortexResult;
25use vortex_fastlanes::Delta;
26
27use crate::ArrayAndStats;
28use crate::CascadingCompressor;
29use crate::CompressorContext;
30use crate::GenerateStatsOptions;
31use crate::Scheme;
32use crate::SchemeExt;
33
34#[derive(Debug, Copy, Clone, PartialEq)]
43pub struct DeltaScheme {
44 min_ratio: f64,
45}
46
47impl DeltaScheme {
48 pub const fn new(min_ratio: f64) -> Self {
53 Self { min_ratio }
54 }
55}
56
57impl Default for DeltaScheme {
58 fn default() -> Self {
59 Self::new(1.25)
60 }
61}
62
63const DELTA_PENALTY: f64 = 0.95;
70
71const MIN_DELTA_LEN: usize = 1024;
73
74impl Scheme for DeltaScheme {
75 fn scheme_name(&self) -> &'static str {
76 "vortex.int.delta"
77 }
78
79 fn matches(&self, canonical: &Canonical) -> bool {
80 canonical.dtype().is_int()
81 }
82
83 fn produced_encodings(&self) -> Vec<ArrayId> {
84 vec![Delta.id()]
85 }
86
87 fn num_children(&self) -> usize {
88 2
89 }
90
91 fn descendant_exclusions(&self) -> Vec<DescendantExclusion> {
94 vec![DescendantExclusion {
95 excluded: self.id(),
96 children: ChildSelection::All,
97 }]
98 }
99
100 fn ancestor_exclusions(&self) -> Vec<AncestorExclusion> {
103 vec![
104 AncestorExclusion {
105 ancestor: IntDictScheme.id(),
106 children: ChildSelection::One(1),
107 },
108 AncestorExclusion {
109 ancestor: FloatDictScheme.id(),
110 children: ChildSelection::One(1),
111 },
112 AncestorExclusion {
113 ancestor: StringDictScheme.id(),
114 children: ChildSelection::One(1),
115 },
116 AncestorExclusion {
117 ancestor: BinaryDictScheme.id(),
118 children: ChildSelection::One(1),
119 },
120 ]
121 }
122
123 fn expected_compression_ratio(
124 &self,
125 data: &ArrayAndStats,
126 compress_ctx: CompressorContext,
127 _exec_ctx: &mut ExecutionCtx,
128 ) -> CompressionEstimate {
129 if compress_ctx.finished_cascading() {
131 return CompressionEstimate::Verdict(EstimateVerdict::Skip);
132 }
133 if data.array_len() < MIN_DELTA_LEN {
135 return CompressionEstimate::Verdict(EstimateVerdict::Skip);
136 }
137
138 let min_ratio = self.min_ratio;
141 CompressionEstimate::Deferred(DeferredEstimate::Callback(Box::new(
142 move |_compressor, data, best_so_far, _ctx, exec_ctx| {
143 let primitive = data.array().clone().execute::<PrimitiveArray>(exec_ctx)?;
144 let full_width = primitive.ptype().bit_width() as f64;
145
146 let threshold = best_so_far.and_then(EstimateScore::finite_ratio);
149 if threshold.is_some_and(|t| full_width * DELTA_PENALTY <= t) {
150 return Ok(EstimateVerdict::Skip);
151 }
152
153 let (_bases, deltas) = vortex_fastlanes::delta_compress(&primitive, exec_ctx)?;
157 let delta_stats =
158 ArrayAndStats::new(deltas.into_array(), GenerateStatsOptions::default());
159 let span = delta_stats.integer_stats(exec_ctx).erased().max_minus_min();
160
161 let delta_bits = match span.checked_ilog2() {
164 Some(l) => (l + 1) as f64,
165 None => return Ok(EstimateVerdict::Skip),
166 };
167
168 let ratio = full_width / delta_bits * DELTA_PENALTY;
169 if ratio <= min_ratio {
170 return Ok(EstimateVerdict::Skip);
171 }
172 Ok(EstimateVerdict::Ratio(ratio))
173 },
174 )))
175 }
176
177 fn compress(
178 &self,
179 compressor: &CascadingCompressor,
180 data: &ArrayAndStats,
181 compress_ctx: CompressorContext,
182 exec_ctx: &mut ExecutionCtx,
183 ) -> VortexResult<ArrayRef> {
184 let primitive = data.array().clone().execute::<PrimitiveArray>(exec_ctx)?;
185 let len = primitive.len();
186 let (bases, deltas) = vortex_fastlanes::delta_compress(&primitive, exec_ctx)?;
187
188 let compressed_bases = compressor.compress_child(
189 &bases.into_array(),
190 &compress_ctx,
191 self.id(),
192 0,
193 exec_ctx,
194 )?;
195 let compressed_deltas = compressor.compress_child(
196 &deltas.into_array(),
197 &compress_ctx,
198 self.id(),
199 1,
200 exec_ctx,
201 )?;
202
203 Delta::try_new(compressed_bases, compressed_deltas, 0, len).map(IntoArray::into_array)
204 }
205}