fuzzy_control/
defuzzification.rs1use crate::*;
4
5pub trait Defuzzifier<D: Float, M: Float> {
7 fn defuzzify(
9 &self,
10 output_variable: &LinguisticVariable<D, M>,
11 activations: &HashMap<String, MembershipDegree<M>>,
12 ) -> Result<D, MembershipError>;
13}
14
15pub struct Centroid {
17 samples: usize,
18}
19
20impl Centroid {
21 pub fn new(samples: usize) -> Result<Self, MembershipError> {
23 if samples == 0 {
24 return Err(MembershipError::InvalidConfiguration {
25 message: "Sample count must be greater than 0".to_string(),
26 });
27 }
28 Ok(Centroid { samples })
29 }
30}
31
32impl<D: Float, M: Float> Defuzzifier<D, M> for Centroid {
33 fn defuzzify(
34 &self,
35 output_variable: &LinguisticVariable<D, M>,
36 activations: &HashMap<String, MembershipDegree<M>>,
37 ) -> Result<D, MembershipError> {
38 let (min, max) = output_variable.range();
39 let step = (max - min) / D::from(self.samples).unwrap();
40
41 let mut numerator = D::zero();
42 let mut denominator = D::zero();
43
44 for i in 0..=self.samples {
45 let x = min + step * D::from(i).unwrap();
46 let mut max_activation = M::zero();
47
48 for set in &output_variable.sets {
49 if let Some(activation) = activations.get(set.name()) {
50 let membership = set.evaluate(x);
51 let clipped = membership.get().min(activation.get());
52 max_activation = max_activation.max(clipped);
53 }
54 }
55
56 let activation_d = D::from(max_activation).unwrap_or(D::zero());
57 numerator = numerator + x * activation_d;
58 denominator = denominator + activation_d;
59 }
60
61 if denominator == D::zero() {
62 Ok((min + max) / D::from(2.0).unwrap())
63 } else {
64 Ok(numerator / denominator)
65 }
66 }
67}
68
69pub struct Bisector {
71 samples: usize,
72}
73
74impl Bisector {
75 pub fn new(samples: usize) -> Result<Self, MembershipError> {
77 if samples == 0 {
78 return Err(MembershipError::InvalidConfiguration {
79 message: "Sample count must be greater than 0".to_string(),
80 });
81 }
82 Ok(Bisector { samples })
83 }
84}
85
86impl<D: Float, M: Float> Defuzzifier<D, M> for Bisector {
87 fn defuzzify(
88 &self,
89 output_variable: &LinguisticVariable<D, M>,
90 activations: &HashMap<String, MembershipDegree<M>>,
91 ) -> Result<D, MembershipError> {
92 let (min, max) = output_variable.range();
93 let step = (max - min) / D::from(self.samples).unwrap();
94
95 let mut total_area = D::zero();
96 let mut areas = Vec::new();
97
98 for i in 0..=self.samples {
99 let x = min + step * D::from(i).unwrap();
100 let mut max_activation = M::zero();
101
102 for set in &output_variable.sets {
103 if let Some(activation) = activations.get(set.name()) {
104 let membership = set.evaluate(x);
105 let clipped = membership.get().min(activation.get());
106 max_activation = max_activation.max(clipped);
107 }
108 }
109
110 let activation_d = D::from(max_activation).unwrap_or(D::zero());
111 areas.push((x, activation_d));
112 total_area = total_area + activation_d;
113 }
114
115 if total_area == D::zero() {
116 return Ok((min + max) / D::from(2.0).unwrap());
117 }
118
119 let half_area = total_area / D::from(2.0).unwrap();
120 let mut accumulated_area = D::zero();
121
122 for (x, area) in areas {
123 accumulated_area = accumulated_area + area;
124 if accumulated_area >= half_area {
125 return Ok(x);
126 }
127 }
128
129 Ok(max)
130 }
131}
132
133pub struct MeanOfMaximum {
135 samples: usize,
136}
137
138impl MeanOfMaximum {
139 pub fn new(samples: usize) -> Result<Self, MembershipError> {
141 if samples == 0 {
142 return Err(MembershipError::InvalidConfiguration {
143 message: "Sample count must be greater than 0".to_string(),
144 });
145 }
146 Ok(MeanOfMaximum { samples })
147 }
148}
149
150impl<D: Float, M: Float> Defuzzifier<D, M> for MeanOfMaximum {
151 fn defuzzify(
152 &self,
153 output_variable: &LinguisticVariable<D, M>,
154 activations: &HashMap<String, MembershipDegree<M>>,
155 ) -> Result<D, MembershipError> {
156 let (min, max) = output_variable.range();
157 let step = (max - min) / D::from(self.samples).unwrap();
158
159 let mut max_membership = M::zero();
160 let mut max_points = Vec::new();
161
162 for i in 0..=self.samples {
163 let x = min + step * D::from(i).unwrap();
164 let mut aggregated = M::zero();
165
166 for set in &output_variable.sets {
167 if let Some(activation) = activations.get(set.name()) {
168 let membership = set.evaluate(x);
169 let clipped = membership.get().min(activation.get());
170 aggregated = aggregated.max(clipped);
171 }
172 }
173
174 if aggregated > max_membership {
175 max_membership = aggregated;
176 max_points.clear();
177 max_points.push(x);
178 } else if aggregated == max_membership && aggregated > M::zero() {
179 max_points.push(x);
180 }
181 }
182
183 if max_points.is_empty() {
184 Ok((min + max) / D::from(2.0).unwrap())
185 } else {
186 let sum: D = max_points.iter().fold(D::zero(), |acc, &x| acc + x);
187 Ok(sum / D::from(max_points.len()).unwrap())
188 }
189 }
190}
191
192pub struct SmallestOfMaximum {
194 samples: usize,
195}
196
197impl SmallestOfMaximum {
198 pub fn new(samples: usize) -> Result<Self, MembershipError> {
200 if samples == 0 {
201 return Err(MembershipError::InvalidConfiguration {
202 message: "Sample count must be greater than 0".to_string(),
203 });
204 }
205 Ok(SmallestOfMaximum { samples })
206 }
207}
208
209impl<D: Float, M: Float> Defuzzifier<D, M> for SmallestOfMaximum {
210 fn defuzzify(
211 &self,
212 output_variable: &LinguisticVariable<D, M>,
213 activations: &HashMap<String, MembershipDegree<M>>,
214 ) -> Result<D, MembershipError> {
215 let (min, max) = output_variable.range();
216 let step = (max - min) / D::from(self.samples).unwrap();
217
218 let mut max_membership = M::zero();
219 let mut smallest_max = max;
220
221 for i in 0..=self.samples {
222 let x = min + step * D::from(i).unwrap();
223 let mut aggregated = M::zero();
224
225 for set in &output_variable.sets {
226 if let Some(activation) = activations.get(set.name()) {
227 let membership = set.evaluate(x);
228 let clipped = membership.get().min(activation.get());
229 aggregated = aggregated.max(clipped);
230 }
231 }
232
233 if aggregated > max_membership {
234 max_membership = aggregated;
235 smallest_max = x;
236 }
237 }
238
239 Ok(smallest_max)
240 }
241}
242
243pub struct LargestOfMaximum {
245 samples: usize,
246}
247
248impl LargestOfMaximum {
249 pub fn new(samples: usize) -> Result<Self, MembershipError> {
251 if samples == 0 {
252 return Err(MembershipError::InvalidConfiguration {
253 message: "Sample count must be greater than 0".to_string(),
254 });
255 }
256 Ok(LargestOfMaximum { samples })
257 }
258}
259
260impl<D: Float, M: Float> Defuzzifier<D, M> for LargestOfMaximum {
261 fn defuzzify(
262 &self,
263 output_variable: &LinguisticVariable<D, M>,
264 activations: &HashMap<String, MembershipDegree<M>>,
265 ) -> Result<D, MembershipError> {
266 let (min, max) = output_variable.range();
267 let step = (max - min) / D::from(self.samples).unwrap();
268
269 let mut max_membership = M::zero();
270 let mut largest_max = min;
271
272 for i in 0..=self.samples {
273 let x = min + step * D::from(i).unwrap();
274 let mut aggregated = M::zero();
275
276 for set in &output_variable.sets {
277 if let Some(activation) = activations.get(set.name()) {
278 let membership = set.evaluate(x);
279 let clipped = membership.get().min(activation.get());
280 aggregated = aggregated.max(clipped);
281 }
282 }
283
284 if aggregated >= max_membership {
285 max_membership = aggregated;
286 largest_max = x;
287 }
288 }
289
290 Ok(largest_max)
291 }
292}
293
294pub struct WeightedAverage;
296
297impl<D: Float, M: Float> Defuzzifier<D, M> for WeightedAverage {
298 fn defuzzify(
299 &self,
300 output_variable: &LinguisticVariable<D, M>,
301 activations: &HashMap<String, MembershipDegree<M>>,
302 ) -> Result<D, MembershipError> {
303 let mut weighted_sum = D::zero();
304 let mut weight_sum = M::zero();
305
306 for set in &output_variable.sets {
307 if let Some(activation) = activations.get(set.name()) {
308 let (min, max) = output_variable.range();
309 let samples = 100;
310 let step = (max - min) / D::from(samples).unwrap();
311
312 let mut peak_value = min;
313 let mut peak_membership = M::zero();
314
315 for i in 0..=samples {
316 let x = min + step * D::from(i).unwrap();
317 let membership = set.evaluate(x).get();
318 if membership > peak_membership {
319 peak_membership = membership;
320 peak_value = x;
321 }
322 }
323
324 weighted_sum = weighted_sum + peak_value * D::from(activation.get()).unwrap();
325 weight_sum = weight_sum + activation.get();
326 }
327 }
328
329 if weight_sum == M::zero() {
330 let (min, max) = output_variable.range();
331 Ok((min + max) / D::from(2.0).unwrap())
332 } else {
333 Ok(weighted_sum / D::from(weight_sum).unwrap())
334 }
335 }
336}