Skip to main content

implicit_predicates_gen/
bound.rs

1//! The error bound of the floating-point filter of a predicate.
2//!
3//! The filter evaluates the difference form in `f64` and accepts the sign of
4//! the result when its magnitude exceeds a bound on the rounding error. The
5//! bound is derived here, node by node, in exact arithmetic.
6
7use crate::differences::{DiffGraph, DiffNode};
8use crate::dyadic::Dyadic;
9use crate::error::Error;
10use crate::graph::NodeId;
11use crate::parse::Position;
12
13/// The exponent of the limit on the magnitude of every computed node.
14const OVERFLOW_EXPONENT: i64 = 1022;
15
16/// The highest degree the filter accepts. Every `c` is at least 1, so a
17/// node of higher degree exceeds `2^1022` for every `D > 1`.
18pub(crate) const MAX_DEGREE: u32 = 1022;
19
20/// The most bits that the exact coefficients of all the nodes may take
21/// together, by the estimate of [`coefficient_bits`].
22pub(crate) const MAX_COEFFICIENT_BITS: u64 = 1 << 26;
23
24/// The bounds of one node of degree `g`, in terms of `D` and `E`:
25/// `|true value| <= m * D^g`, `|computed value| <= c * D^g` and
26/// `|computed - true| <= (alpha * D + beta * E) * D^(g - 1)`.
27#[derive(Clone, Debug)]
28pub(crate) struct NodeBound {
29    pub(crate) degree: u32,
30    pub(crate) m: Dyadic,
31    pub(crate) c: Dyadic,
32    pub(crate) alpha: Dyadic,
33    pub(crate) beta: Dyadic,
34}
35
36/// The exact error bound of the `f64` evaluation of a difference form.
37///
38/// For a call, let `delta` be the largest magnitude of the computed leaves
39/// (the differences of the coordinates of the approximations of the points),
40/// `E` the sum of the two largest errors of the approximations, and
41/// `D = delta + E`. The `f64` value of the form differs from the exact value
42/// at the exact points by at most `(epsilon * D + kappa * E) * D^(k - 1)`,
43/// where `k` is the degree, as long as no product underflows; see
44/// [`FilterBound::constants`] for the range in which underflow and overflow
45/// are accounted for.
46#[derive(Clone, Debug)]
47pub struct FilterBound {
48    /// The degree `k` of the form in the differences.
49    pub degree: u32,
50    /// `epsilon / u`, with `u = 2^-53`, as an exact fraction.
51    pub epsilon_in_u: String,
52    /// `kappa` as an exact fraction.
53    pub kappa_exact: String,
54    /// The number of products `N` in the form; a product used several times
55    /// is counted once.
56    pub products: u32,
57    nodes: Vec<NodeBound>,
58    root: NodeId,
59    d_min_exponent: i64,
60    d_max_exponent: i64,
61}
62
63/// The constants of the filter of a predicate, as `f64`.
64///
65/// The filter decides when `d_min <= D <= d_max` and
66/// `|value| > (epsilon * D + kappa * E) * D^(degree - 1)`.
67#[derive(Clone, Copy, Debug, PartialEq)]
68pub struct FilterConstants {
69    /// The degree `k` of the form.
70    pub degree: u32,
71    /// The coefficient of `D^k`, rounded up.
72    pub epsilon: f64,
73    /// The coefficient of `E * D^(k - 1)`, rounded up.
74    pub kappa: f64,
75    /// The smallest `D` for which the bound covers underflow: a power of two
76    /// below 1.
77    pub d_min: f64,
78    /// The largest `D` for which no node overflows: a power of two above 1.
79    pub d_max: f64,
80}
81
82/// Derives the error bound of the `f64` evaluation of `graph`.
83///
84/// With `u = 2^-53`, every node of degree `g` gets coefficients `m`, `c`,
85/// `alpha` and `beta` such that `|true value| <= m * D^g`,
86/// `|computed value| <= c * D^g` and
87/// `|computed - true| <= (alpha * D + beta * E) * D^(g - 1)`:
88///
89/// - a leaf has `g = 1`, `m = 1 + u`, `c = 1`, `alpha = u`, `beta = 1`;
90/// - a negation is exact and keeps the coefficients of its operand;
91/// - the sum or difference of `x` and `y`, of equal degree, has
92///   `m = m_x + m_y`, `c = (c_x + c_y)(1 + u)`,
93///   `alpha = alpha_x + alpha_y + u (c_x + c_y)` and `beta = beta_x + beta_y`;
94/// - the product of `x` and `y` has `g = g_x + g_y`, `m = m_x m_y`,
95///   `c = c_x c_y (1 + u)`, `alpha = c_x alpha_y + m_y alpha_x + u c_x c_y`
96///   and `beta = c_x beta_y + m_y beta_x`.
97///
98/// The root gives `epsilon = alpha` and `kappa = beta`. Every node is
99/// analysed once, in the order of the graph, so a shared subexpression counts
100/// as the single computation it is.
101///
102/// # Errors
103///
104/// Returns an error, at `position` (the name of the predicate) in `file`,
105/// when the degree is so high that no `D > 1` keeps every computed node
106/// below `2^1022`, or that no `D < 1` keeps the bound clear of underflow (see
107/// [`FilterBound::constants`]), and when the exact coefficients would take
108/// more than `2^26` bits in all. The coefficients of a sum are about 53
109/// bits longer than those of its operands, so a sum written as one chain of
110/// more than about 1,560 terms reaches that limit; the same terms summed in a
111/// balanced tree, with `let`, need far fewer bits. The size is estimated
112/// node by node before any coefficient is computed, so a definition that
113/// is too large fails quickly.
114pub fn filter_bound(
115    graph: &DiffGraph,
116    file: &str,
117    position: Position,
118) -> Result<FilterBound, Error> {
119    if let Some(&degree) = graph.degrees.iter().filter(|&&g| g > MAX_DEGREE).max() {
120        return Err(Error::at(file, position, overflow_message(degree)));
121    }
122    if coefficient_bits(graph) > MAX_COEFFICIENT_BITS {
123        return Err(Error::at(
124            file,
125            position,
126            format!(
127                "the expression is too large for the error bound of the filter: its exact \
128                 coefficients would take more than 2^{} bits in all; a long sum written as \
129                 one chain is the usual cause, and the same terms summed in a balanced \
130                 tree with `let` need far fewer",
131                MAX_COEFFICIENT_BITS.ilog2()
132            ),
133        ));
134    }
135    let u = Dyadic::pow2(-53);
136    let one = Dyadic::integer(1);
137    let one_plus_u = &one + &u;
138    let mut nodes: Vec<NodeBound> = Vec::with_capacity(graph.nodes.len());
139    for (id, node) in graph.nodes.iter().enumerate() {
140        let bound = match *node {
141            DiffNode::Leaf { .. } => NodeBound {
142                degree: 1,
143                m: one_plus_u.clone(),
144                c: one.clone(),
145                alpha: u.clone(),
146                beta: one.clone(),
147            },
148            DiffNode::Neg(x) => nodes[x].clone(),
149            DiffNode::Add(x, y) | DiffNode::Sub(x, y) => {
150                let (x, y) = (&nodes[x], &nodes[y]);
151                let c_sum = &x.c + &y.c;
152                NodeBound {
153                    degree: x.degree,
154                    m: &x.m + &y.m,
155                    c: &c_sum * &one_plus_u,
156                    alpha: &(&x.alpha + &y.alpha) + &(&u * &c_sum),
157                    beta: &x.beta + &y.beta,
158                }
159            }
160            DiffNode::Mul(x, y) => {
161                let (x, y) = (&nodes[x], &nodes[y]);
162                let c_product = &x.c * &y.c;
163                NodeBound {
164                    degree: x.degree + y.degree,
165                    m: &x.m * &y.m,
166                    c: &c_product * &one_plus_u,
167                    alpha: &(&(&x.c * &y.alpha) + &(&y.m * &x.alpha)) + &(&u * &c_product),
168                    beta: &(&x.c * &y.beta) + &(&y.m * &x.beta),
169                }
170            }
171        };
172        debug_assert_eq!(bound.degree, graph.degrees[id]);
173        nodes.push(bound);
174    }
175    let products = graph
176        .nodes
177        .iter()
178        .filter(|n| matches!(n, DiffNode::Mul(..)))
179        .count();
180    let root = &nodes[graph.root];
181    let degree = root.degree;
182    // Every node takes at least 55 bits, so the budget allows fewer than
183    // 2^19 nodes.
184    let products = u32::try_from(products).expect("fewer than 2^32 products");
185    let d_max_exponent = d_max_exponent(&nodes)
186        .map_err(|degree| Error::at(file, position, overflow_message(degree)))?;
187    let d_min_exponent = d_min_exponent(&nodes, root, products).ok_or_else(|| {
188        Error::at(
189            file,
190            position,
191            format!(
192                "the degree {degree} is too high for the floating-point filter: \
193                 no scale of the inputs below 1 keeps its error bound clear of underflow"
194            ),
195        )
196    })?;
197    Ok(FilterBound {
198        degree,
199        epsilon_in_u: (&root.alpha * &Dyadic::pow2(53)).fraction(),
200        kappa_exact: root.beta.fraction(),
201        products,
202        nodes,
203        root: graph.root,
204        d_min_exponent,
205        d_max_exponent,
206    })
207}
208
209/// An upper bound on the bits of all the exact coefficients of `graph`
210/// together, found without computing them, saturated at `u64::MAX`.
211///
212/// Every coefficient of a node is `P(u)` for a polynomial `P` with
213/// nonnegative integer coefficients, so it is an integer times `2^(-53 d)`,
214/// with `d` the degree of `P`, of magnitude at most `P(1)`: its mantissa
215/// has at most `53 d + log2 P(1) + 1` bits. Following the rules of
216/// [`filter_bound`], that is at most 55 bits for a leaf, the operand's
217/// for a negation, 55 more than the larger operand's for a sum or
218/// difference, and 55 more than the two operands' together for a product.
219/// The result is the sum over the nodes.
220fn coefficient_bits(graph: &DiffGraph) -> u64 {
221    let mut bits: Vec<u64> = Vec::with_capacity(graph.nodes.len());
222    for node in &graph.nodes {
223        bits.push(match *node {
224            DiffNode::Leaf { .. } => 55,
225            DiffNode::Neg(x) => bits[x],
226            DiffNode::Add(x, y) | DiffNode::Sub(x, y) => bits[x].max(bits[y]).saturating_add(55),
227            DiffNode::Mul(x, y) => bits[x].saturating_add(bits[y]).saturating_add(55),
228        });
229    }
230    bits.iter().fold(0u64, |total, &b| total.saturating_add(b))
231}
232
233fn overflow_message(degree: u32) -> String {
234    format!(
235        "the degree {degree} is too high for the floating-point filter: \
236         no scale of the inputs above 1 keeps every computed value below 2^{OVERFLOW_EXPONENT}"
237    )
238}
239
240impl FilterBound {
241    /// Whether `n u <= epsilon < n u (1 + 2^-40)`, exactly.
242    ///
243    /// The exact `epsilon` is slightly above an integer multiple of `u`,
244    /// because of the terms in `u^2` and beyond.
245    pub fn epsilon_is_u_times(&self, n: u64) -> bool {
246        let target = &Dyadic::integer(n) * &Dyadic::pow2(-53);
247        close_above(&self.nodes[self.root].alpha, &target)
248    }
249
250    /// Whether `n <= kappa < n (1 + 2^-40)`, exactly.
251    pub fn kappa_is(&self, n: u64) -> bool {
252        close_above(&self.nodes[self.root].beta, &Dyadic::integer(n))
253    }
254
255    /// The constants of the filter.
256    ///
257    /// `epsilon` and `kappa` are the exact values times `1 + 2^-30`, rounded
258    /// up; the inflation covers the rounding of the evaluation of the bound
259    /// itself and an allowance for products that underflow. `d_min` is the
260    /// smallest power of two below 1 for which that allowance covers
261    /// underflow and the bound stays a normal number, and `d_max` the
262    /// largest above 1 for which no computed node exceeds `2^1022`; both
263    /// exist, or [`filter_bound`] would have returned an error.
264    pub fn constants(&self) -> FilterConstants {
265        let inflate = &Dyadic::integer(1) + &Dyadic::pow2(-30);
266        let root = &self.nodes[self.root];
267        FilterConstants {
268            degree: self.degree,
269            epsilon: (&root.alpha * &inflate).round_up(),
270            kappa: (&root.beta * &inflate).round_up(),
271            d_min: Dyadic::pow2(self.d_min_exponent).round_up(),
272            d_max: Dyadic::pow2(self.d_max_exponent).round_up(),
273        }
274    }
275}
276
277/// The exponent of `d_max`: the largest `j >= 1` such that
278/// `c_x * 2^(j g_x) <= 2^1022` at every node `x`.
279///
280/// For `D <= d_max` every computed node then has magnitude at most
281/// `c_x * D^(g_x) <= 2^1022` (for `D < 1`, `D^(g_x) <= 1 < d_max^(g_x)`),
282/// a quarter of the overflow threshold `2^1024`, so no intermediate value
283/// and not the result overflows. An overflow in the evaluation of the bound
284/// itself gives an infinite bound, which only makes the filter abstain.
285///
286/// Returns the degree of a node that overflows for every `j >= 1` if there is
287/// no such `j`.
288fn d_max_exponent(nodes: &[NodeBound]) -> Result<i64, u32> {
289    // `c_x * 2^(j g) <= 2^1022` iff `j g <= 1022 - s` with `s` the
290    // smallest integer such that `c_x <= 2^s`.
291    let (j, degree) = nodes
292        .iter()
293        .map(|n| {
294            let j = (OVERFLOW_EXPONENT - n.c.ceil_log2()).div_euclid(i64::from(n.degree));
295            (j, n.degree)
296        })
297        .min()
298        .expect("a graph has a root");
299    if j >= 1 { Ok(j) } else { Err(degree) }
300}
301
302/// The exponent of `d_min`: the smallest `j <= -1` such that `P = 2^j`
303/// satisfies
304///
305/// 1. `epsilon * P^k * 2^-32 >= N * k * c_max * 2^-1074`, with `N` the
306///    number of products and `c_max` the largest `c` of any node, and
307/// 2. `epsilon * P^k >= 2^-1021`.
308///
309/// Both then hold for every larger `j`.
310///
311/// Why this is sound, for `D >= d_min` and with `B` the rounding-error
312/// bound `(epsilon * D + kappa * E) * D^(k - 1)`:
313///
314/// - The node rules assume that every rounding error is at most `u` times
315///   the computed magnitude. A sum or difference whose result is
316///   subnormal is exact, but a product that underflows can err by up to
317///   `2^-1075` in absolute terms; allow `2^-1074` per product.
318/// - An absolute error at a product `x` reaches the root multiplied by
319///   the derivative of the computation with respect to `x`: a sum over
320///   the paths from `x` to the root of the products of the other factors
321///   met on the way, each with its `(1 + u)` rounding. Every factor is
322///   bounded by its `c * D^g`, so the derivative is at most
323///   `p'(c_x) * D^(k - g_x)`, where `p(t)` is `c_root` computed by the
324///   rules with `c_x` replaced by `t`. That polynomial has nonnegative
325///   coefficients and degree at most `k / g_x`, since degrees add along
326///   products, so `c_x p'(c_x) <= (k / g_x) p(c_x)`; with `c_x >= 1` and
327///   `g_x >= 2` the derivative is at most `k * c_max * D^(k - g_x)`.
328///   Terms of second order in these errors are products of two such
329///   errors, each at most `2^-1074` times factors bounded as above, so they
330///   are smaller than the first-order terms by a factor of about `2^-1000`
331///   and are absorbed by the slack of `2^-32` of the bound below.
332/// - The total underflow error is therefore
333///   `U <= N * k * c_max * 2^-1074 * max(1, D^k)`. For `d_min <= D <= 1`,
334///   condition 1 gives `U <= 2^-32 * epsilon * d_min^k
335///   <= 2^-32 * epsilon * D^k`; for `D > 1` it gives the same, because
336///   condition 1 holds with `d_min^k < 1`. So `U <= 2^-32 * B`, and the
337///   value of the form errs by at most `(1 + 2^-32) * B`.
338/// - The emitted constants are at least `(1 + 2^-30)` times the exact
339///   ones, so the exact value of the emitted bound is at least
340///   `(1 + 2^-30) * B`. Its `f64` evaluation, `d = delta + e`, then
341///   `(EPSILON * d + KAPPA * e) * d * ... * d` with `k - 1` products by
342///   `d`, takes `k + 3` operations, each of which can round down by a
343///   factor `1 - u`. The rounding of `d` enters the bound once for each of
344///   the `k` times `d` is used, so the computed bound is at least
345///   `(1 - u)^(2k + 2)` times its exact value: `k + 3` factors for the
346///   operations and `k - 1` more for the uses of `d` beyond the first
347///   (`e` is an upper bound on `E`, rounded up by the caller, so it adds
348///   none; a caller that rounded it to nearest instead would add one factor
349///   for each use of `e` and of `d`, `3k + 3` in all, within `2k + 8` only
350///   for `k <= 5`). That holds while no partial result is subnormal:
351///   condition 2 keeps every partial result built from `EPSILON * d` at least
352///   `epsilon * min(1, D)^k >= 2^-1021`, and the term `KAPPA * e`, which
353///   may underflow, loses at most `2^-1075`, a fraction `2^-54` of the sum
354///   it joins. The constants were derived for up to `2k + 8` such factors,
355///   and `(1 + 2^-30)(1 - u)^(2k + 8) >= 1 + 2^-31` for `k <= 1022` (higher
356///   degrees are rejected), so the computed bound is at least
357///   `(1 + 2^-31) * B`, which exceeds the error `(1 + 2^-32) * B`.
358///
359/// Returns `None` if no `j <= -1` satisfies both.
360fn d_min_exponent(nodes: &[NodeBound], root: &NodeBound, products: u32) -> Option<i64> {
361    let epsilon = &root.alpha;
362    let c_max = nodes
363        .iter()
364        .map(|n| &n.c)
365        .max()
366        .expect("a graph has a root");
367    let factor = Dyadic::integer(u64::from(products) * u64::from(root.degree));
368    let underflow = &(&factor * c_max) * &Dyadic::pow2(-1074 + 32);
369    let normal = Dyadic::pow2(-1021);
370    let k = i64::from(root.degree);
371    let covers = |j: i64| {
372        let scaled = epsilon * &Dyadic::pow2(j * k);
373        scaled >= underflow && scaled >= normal
374    };
375    if !covers(-1) {
376        return None;
377    }
378    let mut j = -1;
379    while covers(j - 1) {
380        j -= 1;
381    }
382    Some(j)
383}
384
385/// Whether `target <= value < target * (1 + 2^-40)`.
386fn close_above(value: &Dyadic, target: &Dyadic) -> bool {
387    let limit = target * &(&Dyadic::integer(1) + &Dyadic::pow2(-40));
388    target <= value && *value < limit
389}
390
391#[cfg(test)]
392mod tests;