Skip to main content

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/// Strict gate threshold fraction
150///
151/// When pool is near capacity, only accept candidates with error
152/// below this fraction of accept_error.
153///
154/// Value: 0.5
155pub const STRICT_GATE_FACTOR: f64 = 0.5;
156
157/// Pool capacity fraction for triggering strict gate
158///
159/// When pool is above this fraction of capacity, strict gating is applied.
160///
161/// Value: 4/5 = 0.8
162pub const STRICT_GATE_CAPACITY_FRACTION: f64 = 0.8;
163
164// =============================================================================
165// Adaptive Search Radius Constants
166// =============================================================================
167
168/// Base search radius factor (as fraction of derivative)
169///
170/// The minimum search radius is this fraction of the LHS derivative magnitude.
171/// This allows for approximately this much error in x before filtering.
172///
173/// Value: 0.5
174pub const BASE_SEARCH_RADIUS_FACTOR: f64 = 0.5;
175
176/// Maximum search radius factor (as fraction of derivative)
177///
178/// The search radius is capped at this multiple of the derivative to prevent
179/// searching too broadly when errors are large.
180///
181/// Value: 2.0
182pub const MAX_SEARCH_RADIUS_FACTOR: f64 = 2.0;
183
184/// Complexity scaling factor for adaptive radius
185///
186/// Higher complexity expressions get tighter search bounds.
187/// The radius is scaled by: 1.0 / (1.0 + COMPLEXITY_SCALE * normalized_complexity)
188///
189/// Value: 0.5
190pub const ADAPTIVE_COMPLEXITY_SCALE: f64 = 0.5;
191
192/// Pool fullness factor for adaptive radius
193///
194/// When the pool is fuller, we become more selective.
195/// The radius is scaled by: 1.0 - POOL_FULLNESS_SCALE * (pool_size / capacity)
196///
197/// Value: 0.3
198pub const ADAPTIVE_POOL_FULLNESS_SCALE: f64 = 0.3;
199
200/// Exact match bonus factor for adaptive radius
201///
202/// When exact matches have been found, we become much more selective.
203/// The radius is multiplied by this factor.
204///
205/// Value: 0.1
206pub const ADAPTIVE_EXACT_MATCH_FACTOR: f64 = 0.1;
207
208/// Complexity tier boundaries for tiered search
209///
210/// Tier 0: 0-15 (simplest expressions)
211/// Tier 1: 16-25 (moderate complexity)
212/// Tier 2: 26-35 (higher complexity)
213/// Tier 3: 36+ (highest complexity)
214pub const TIER_0_MAX: u32 = 15;
215pub const TIER_1_MAX: u32 = 25;
216pub const TIER_2_MAX: u32 = 35;
217
218/// Early exit threshold for tiered search
219///
220/// If we find matches with error below this threshold in a lower tier,
221/// we may skip searching higher tiers.
222///
223/// Value: 1e-8
224pub const TIER_EARLY_EXIT_ERROR: f64 = 1e-8;
225
226// =============================================================================
227// Quantization Thresholds
228// =============================================================================
229
230/// Scale factor for value quantization in deduplication
231///
232/// Values are multiplied by this factor before rounding to integer
233/// for deduplication purposes.
234///
235/// Value: 1e8 (preserves ~8 significant digits)
236pub const QUANTIZE_SCALE: f64 = 1e8;
237
238// =============================================================================
239// Helper Functions
240// =============================================================================
241
242/// Check if an error is within exact match tolerance
243#[inline]
244pub fn is_exact_match(error: f64) -> bool {
245    error.abs() < EXACT_MATCH_TOLERANCE
246}
247
248/// Check if a derivative is degenerate (too small for Newton-Raphson)
249#[inline]
250pub fn is_degenerate_derivative(derivative: f64) -> bool {
251    derivative.abs() < DEGENERATE_DERIVATIVE
252}
253
254/// Check if a value is effectively zero for pruning purposes
255#[inline]
256pub fn is_effectively_zero(value: f64) -> bool {
257    value.abs() < ZERO_VALUE_PRUNE
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    #[test]
265    fn test_is_exact_match() {
266        assert!(is_exact_match(0.0));
267        assert!(is_exact_match(1e-15));
268        assert!(is_exact_match(-1e-15));
269        assert!(!is_exact_match(1e-13));
270        assert!(!is_exact_match(0.001));
271    }
272
273    #[test]
274    fn test_is_degenerate_derivative() {
275        assert!(is_degenerate_derivative(0.0));
276        assert!(is_degenerate_derivative(1e-101));
277        assert!(is_degenerate_derivative(-1e-101));
278        assert!(!is_degenerate_derivative(1e-99));
279        assert!(!is_degenerate_derivative(0.001));
280    }
281
282    #[test]
283    fn test_is_effectively_zero() {
284        assert!(is_effectively_zero(0.0));
285        assert!(is_effectively_zero(1e-5));
286        assert!(is_effectively_zero(-1e-5));
287        assert!(!is_effectively_zero(1e-3));
288        assert!(!is_effectively_zero(0.1));
289    }
290
291    #[test]
292    #[allow(clippy::assertions_on_constants)]
293    fn test_constants_are_sane() {
294        // Exact match tolerance should be small but not at machine epsilon
295        assert!(EXACT_MATCH_TOLERANCE > 1e-16);
296        assert!(EXACT_MATCH_TOLERANCE < 1e-10);
297
298        // Newton tolerance should be very tight
299        assert!(NEWTON_TOLERANCE < EXACT_MATCH_TOLERANCE);
300
301        // Pruning thresholds should be larger than exact tolerance
302        assert!(ZERO_VALUE_PRUNE > EXACT_MATCH_TOLERANCE);
303    }
304}