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 check_budget(graph, file, position)?;
120 let u = Dyadic::pow2(-53);
121 let one = Dyadic::integer(1);
122 let one_plus_u = &one + &u;
123 let mut nodes: Vec<NodeBound> = Vec::with_capacity(graph.nodes.len());
124 for (id, node) in graph.nodes.iter().enumerate() {
125 let bound = match *node {
126 DiffNode::Leaf { .. } => NodeBound {
127 degree: 1,
128 m: one_plus_u.clone(),
129 c: one.clone(),
130 alpha: u.clone(),
131 beta: one.clone(),
132 },
133 DiffNode::Neg(x) => nodes[x].clone(),
134 DiffNode::Add(x, y) | DiffNode::Sub(x, y) => {
135 let (x, y) = (&nodes[x], &nodes[y]);
136 let c_sum = &x.c + &y.c;
137 NodeBound {
138 degree: x.degree,
139 m: &x.m + &y.m,
140 c: &c_sum * &one_plus_u,
141 alpha: &(&x.alpha + &y.alpha) + &(&u * &c_sum),
142 beta: &x.beta + &y.beta,
143 }
144 }
145 DiffNode::Mul(x, y) => {
146 let (x, y) = (&nodes[x], &nodes[y]);
147 let c_product = &x.c * &y.c;
148 NodeBound {
149 degree: x.degree + y.degree,
150 m: &x.m * &y.m,
151 c: &c_product * &one_plus_u,
152 alpha: &(&(&x.c * &y.alpha) + &(&y.m * &x.alpha)) + &(&u * &c_product),
153 beta: &(&x.c * &y.beta) + &(&y.m * &x.beta),
154 }
155 }
156 };
157 debug_assert_eq!(bound.degree, graph.degrees[id]);
158 nodes.push(bound);
159 }
160 let products = graph
161 .nodes
162 .iter()
163 .filter(|n| matches!(n, DiffNode::Mul(..)))
164 .count();
165 let root = &nodes[graph.root];
166 let degree = root.degree;
167 // Every node takes at least 55 bits, so the budget allows fewer than
168 // 2^19 nodes.
169 let products = u32::try_from(products).expect("fewer than 2^32 products");
170 let d_max_exponent = d_max_exponent(nodes.iter().map(|n| (&n.c, n.degree)))
171 .map_err(|degree| Error::at(file, position, overflow_message(degree)))?;
172 let c_max = nodes
173 .iter()
174 .map(|n| &n.c)
175 .max()
176 .expect("a graph has a root");
177 let d_min_exponent = d_min_exponent(&root.alpha, c_max, degree, products)
178 .ok_or_else(|| Error::at(file, position, underflow_message(degree)))?;
179 Ok(FilterBound {
180 degree,
181 epsilon_in_u: (&root.alpha * &Dyadic::pow2(53)).fraction(),
182 kappa_exact: root.beta.fraction(),
183 products,
184 nodes,
185 root: graph.root,
186 d_min_exponent,
187 d_max_exponent,
188 })
189}
190
191/// An upper bound on the bits of all the exact coefficients of `graph`
192/// together, found without computing them, saturated at `u64::MAX`.
193///
194/// Every coefficient of a node is `P(u)` for a polynomial `P` with
195/// nonnegative integer coefficients, so it is an integer times `2^(-53 d)`,
196/// with `d` the degree of `P`, of magnitude at most `P(1)`: its mantissa
197/// has at most `53 d + log2 P(1) + 1` bits. Following the rules of
198/// [`filter_bound`], that is at most 55 bits for a leaf, the operand's
199/// for a negation, 55 more than the larger operand's for a sum or
200/// difference, and 55 more than the two operands' together for a product.
201/// The result is the sum over the nodes.
202fn coefficient_bits(graph: &DiffGraph) -> u64 {
203 let mut bits: Vec<u64> = Vec::with_capacity(graph.nodes.len());
204 for node in &graph.nodes {
205 bits.push(match *node {
206 DiffNode::Leaf { .. } => 55,
207 DiffNode::Neg(x) => bits[x],
208 DiffNode::Add(x, y) | DiffNode::Sub(x, y) => bits[x].max(bits[y]).saturating_add(55),
209 DiffNode::Mul(x, y) => bits[x].saturating_add(bits[y]).saturating_add(55),
210 });
211 }
212 bits.iter().fold(0u64, |total, &b| total.saturating_add(b))
213}
214
215/// Checks, before any coefficient is computed, the two limits that do not
216/// depend on the coefficients: the degree of every node and the size of
217/// the exact coefficients.
218fn check_budget(graph: &DiffGraph, file: &str, position: Position) -> Result<(), Error> {
219 if let Some(°ree) = graph.degrees.iter().filter(|&&g| g > MAX_DEGREE).max() {
220 return Err(Error::at(file, position, overflow_message(degree)));
221 }
222 if coefficient_bits(graph) > MAX_COEFFICIENT_BITS {
223 return Err(Error::at(
224 file,
225 position,
226 format!(
227 "the expression is too large for the error bound of the filter: its exact \
228 coefficients would take more than 2^{} bits in all; a long sum written as \
229 one chain is the usual cause, and the same terms summed in a balanced \
230 tree with `let` need far fewer",
231 MAX_COEFFICIENT_BITS.ilog2()
232 ),
233 ));
234 }
235 Ok(())
236}
237
238fn underflow_message(degree: u32) -> String {
239 format!(
240 "the degree {degree} is too high for the floating-point filter: \
241 no scale of the inputs below 1 keeps its error bound clear of underflow"
242 )
243}
244
245fn overflow_message(degree: u32) -> String {
246 format!(
247 "the degree {degree} is too high for the floating-point filter: \
248 no scale of the inputs above 1 keeps every computed value below 2^{OVERFLOW_EXPONENT}"
249 )
250}
251
252impl FilterBound {
253 /// Whether `n u <= epsilon < n u (1 + 2^-40)`, exactly.
254 ///
255 /// The exact `epsilon` is slightly above an integer multiple of `u`,
256 /// because of the terms in `u^2` and beyond.
257 pub fn epsilon_is_u_times(&self, n: u64) -> bool {
258 let target = &Dyadic::integer(n) * &Dyadic::pow2(-53);
259 close_above(&self.nodes[self.root].alpha, &target)
260 }
261
262 /// Whether `n <= kappa < n (1 + 2^-40)`, exactly.
263 pub fn kappa_is(&self, n: u64) -> bool {
264 close_above(&self.nodes[self.root].beta, &Dyadic::integer(n))
265 }
266
267 /// The constants of the filter.
268 ///
269 /// `epsilon` and `kappa` are the exact values times `1 + 2^-30`, rounded
270 /// up; the inflation covers the rounding of the evaluation of the bound
271 /// itself and an allowance for products that underflow. `d_min` is the
272 /// smallest power of two below 1 for which that allowance covers
273 /// underflow and the bound stays a normal number, and `d_max` the
274 /// largest above 1 for which no computed node exceeds `2^1022`; both
275 /// exist, or [`filter_bound`] would have returned an error.
276 pub fn constants(&self) -> FilterConstants {
277 let inflate = &Dyadic::integer(1) + &Dyadic::pow2(-30);
278 let root = &self.nodes[self.root];
279 FilterConstants {
280 degree: self.degree,
281 epsilon: (&root.alpha * &inflate).round_up(),
282 kappa: (&root.beta * &inflate).round_up(),
283 d_min: Dyadic::pow2(self.d_min_exponent).round_up(),
284 d_max: Dyadic::pow2(self.d_max_exponent).round_up(),
285 }
286 }
287}
288
289/// The exponent of `d_max`: the largest `j >= 1` such that
290/// `c_x * 2^(j g_x) <= 2^1022` at every node `x`.
291///
292/// For `D <= d_max` every computed node then has magnitude at most
293/// `c_x * D^(g_x) <= 2^1022` (for `D < 1`, `D^(g_x) <= 1 < d_max^(g_x)`),
294/// a quarter of the overflow threshold `2^1024`, so no intermediate value
295/// and not the result overflows. An overflow in the evaluation of the bound
296/// itself gives an infinite bound, or a NaN one when an infinite partial
297/// product is then multiplied by an uncertainty of zero; either only makes
298/// the filter abstain, since it decides on `|value| > bound`, false for both.
299///
300/// The nodes are given as their `c` and their degree `g`. Returns the degree
301/// of a node that overflows for every `j >= 1` if there is no such `j`.
302fn d_max_exponent<'a>(nodes: impl Iterator<Item = (&'a Dyadic, u32)>) -> Result<i64, u32> {
303 // `c_x * 2^(j g) <= 2^1022` iff `j g <= 1022 - s` with `s` the
304 // smallest integer such that `c_x <= 2^s`.
305 let (j, degree) = nodes
306 .map(|(c, degree)| {
307 let j = (OVERFLOW_EXPONENT - c.ceil_log2()).div_euclid(i64::from(degree));
308 (j, degree)
309 })
310 .min()
311 .expect("a graph has a root");
312 if j >= 1 { Ok(j) } else { Err(degree) }
313}
314
315/// The exponent of `d_min`: the smallest `j <= -1` such that `P = 2^j`
316/// satisfies
317///
318/// 1. `epsilon * P^k * 2^-32 >= N * k * c_max * 2^-1074`, with `N` the
319/// number of products and `c_max` the largest `c` of any node, and
320/// 2. `epsilon * P^k >= 2^-1021`.
321///
322/// Both then hold for every larger `j`.
323///
324/// Why this is sound, for `D >= d_min` and with `B` the rounding-error
325/// bound `(epsilon * D + kappa * E) * D^(k - 1)`:
326///
327/// - The node rules assume that every rounding error is at most `u` times
328/// the computed magnitude. A sum or difference whose result is
329/// subnormal is exact, but a product that underflows can err by up to
330/// `2^-1075` in absolute terms; allow `2^-1074` per product.
331/// - An absolute error at a product `x` reaches the root multiplied by
332/// the derivative of the computation with respect to `x`: a sum over
333/// the paths from `x` to the root of the products of the other factors
334/// met on the way, each with its `(1 + u)` rounding. Every factor is
335/// bounded by its `c * D^g`, so the derivative is at most
336/// `p'(c_x) * D^(k - g_x)`, where `p(t)` is `c_root` computed by the
337/// rules with `c_x` replaced by `t`. That polynomial has nonnegative
338/// coefficients and degree at most `k / g_x`, since degrees add along
339/// products, so `c_x p'(c_x) <= (k / g_x) p(c_x)`; with `c_x >= 1` and
340/// `g_x >= 2` the derivative is at most `k * c_max * D^(k - g_x)`.
341/// Terms of second order in these errors are products of two such
342/// errors, each at most `2^-1074` times factors bounded as above, so they
343/// are smaller than the first-order terms by a factor of about `2^-1000`
344/// and are absorbed by the slack of `2^-32` of the bound below.
345/// - The total underflow error is therefore
346/// `U <= N * k * c_max * 2^-1074 * max(1, D^k)`. For `d_min <= D <= 1`,
347/// condition 1 gives `U <= 2^-32 * epsilon * d_min^k
348/// <= 2^-32 * epsilon * D^k`; for `D > 1` it gives the same, because
349/// condition 1 holds with `d_min^k < 1`. So `U <= 2^-32 * B`, and the
350/// value of the form errs by at most `(1 + 2^-32) * B`.
351/// - The emitted constants are at least `(1 + 2^-30)` times the exact
352/// ones, so the exact value of the emitted bound is at least
353/// `(1 + 2^-30) * B`. Its `f64` evaluation, `d = delta + e`, then
354/// `(EPSILON * d + KAPPA * e) * d * ... * d` with `k - 1` products by
355/// `d`, takes `k + 3` operations, each of which can round down by a
356/// factor `1 - u`. The rounding of `d` enters the bound once for each of
357/// the `k` times `d` is used, so the computed bound is at least
358/// `(1 - u)^(2k + 2)` times its exact value: `k + 3` factors for the
359/// operations and `k - 1` more for the uses of `d` beyond the first
360/// (`e` is an upper bound on `E`, rounded up by the caller, so it adds
361/// none; a caller that rounded it to nearest instead would add one factor
362/// for each use of `e` and of `d`, `3k + 3` in all, within `2k + 8` only
363/// for `k <= 5`). That holds while no partial result is subnormal:
364/// condition 2 keeps every partial result built from `EPSILON * d` at least
365/// `epsilon * min(1, D)^k >= 2^-1021`, and the term `KAPPA * e`, which
366/// may underflow, loses at most `2^-1075`, a fraction `2^-54` of the sum
367/// it joins. The constants were derived for up to `2k + 8` such factors,
368/// and `(1 + 2^-30)(1 - u)^(2k + 8) >= 1 + 2^-31` for `k <= 1022` (higher
369/// degrees are rejected), so the computed bound is at least
370/// `(1 + 2^-31) * B`, which exceeds the error `(1 + 2^-32) * B`.
371///
372/// Returns `None` if no `j <= -1` satisfies both.
373fn d_min_exponent(epsilon: &Dyadic, c_max: &Dyadic, degree: u32, products: u32) -> Option<i64> {
374 let factor = Dyadic::integer(u64::from(products) * u64::from(degree));
375 let underflow = &(&factor * c_max) * &Dyadic::pow2(-1074 + 32);
376 let normal = Dyadic::pow2(-1021);
377 let k = i64::from(degree);
378 let covers = |j: i64| {
379 let scaled = epsilon * &Dyadic::pow2(j * k);
380 scaled >= underflow && scaled >= normal
381 };
382 if !covers(-1) {
383 return None;
384 }
385 let mut j = -1;
386 while covers(j - 1) {
387 j -= 1;
388 }
389 Some(j)
390}
391
392/// Whether `target <= value < target * (1 + 2^-40)`.
393fn close_above(value: &Dyadic, target: &Dyadic) -> bool {
394 let limit = target * &(&Dyadic::integer(1) + &Dyadic::pow2(-40));
395 target <= value && *value < limit
396}
397
398#[cfg(test)]
399mod tests;
400
401mod axis;
402
403pub use axis::{AxisBound, AxisConstants, FilterForm, filter_form};