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(°ree) = 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;