Skip to main content

competitive/math/min_plus_convolution/
concave.rs

1use super::{Signed, assert_finite, output_len};
2
3pub(super) fn is_concave<T>(values: &[T]) -> bool
4where
5    T: Signed,
6{
7    values
8        .windows(3)
9        .all(|window| window[1] - window[0] >= window[2] - window[1])
10}
11
12fn bit_width(value: usize) -> u32 {
13    value.checked_ilog2().map_or(0, |log| log + 1)
14}
15
16struct ConcaveEnvelope<'a, T> {
17    arbitrary: &'a [T],
18    concave: &'a [T],
19    leaf_count: usize,
20    query_root: usize,
21    node_curves: Vec<Option<usize>>,
22    result: Vec<T>,
23}
24
25impl<'a, T> ConcaveEnvelope<'a, T>
26where
27    T: Signed,
28{
29    fn new(arbitrary: &'a [T], concave: &'a [T]) -> Self {
30        let output_len = output_len(arbitrary.len(), concave.len());
31        let leaf_count = 1_usize
32            .checked_shl(bit_width(output_len))
33            .expect("min-plus convolution envelope size must fit usize");
34        ConcaveEnvelope {
35            arbitrary,
36            concave,
37            leaf_count,
38            query_root: leaf_count >> bit_width(concave.len() - 1),
39            node_curves: vec![None; leaf_count],
40            result: vec![T::maximum(); output_len],
41        }
42    }
43
44    #[inline]
45    fn value(&self, curve: usize, output: usize) -> T {
46        self.arbitrary[curve] + self.concave[output - curve]
47    }
48
49    #[inline]
50    fn query(&mut self, output: usize) {
51        let mut best = self.result[output];
52        let mut node = (output + self.leaf_count) >> 1;
53        while node >= self.query_root {
54            if let Some(curve) = self.node_curves[node] {
55                best = best.min(self.value(curve, output));
56            }
57            node >>= 1;
58        }
59        self.result[output] = best;
60    }
61
62    #[inline]
63    fn insert_from_left(&mut self, left: usize) {
64        let mut right = left + self.concave.len();
65        let block = 1_usize << (left ^ right).ilog2();
66        right &= !(block - 1);
67        let mut depth = bit_width(right - left - 1);
68        let mut node = (self.leaf_count + left) >> depth;
69        let mut pending = (!self.arbitrary[left].is_maximum()).then_some(left);
70        while depth != 0 {
71            let Some(curve) = pending else {
72                break;
73            };
74            depth -= 1;
75            let middle = ((node << 1 | 1) << depth) - self.leaf_count - 1;
76            if middle < left {
77                node = node << 1 | 1;
78            } else if self.node_curves[node]
79                .is_some_and(|old| self.value(old, middle) < self.value(curve, middle))
80            {
81                node <<= 1;
82            } else {
83                std::mem::swap(&mut self.node_curves[node], &mut pending);
84                node = node << 1 | 1;
85            }
86        }
87        if let Some(curve) = pending {
88            let output = node - self.leaf_count;
89            self.result[output] = self.result[output].min(self.value(curve, output));
90        }
91    }
92
93    #[inline]
94    fn insert_from_right(&mut self, right: usize) {
95        let curve = right - self.concave.len();
96        let block = 1_usize << (curve ^ right).ilog2();
97        let left = right & !(block - 1);
98        if left == right {
99            return;
100        }
101        let mut depth = bit_width(right - left - 1);
102        let mut node = (self.leaf_count + left) >> depth;
103        let mut pending = (!self.arbitrary[curve].is_maximum()).then_some(curve);
104        while depth != 0 {
105            let Some(curve) = pending else {
106                break;
107            };
108            depth -= 1;
109            let middle = ((node << 1 | 1) << depth) - self.leaf_count;
110            if middle >= right {
111                node <<= 1;
112            } else if self.node_curves[node]
113                .is_some_and(|old| self.value(old, middle) < self.value(curve, middle))
114            {
115                node = node << 1 | 1;
116            } else {
117                std::mem::swap(&mut self.node_curves[node], &mut pending);
118                node <<= 1;
119            }
120        }
121        if let Some(curve) = pending {
122            let output = node - self.leaf_count;
123            self.result[output] = self.result[output].min(self.value(curve, output));
124        }
125    }
126
127    fn convolve(mut self) -> Vec<T> {
128        // Curve i is valid on [i, i + concave.len()). The two passes insert
129        // opposite sides of each validity interval into the segment envelope.
130        for left in 0..self.arbitrary.len() {
131            self.insert_from_left(left);
132            self.query(left);
133        }
134        for output in self.arbitrary.len()..self.result.len() {
135            self.query(output);
136        }
137
138        self.node_curves.fill(None);
139        let mut right = self.result.len();
140        while right >= self.concave.len() {
141            self.insert_from_right(right);
142            right -= 1;
143            self.query(right);
144        }
145        for output in 0..self.concave.len() {
146            self.query(output);
147        }
148        self.result
149    }
150}
151
152/// Computes convolution when one finite input is concave using offline envelopes.
153///
154/// The running time is `O((n + m) log(n + m))`. The arbitrary input may
155/// contain `T::maximum()`.
156///
157/// # Panics
158///
159/// Panics unless at least one input is finite and concave, or if the
160/// interval-tree size cannot be represented.
161pub fn min_plus_convolution_concave_envelope<T>(a: &[T], b: &[T]) -> Vec<T>
162where
163    T: Signed,
164{
165    let len = output_len(a.len(), b.len());
166    if len == 0 {
167        return Vec::new();
168    }
169    let a_is_concave = !a.iter().any(T::is_maximum) && is_concave(a);
170    let b_is_concave = !b.iter().any(T::is_maximum) && is_concave(b);
171    let (arbitrary, concave) = if b_is_concave {
172        (a, b)
173    } else if a_is_concave {
174        (b, a)
175    } else {
176        panic!("at least one min-plus convolution input must be finite and concave")
177    };
178    concave_envelope(arbitrary, concave)
179}
180
181pub(super) fn concave_envelope<T>(arbitrary: &[T], concave: &[T]) -> Vec<T>
182where
183    T: Signed,
184{
185    if concave.len() == 1 {
186        return arbitrary
187            .iter()
188            .map(|&value| {
189                if value.is_maximum() {
190                    T::maximum()
191                } else {
192                    value + concave[0]
193                }
194            })
195            .collect();
196    }
197    if arbitrary.len() == 1 {
198        return if arbitrary[0].is_maximum() {
199            vec![T::maximum(); concave.len()]
200        } else {
201            concave.iter().map(|&value| arbitrary[0] + value).collect()
202        };
203    }
204
205    ConcaveEnvelope::new(arbitrary, concave).convolve()
206}
207
208/// Computes convolution of two concave inputs from antidiagonal endpoints.
209///
210/// The running time is `O(n + m)`.
211///
212/// # Panics
213///
214/// Panics unless both inputs are finite and concave.
215pub fn min_plus_convolution_concave_both<T>(a: &[T], b: &[T]) -> Vec<T>
216where
217    T: Signed,
218{
219    let len = output_len(a.len(), b.len());
220    if len == 0 {
221        return Vec::new();
222    }
223    assert_finite(a);
224    assert_finite(b);
225    assert!(
226        is_concave(a) && is_concave(b),
227        "both inputs must be concave"
228    );
229    concave_both(a, b)
230}
231
232pub(super) fn concave_both<T>(a: &[T], b: &[T]) -> Vec<T>
233where
234    T: Signed,
235{
236    let len = output_len(a.len(), b.len());
237    let mut result = Vec::with_capacity(len);
238    for output in 0..len {
239        let first = output.saturating_sub(b.len() - 1);
240        let last = output.min(a.len() - 1);
241        result.push((a[first] + b[output - first]).min(a[last] + b[output - last]));
242    }
243    result
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249    use crate::{math::min_plus_convolution::min_plus_convolution_naive, tools::Xorshift};
250
251    #[test]
252    fn test_concave_algorithms_randomly() {
253        let mut rng = Xorshift::default();
254        for _ in 0..1_000 {
255            let arbitrary_len = rng.random(0..=32);
256            let concave_len: usize = rng.random(0..=32);
257            let arbitrary: Vec<_> = (0..arbitrary_len)
258                .map(|_| {
259                    if rng.random(0_u64..8) == 0 {
260                        i64::MAX
261                    } else {
262                        rng.random(-50_i64..=50)
263                    }
264                })
265                .collect();
266            let mut slopes: Vec<_> = rng
267                .random_iter(-20_i64..=20)
268                .take(concave_len.saturating_sub(1))
269                .collect();
270            slopes.sort_unstable_by(|a, b| b.cmp(a));
271            let mut concave = Vec::with_capacity(concave_len);
272            if concave_len != 0 {
273                concave.push(rng.random(-50_i64..=50));
274            }
275            for slope in slopes {
276                concave.push(concave[concave.len() - 1] + slope);
277            }
278            let other_len: usize = rng.random(0..=32);
279            let mut slopes: Vec<_> = rng
280                .random_iter(-20_i64..=20)
281                .take(other_len.saturating_sub(1))
282                .collect();
283            slopes.sort_unstable_by(|a, b| b.cmp(a));
284            let mut other = Vec::with_capacity(other_len);
285            if other_len != 0 {
286                other.push(rng.random(-50_i64..=50));
287            }
288            for slope in slopes {
289                other.push(other[other.len() - 1] + slope);
290            }
291            assert_eq!(
292                min_plus_convolution_concave_envelope(&arbitrary, &concave),
293                min_plus_convolution_naive(&arbitrary, &concave)
294            );
295            assert_eq!(
296                min_plus_convolution_concave_both(&concave, &other),
297                min_plus_convolution_naive(&concave, &other)
298            );
299        }
300    }
301
302    #[test]
303    fn test_concave_envelope_exhaustively() {
304        let values = [-2_i64, 0, 3, i64::MAX];
305        let mut inputs = vec![Vec::new()];
306        for _ in 0..3 {
307            let prefixes = inputs.clone();
308            for prefix in prefixes {
309                for &value in &values {
310                    let mut input = prefix.clone();
311                    input.push(value);
312                    inputs.push(input);
313                }
314            }
315        }
316        inputs.sort();
317        inputs.dedup();
318        let concave: Vec<_> = inputs
319            .iter()
320            .filter(|input| !input.contains(&i64::MAX) && is_concave(input))
321            .collect();
322        for arbitrary in &inputs {
323            for &structured in &concave {
324                assert_eq!(
325                    min_plus_convolution_concave_envelope(arbitrary, structured),
326                    min_plus_convolution_naive(arbitrary, structured)
327                );
328            }
329        }
330    }
331}