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