qualia_core_db/solvers/fuzzy_query/
membership.rs1#[inline]
8fn clamp01(x: f64) -> f64 {
9 x.clamp(0.0, 1.0)
10}
11
12pub fn ramp_up(x: f64, a: f64, b: f64) -> f64 {
15 if b <= a {
16 return if x >= a { 1.0 } else { 0.0 };
17 }
18 clamp01((x - a) / (b - a))
19}
20
21pub fn ramp_down(x: f64, a: f64, b: f64) -> f64 {
23 if b <= a {
24 return if x <= a { 1.0 } else { 0.0 };
25 }
26 clamp01((b - x) / (b - a))
27}
28
29pub fn triangular(x: f64, a: f64, m: f64, b: f64) -> f64 {
32 if x <= a || x >= b {
33 0.0
34 } else if (x - m).abs() < f64::EPSILON {
35 1.0
36 } else if x < m {
37 clamp01((x - a) / (m - a))
38 } else {
39 clamp01((b - x) / (b - m))
40 }
41}
42
43pub fn trapezoidal(x: f64, a: f64, b: f64, c: f64, d: f64) -> f64 {
46 if x <= a || x >= d {
47 0.0
48 } else if x < b {
49 clamp01((x - a) / (b - a))
50 } else if x <= c {
51 1.0
52 } else {
53 clamp01((d - x) / (d - c))
54 }
55}
56
57pub fn approximately(x: f64, target: f64, tol: f64) -> f64 {
60 if tol <= 0.0 {
61 return if (x - target).abs() < f64::EPSILON {
62 1.0
63 } else {
64 0.0
65 };
66 }
67 triangular(x, target - tol, target, target + tol)
68}
69
70pub fn much_greater_than(x: f64, reference: f64, spread: f64) -> f64 {
73 ramp_up(x, reference, reference + spread.max(0.0))
74}
75
76pub fn much_less_than(x: f64, reference: f64, spread: f64) -> f64 {
79 ramp_down(x, reference - spread.max(0.0), reference)
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 const EPS: f64 = 1e-9;
86
87 #[test]
88 fn ramps_endpoints_and_interior() {
89 assert!((ramp_up(0.0, 1.0, 3.0)).abs() < EPS);
90 assert!((ramp_up(2.0, 1.0, 3.0) - 0.5).abs() < EPS);
91 assert!((ramp_up(9.0, 1.0, 3.0) - 1.0).abs() < EPS);
92 assert!((ramp_down(0.0, 1.0, 3.0) - 1.0).abs() < EPS);
93 assert!((ramp_down(2.0, 1.0, 3.0) - 0.5).abs() < EPS);
94 }
95
96 #[test]
97 fn triangle_peaks_at_m() {
98 assert!((triangular(30.0, 20.0, 30.0, 40.0) - 1.0).abs() < EPS);
99 assert!((triangular(25.0, 20.0, 30.0, 40.0) - 0.5).abs() < EPS);
100 assert!(triangular(45.0, 20.0, 30.0, 40.0).abs() < EPS);
101 }
102
103 #[test]
104 fn trapezoid_plateau() {
105 assert!((trapezoidal(5.0, 1.0, 4.0, 6.0, 9.0) - 1.0).abs() < EPS); assert!((trapezoidal(2.5, 1.0, 4.0, 6.0, 9.0) - 0.5).abs() < EPS); assert!((trapezoidal(7.5, 1.0, 4.0, 6.0, 9.0) - 0.5).abs() < EPS); }
109
110 #[test]
111 fn approximately_is_symmetric() {
112 assert!((approximately(30.0, 30.0, 5.0) - 1.0).abs() < EPS);
113 assert!((approximately(32.5, 30.0, 5.0) - 0.5).abs() < EPS);
114 assert!((approximately(27.5, 30.0, 5.0) - 0.5).abs() < EPS);
115 assert!(approximately(40.0, 30.0, 5.0).abs() < EPS);
116 }
117
118 #[test]
119 fn comparators() {
120 assert!((much_greater_than(100.0, 50.0, 50.0) - 1.0).abs() < EPS);
121 assert!((much_greater_than(50.0, 50.0, 50.0)).abs() < EPS);
122 assert!((much_less_than(0.0, 50.0, 50.0) - 1.0).abs() < EPS);
123 }
124}