ries_rs/thresholds.rs
1//! Named threshold constants for numerical comparisons
2//!
3//! This module centralizes all magic numbers used for numerical comparisons
4//! throughout the codebase. Using named constants improves code readability
5//! and makes it easier to adjust thresholds globally.
6//!
7//! # Categories
8//!
9//! - **Match quality**: Thresholds for determining match accuracy
10//! - **Newton-Raphson**: Convergence and stability thresholds
11//! - **Pruning**: Thresholds for expression pruning decisions
12//! - **Numerical safety**: Thresholds for avoiding numerical issues
13//!
14//! Note: Some constants are defined for completeness and future use.
15#![allow(dead_code)]
16
17// =============================================================================
18// Match Quality Thresholds
19// =============================================================================
20
21/// Tolerance for considering a match "exact"
22///
23/// Matches with error below this threshold are considered mathematically exact
24/// (within floating-point precision limits).
25///
26/// Value: 1e-14 (roughly 100x machine epsilon for f64)
27pub const EXACT_MATCH_TOLERANCE: f64 = 1e-14;
28
29/// Maximum relative error for coarse filtering
30///
31/// Used in the initial candidate filtering before Newton-Raphson refinement.
32/// Candidates with estimated error above this are skipped.
33///
34/// Value: 1.0 (100% relative error)
35pub const COARSE_ERROR_MAX: f64 = 1.0;
36
37/// Minimum search radius as fraction of derivative
38///
39/// When searching for RHS matches, the minimum radius is this fraction
40/// of the LHS derivative magnitude.
41///
42/// Value: 0.5
43pub const MIN_SEARCH_RADIUS_FACTOR: f64 = 0.5;
44
45// =============================================================================
46// Newton-Raphson Thresholds
47// =============================================================================
48
49/// Convergence tolerance for Newton-Raphson iteration
50///
51/// Iteration stops when |delta| < tolerance * (1 + |x|).
52///
53/// Value: 1e-15 (approximately machine epsilon)
54pub const NEWTON_TOLERANCE: f64 = 1e-15;
55
56/// Threshold below which derivative is considered degenerate
57///
58/// If |derivative| < this threshold, the Newton-Raphson method is likely
59/// to fail or produce unreliable results.
60///
61/// Value: 1e-100
62pub const DEGENERATE_DERIVATIVE: f64 = 1e-100;
63
64/// Maximum acceptable Newton-Raphson result for refinement success
65///
66/// After Newton-Raphson refinement, if |f(x) - rhs| > this threshold,
67/// the result is considered not converged.
68///
69/// Value: 1e-10
70pub const NEWTON_FINAL_TOLERANCE: f64 = 1e-10;
71
72/// Threshold for detecting degenerate expressions during test evaluation
73///
74/// When testing if an expression is degenerate (derivative ~0 everywhere),
75/// this threshold is used to check if derivative is still near zero.
76///
77/// Value: 1e-10
78pub const DEGENERATE_TEST_THRESHOLD: f64 = 1e-10;
79
80/// Tolerance for checking degenerate matches
81///
82/// If an expression is degenerate, we still check if its value matches
83/// an RHS expression within this tolerance to catch true repeated roots.
84///
85/// Value: 0.01
86pub const DEGENERATE_RANGE_TOLERANCE: f64 = 0.01;
87
88/// Maximum x value magnitude before Newton-Raphson is considered diverged
89///
90/// Value: 1e100
91pub const NEWTON_DIVERGENCE_THRESHOLD: f64 = 1e100;
92
93// =============================================================================
94// Pruning Thresholds
95// =============================================================================
96
97/// Threshold for pruning zero-value LHS expressions
98///
99/// LHS expressions with |value| < this threshold are pruned to avoid
100/// flooding matches with trivial identities.
101///
102/// Value: 1e-4
103pub const ZERO_VALUE_PRUNE: f64 = 1e-4;
104
105/// Threshold for pruning degenerate expressions (derivative near zero)
106///
107/// Value: 1e-10
108pub const DEGENERATE_DERIVATIVE_PRUNE: f64 = 1e-10;
109
110// =============================================================================
111// Numerical Safety Thresholds
112// =============================================================================
113
114/// Minimum absolute value before division is considered division by zero
115///
116/// Used in evaluation to detect potential division by zero.
117///
118/// Value: f64::MIN_POSITIVE
119pub const DIVISION_BY_ZERO_THRESHOLD: f64 = f64::MIN_POSITIVE;
120
121/// Maximum absolute value before result is considered overflow
122///
123/// Used in evaluation to detect potential overflow in generated expressions.
124///
125/// Value: 1e12
126pub const VALUE_OVERFLOW_THRESHOLD: f64 = 1e12;
127
128/// Maximum absolute value before skipping expression entirely
129///
130/// Used in generation to skip expressions with extreme values.
131///
132/// Value: 1e10
133pub const EXTREME_VALUE_THRESHOLD: f64 = 1e10;
134
135// =============================================================================
136// Pool Thresholds
137// =============================================================================
138
139/// Factor for tightening best error when exact match is found
140///
141/// Value: 0.999
142pub const BEST_ERROR_TIGHTEN_FACTOR: f64 = 0.999;
143
144/// Factor for tightening accept error for diversity
145///
146/// Value: 0.9999
147pub const ACCEPT_ERROR_TIGHTEN_FACTOR: f64 = 0.9999;
148
149/// Pool capacity fraction below which accept_error may relax after eviction
150///
151/// Value: 0.5
152pub const ACCEPT_ERROR_RELAX_CAPACITY_FRACTION: f64 = 0.5;
153
154/// Strict gate threshold fraction
155///
156/// When pool is near capacity, only accept candidates with error
157/// below this fraction of accept_error.
158///
159/// Value: 0.5
160pub const STRICT_GATE_FACTOR: f64 = 0.5;
161
162/// Pool capacity fraction for triggering strict gate
163///
164/// When pool is above this fraction of capacity, strict gating is applied.
165///
166/// Value: 4/5 = 0.8
167pub const STRICT_GATE_CAPACITY_FRACTION: f64 = 0.8;
168
169// =============================================================================
170// Adaptive Search Radius Constants
171// =============================================================================
172
173/// Base search radius factor (as fraction of derivative)
174///
175/// The minimum search radius is this fraction of the LHS derivative magnitude.
176/// This allows for approximately this much error in x before filtering.
177///
178/// Value: 0.5
179pub const BASE_SEARCH_RADIUS_FACTOR: f64 = 0.5;
180
181/// Maximum search radius factor (as fraction of derivative)
182///
183/// The search radius is capped at this multiple of the derivative to prevent
184/// searching too broadly when errors are large.
185///
186/// Value: 2.0
187pub const MAX_SEARCH_RADIUS_FACTOR: f64 = 2.0;
188
189/// Complexity scaling factor for adaptive radius
190///
191/// Higher complexity expressions get tighter search bounds.
192/// The radius is scaled by: 1.0 / (1.0 + COMPLEXITY_SCALE * normalized_complexity)
193///
194/// Value: 0.5
195pub const ADAPTIVE_COMPLEXITY_SCALE: f64 = 0.5;
196
197/// Pool fullness factor for adaptive radius
198///
199/// When the pool is fuller, we become more selective.
200/// The radius is scaled by: 1.0 - POOL_FULLNESS_SCALE * (pool_size / capacity)
201///
202/// Value: 0.3
203pub const ADAPTIVE_POOL_FULLNESS_SCALE: f64 = 0.3;
204
205/// Exact match bonus factor for adaptive radius
206///
207/// When exact matches have been found, we become much more selective.
208/// The radius is multiplied by this factor.
209///
210/// Value: 0.1
211pub const ADAPTIVE_EXACT_MATCH_FACTOR: f64 = 0.1;
212
213/// Complexity tier boundaries for tiered search
214///
215/// Tier 0: 0-15 (simplest expressions)
216/// Tier 1: 16-25 (moderate complexity)
217/// Tier 2: 26-35 (higher complexity)
218/// Tier 3: 36+ (highest complexity)
219pub const TIER_0_MAX: u32 = 15;
220pub const TIER_1_MAX: u32 = 25;
221pub const TIER_2_MAX: u32 = 35;
222
223/// Early exit threshold for tiered search
224///
225/// If we find matches with error below this threshold in a lower tier,
226/// we may skip searching higher tiers.
227///
228/// Value: 1e-8
229pub const TIER_EARLY_EXIT_ERROR: f64 = 1e-8;
230
231// =============================================================================
232// Quantization Thresholds
233// =============================================================================
234
235/// Scale factor for value quantization in deduplication
236///
237/// Values are multiplied by this factor before rounding to integer
238/// for deduplication purposes.
239///
240/// Value: 1e8 (preserves ~8 significant digits)
241pub const QUANTIZE_SCALE: f64 = 1e8;
242
243// =============================================================================
244// Helper Functions
245// =============================================================================
246
247/// Check if an error is within exact match tolerance
248#[inline]
249pub fn is_exact_match(error: f64) -> bool {
250 error.abs() < EXACT_MATCH_TOLERANCE
251}
252
253/// Check if a derivative is degenerate (too small for Newton-Raphson)
254#[inline]
255pub fn is_degenerate_derivative(derivative: f64) -> bool {
256 derivative.abs() < DEGENERATE_DERIVATIVE
257}
258
259/// Check if a value is effectively zero for pruning purposes
260#[inline]
261pub fn is_effectively_zero(value: f64) -> bool {
262 value.abs() < ZERO_VALUE_PRUNE
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn test_is_exact_match() {
271 assert!(is_exact_match(0.0));
272 assert!(is_exact_match(1e-15));
273 assert!(is_exact_match(-1e-15));
274 assert!(!is_exact_match(1e-13));
275 assert!(!is_exact_match(0.001));
276 }
277
278 #[test]
279 fn test_is_degenerate_derivative() {
280 assert!(is_degenerate_derivative(0.0));
281 assert!(is_degenerate_derivative(1e-101));
282 assert!(is_degenerate_derivative(-1e-101));
283 assert!(!is_degenerate_derivative(1e-99));
284 assert!(!is_degenerate_derivative(0.001));
285 }
286
287 #[test]
288 fn test_is_effectively_zero() {
289 assert!(is_effectively_zero(0.0));
290 assert!(is_effectively_zero(1e-5));
291 assert!(is_effectively_zero(-1e-5));
292 assert!(!is_effectively_zero(1e-3));
293 assert!(!is_effectively_zero(0.1));
294 }
295
296 #[test]
297 #[allow(clippy::assertions_on_constants)]
298 fn test_constants_are_sane() {
299 // Exact match tolerance should be small but not at machine epsilon
300 assert!(EXACT_MATCH_TOLERANCE > 1e-16);
301 assert!(EXACT_MATCH_TOLERANCE < 1e-10);
302
303 // Newton tolerance should be very tight
304 assert!(NEWTON_TOLERANCE < EXACT_MATCH_TOLERANCE);
305
306 // Pruning thresholds should be larger than exact tolerance
307 assert!(ZERO_VALUE_PRUNE > EXACT_MATCH_TOLERANCE);
308 }
309}