1use super::functions::*;
5
6pub struct FuzzyInferenceSystem {
8 pub fis_type: FISType,
10 pub n_inputs: usize,
12 pub output_size: usize,
14 pub output_domain: Vec<f64>,
16 pub defuzz_method: DefuzzMethod,
18 pub mamdani_rules: Vec<MamdaniRule>,
20 pub sugeno_rules: Vec<SugenoRule>,
22}
23impl FuzzyInferenceSystem {
24 pub fn mamdani(output_size: usize, output_domain: Vec<f64>) -> Self {
26 FuzzyInferenceSystem {
27 fis_type: FISType::Mamdani,
28 n_inputs: 0,
29 output_size,
30 output_domain,
31 defuzz_method: DefuzzMethod::CentroidOfArea,
32 mamdani_rules: Vec::new(),
33 sugeno_rules: Vec::new(),
34 }
35 }
36 pub fn sugeno(n_inputs: usize) -> Self {
38 FuzzyInferenceSystem {
39 fis_type: FISType::Sugeno,
40 n_inputs,
41 output_size: 0,
42 output_domain: Vec::new(),
43 defuzz_method: DefuzzMethod::CentroidOfArea,
44 mamdani_rules: Vec::new(),
45 sugeno_rules: Vec::new(),
46 }
47 }
48 pub fn with_defuzz(mut self, method: DefuzzMethod) -> Self {
50 self.defuzz_method = method;
51 self
52 }
53 pub fn add_mamdani_rule(&mut self, antecedent_mf: Vec<f64>, consequent: FuzzySet) {
55 self.mamdani_rules.push(MamdaniRule {
56 antecedent_mf,
57 consequent,
58 });
59 }
60 pub fn add_sugeno_rule(
62 &mut self,
63 antecedent_mf: Vec<f64>,
64 output_coeffs: Vec<f64>,
65 output_const: f64,
66 ) {
67 self.sugeno_rules.push(SugenoRule {
68 antecedent_mf,
69 output_coeffs,
70 output_const,
71 });
72 }
73 pub fn infer_mamdani(&self, input_degrees: &[Vec<f64>]) -> f64 {
77 let sys = MamdaniSystem {
78 rules: self.mamdani_rules.clone(),
79 output_size: self.output_size,
80 };
81 let out_fuzzy = sys.infer(input_degrees);
82 defuzzify(&out_fuzzy, &self.output_domain, self.defuzz_method)
83 }
84 pub fn infer_sugeno(&self, inputs: &[f64], input_degrees: &[Vec<f64>]) -> f64 {
86 let sys = SugenoSystem {
87 rules: self.sugeno_rules.clone(),
88 };
89 sys.infer(inputs, input_degrees)
90 }
91 pub fn is_configured(&self) -> bool {
93 match self.fis_type {
94 FISType::Mamdani => !self.mamdani_rules.is_empty(),
95 FISType::Sugeno => !self.sugeno_rules.is_empty(),
96 }
97 }
98}
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum TConorm {
102 Maximum,
103 ProbabilisticSum,
104 BoundedSum,
105 Drastic,
106}
107impl TConorm {
108 pub fn eval(self, a: f64, b: f64) -> f64 {
110 match self {
111 TConorm::Maximum => a.max(b),
112 TConorm::ProbabilisticSum => a + b - a * b,
113 TConorm::BoundedSum => (a + b).min(1.0),
114 TConorm::Drastic => {
115 if a < 1e-9 {
116 b
117 } else if b < 1e-9 {
118 a
119 } else {
120 1.0
121 }
122 }
123 }
124 }
125}
126#[allow(dead_code)]
128#[derive(Debug, Clone)]
129pub struct FuzzyClustering {
130 pub n_samples: usize,
131 pub n_clusters: usize,
132 pub fuzziness: f64,
133 pub membership: Vec<Vec<f64>>,
134}
135#[allow(dead_code)]
136impl FuzzyClustering {
137 pub fn new(n_samples: usize, n_clusters: usize, fuzziness: f64) -> Self {
138 let membership = vec![vec![1.0 / n_clusters as f64; n_samples]; n_clusters];
139 FuzzyClustering {
140 n_samples,
141 n_clusters,
142 fuzziness,
143 membership,
144 }
145 }
146 pub fn get_membership(&self, cluster: usize, sample: usize) -> f64 {
147 self.membership[cluster][sample]
148 }
149 pub fn hard_assignment(&self, sample: usize) -> usize {
150 (0..self.n_clusters)
151 .max_by(|&a, &b| {
152 self.membership[a][sample]
153 .partial_cmp(&self.membership[b][sample])
154 .unwrap_or(std::cmp::Ordering::Equal)
155 })
156 .unwrap_or(0)
157 }
158 pub fn partition_coefficient(&self) -> f64 {
160 let mut sum = 0.0;
161 for c in 0..self.n_clusters {
162 for s in 0..self.n_samples {
163 sum += self.membership[c][s].powi(2);
164 }
165 }
166 sum / self.n_samples as f64
167 }
168 pub fn set_membership(&mut self, cluster: usize, sample: usize, val: f64) {
169 self.membership[cluster][sample] = val.clamp(0.0, 1.0);
170 }
171}
172pub struct FuzzyCMeans {
177 pub c: usize,
179 pub m: f64,
181 pub max_iter: usize,
183 pub tol: f64,
185}
186impl FuzzyCMeans {
187 pub fn new(c: usize) -> Self {
189 FuzzyCMeans {
190 c,
191 m: 2.0,
192 max_iter: 100,
193 tol: 1e-6,
194 }
195 }
196 pub fn with_m(mut self, m: f64) -> Self {
198 self.m = m;
199 self
200 }
201 pub fn with_max_iter(mut self, max_iter: usize) -> Self {
203 self.max_iter = max_iter;
204 self
205 }
206 fn dist(a: &[f64], b: &[f64]) -> f64 {
208 a.iter()
209 .zip(b.iter())
210 .map(|(x, y)| (x - y).powi(2))
211 .sum::<f64>()
212 .sqrt()
213 }
214 pub fn fit(&self, data: &[Vec<f64>]) -> (Vec<Vec<f64>>, Vec<Vec<f64>>) {
220 let n = data.len();
221 let dim = if n > 0 { data[0].len() } else { 1 };
222 if n == 0 || self.c == 0 {
223 return (Vec::new(), Vec::new());
224 }
225 let mut u: Vec<Vec<f64>> = (0..n)
226 .map(|i| {
227 let mut row: Vec<f64> = (0..self.c)
228 .map(|k| {
229 let base = 1.0 / self.c as f64;
230 let delta = 0.1 * ((i + k) % 3) as f64 / 3.0 - 0.05;
231 (base + delta).clamp(0.01, 0.99)
232 })
233 .collect();
234 let s: f64 = row.iter().sum();
235 for v in &mut row {
236 *v /= s;
237 }
238 row
239 })
240 .collect();
241 let mut centers: Vec<Vec<f64>> = vec![vec![0.0; dim]; self.c];
242 for _iter in 0..self.max_iter {
243 let mut old_centers = centers.clone();
244 for k in 0..self.c {
245 let mut num = vec![0.0_f64; dim];
246 let mut denom = 0.0_f64;
247 for i in 0..n {
248 let w = u[i][k].powf(self.m);
249 denom += w;
250 for d in 0..dim {
251 num[d] += w * data[i][d];
252 }
253 }
254 if denom.abs() > 1e-15 {
255 for d in 0..dim {
256 centers[k][d] = num[d] / denom;
257 }
258 }
259 }
260 for i in 0..n {
261 let dists: Vec<f64> = (0..self.c)
262 .map(|k| Self::dist(&data[i], ¢ers[k]).max(1e-15))
263 .collect();
264 let zero_k: Vec<usize> = (0..self.c).filter(|&k| dists[k] < 1e-12).collect();
265 if !zero_k.is_empty() {
266 for k in 0..self.c {
267 u[i][k] = 0.0;
268 }
269 let share = 1.0 / zero_k.len() as f64;
270 for &k in &zero_k {
271 u[i][k] = share;
272 }
273 } else {
274 let exp = 2.0 / (self.m - 1.0);
275 for k in 0..self.c {
276 let sum: f64 = (0..self.c).map(|j| (dists[k] / dists[j]).powf(exp)).sum();
277 u[i][k] = 1.0 / sum;
278 }
279 }
280 }
281 let movement: f64 = old_centers
282 .iter_mut()
283 .zip(centers.iter())
284 .map(|(oc, nc)| Self::dist(oc, nc))
285 .sum();
286 if movement < self.tol {
287 break;
288 }
289 }
290 (u, centers)
291 }
292 pub fn partition_coefficient(membership: &[Vec<f64>]) -> f64 {
296 let n = membership.len();
297 if n == 0 {
298 return 0.0;
299 }
300 let sum: f64 = membership
301 .iter()
302 .flat_map(|row| row.iter())
303 .map(|&u| u * u)
304 .sum();
305 sum / n as f64
306 }
307 pub fn classification_entropy(membership: &[Vec<f64>]) -> f64 {
309 let n = membership.len();
310 if n == 0 {
311 return 0.0;
312 }
313 let sum: f64 = membership
314 .iter()
315 .flat_map(|row| row.iter())
316 .map(|&u| if u > 1e-15 { -u * u.ln() } else { 0.0 })
317 .sum();
318 sum / n as f64
319 }
320}
321#[allow(dead_code)]
323#[derive(Debug, Clone)]
324pub struct MamdaniEngine {
325 pub input_names: Vec<String>,
326 pub output_name: String,
327 pub n_rules: usize,
328 pub defuzz_method: DefuzzMethod,
329}
330#[allow(dead_code)]
331impl MamdaniEngine {
332 pub fn new(inputs: Vec<&str>, output: &str, n_rules: usize) -> Self {
333 MamdaniEngine {
334 input_names: inputs.iter().map(|s| s.to_string()).collect(),
335 output_name: output.to_string(),
336 n_rules,
337 defuzz_method: DefuzzMethod::CentroidOfArea,
338 }
339 }
340 pub fn set_defuzz(&mut self, method: DefuzzMethod) {
341 self.defuzz_method = method;
342 }
343 pub fn n_inputs(&self) -> usize {
344 self.input_names.len()
345 }
346 pub fn centroid_defuzz(values: &[f64], memberships: &[f64]) -> f64 {
348 let num: f64 = values
349 .iter()
350 .zip(memberships.iter())
351 .map(|(v, m)| v * m)
352 .sum();
353 let den: f64 = memberships.iter().sum();
354 if den.abs() < 1e-12 {
355 0.0
356 } else {
357 num / den
358 }
359 }
360}
361#[derive(Debug, Clone)]
363pub struct SugenoRule {
364 pub antecedent_mf: Vec<f64>,
366 pub output_coeffs: Vec<f64>,
368 pub output_const: f64,
369}
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub enum ManyValuedLogic {
373 Lukasiewicz,
374 Godel,
375 Product,
376}
377impl ManyValuedLogic {
378 pub fn conj(self, a: f64, b: f64) -> f64 {
380 match self {
381 ManyValuedLogic::Lukasiewicz => (a + b - 1.0).max(0.0),
382 ManyValuedLogic::Godel => a.min(b),
383 ManyValuedLogic::Product => a * b,
384 }
385 }
386 pub fn disj(self, a: f64, b: f64) -> f64 {
388 match self {
389 ManyValuedLogic::Lukasiewicz => (a + b).min(1.0),
390 ManyValuedLogic::Godel => a.max(b),
391 ManyValuedLogic::Product => a + b - a * b,
392 }
393 }
394 pub fn residuum(self, a: f64, b: f64) -> f64 {
396 match self {
397 ManyValuedLogic::Lukasiewicz => (1.0 - a + b).min(1.0),
398 ManyValuedLogic::Godel => {
399 if a <= b {
400 1.0
401 } else {
402 b
403 }
404 }
405 ManyValuedLogic::Product => {
406 if a <= b {
407 1.0
408 } else {
409 b / a
410 }
411 }
412 }
413 }
414 pub fn neg(self, a: f64) -> f64 {
416 self.residuum(a, 0.0)
417 }
418 pub fn iff(self, a: f64, b: f64) -> f64 {
420 self.conj(self.residuum(a, b), self.residuum(b, a))
421 }
422}
423#[derive(Debug, Clone)]
426pub struct FiniteMTLAlgebra {
427 pub size: usize,
428 pub tnorm: Vec<Vec<usize>>,
430 pub residuum: Vec<Vec<usize>>,
432}
433impl FiniteMTLAlgebra {
434 pub fn from_tnorm(size: usize, tnorm: Vec<Vec<usize>>) -> Self {
436 let mut residuum = vec![vec![0usize; size]; size];
437 for a in 0..size {
438 for b in 0..size {
439 let mut best = 0usize;
440 for k in 0..size {
441 if tnorm[k][a] <= b {
442 best = best.max(k);
443 }
444 }
445 residuum[a][b] = best;
446 }
447 }
448 FiniteMTLAlgebra {
449 size,
450 tnorm,
451 residuum,
452 }
453 }
454 pub fn t(&self, a: usize, b: usize) -> usize {
456 self.tnorm[a][b]
457 }
458 pub fn r(&self, a: usize, b: usize) -> usize {
460 self.residuum[a][b]
461 }
462 pub fn neg(&self, a: usize) -> usize {
464 self.r(a, 0)
465 }
466 pub fn satisfies_prelinearity(&self) -> bool {
468 let top = self.size - 1;
469 for a in 0..self.size {
470 for b in 0..self.size {
471 let imp_ab = self.r(a, b);
472 let imp_ba = self.r(b, a);
473 let join = imp_ab.max(imp_ba);
474 if join != top {
475 return false;
476 }
477 }
478 }
479 true
480 }
481 pub fn satisfies_divisibility(&self) -> bool {
483 for a in 0..self.size {
484 for b in 0..self.size {
485 let meet = a.min(b);
486 let product = self.t(a, self.r(a, b));
487 if meet != product {
488 return false;
489 }
490 }
491 }
492 true
493 }
494}
495#[derive(Debug, Clone)]
497pub struct MamdaniRule {
498 pub antecedent_mf: Vec<f64>,
500 pub consequent: FuzzySet,
502}
503#[derive(Debug, Clone, Copy, PartialEq, Eq)]
505pub enum FISType {
506 Mamdani,
507 Sugeno,
508}
509#[derive(Debug, Clone, Copy, PartialEq, Eq)]
511pub enum DefuzzMethod {
512 CentroidOfArea,
513 BisectorOfArea,
514 MeanOfMaxima,
515 SmallestOfMaxima,
516 LargestOfMaxima,
517}
518#[derive(Debug, Clone, Copy, PartialEq, Eq)]
520pub enum TNorm {
521 Minimum,
522 Product,
523 Lukasiewicz,
524 Drastic,
525}
526impl TNorm {
527 pub fn eval(self, a: f64, b: f64) -> f64 {
529 match self {
530 TNorm::Minimum => a.min(b),
531 TNorm::Product => a * b,
532 TNorm::Lukasiewicz => (a + b - 1.0).max(0.0),
533 TNorm::Drastic => {
534 if (a - 1.0).abs() < 1e-9 {
535 b
536 } else if (b - 1.0).abs() < 1e-9 {
537 a
538 } else {
539 0.0
540 }
541 }
542 }
543 }
544 pub fn is_commutative_sample(&self, a: f64, b: f64) -> bool {
546 (self.eval(a, b) - self.eval(b, a)).abs() < 1e-9
547 }
548 pub fn is_associative_sample(&self, a: f64, b: f64, c: f64) -> bool {
550 (self.eval(self.eval(a, b), c) - self.eval(a, self.eval(b, c))).abs() < 1e-9
551 }
552}
553#[derive(Debug, Clone)]
556pub struct FuzzySet {
557 pub universe_size: usize,
558 pub membership: Vec<f64>,
560}
561impl FuzzySet {
562 pub fn new(universe_size: usize) -> Self {
564 FuzzySet {
565 universe_size,
566 membership: vec![0.0; universe_size],
567 }
568 }
569 pub fn set(&mut self, i: usize, degree: f64) {
571 self.membership[i] = degree.clamp(0.0, 1.0);
572 }
573 pub fn get(&self, i: usize) -> f64 {
575 self.membership[i]
576 }
577 pub fn alpha_cut(&self, alpha: f64) -> Vec<usize> {
579 self.membership
580 .iter()
581 .enumerate()
582 .filter(|(_, &d)| d >= alpha)
583 .map(|(i, _)| i)
584 .collect()
585 }
586 pub fn strong_alpha_cut(&self, alpha: f64) -> Vec<usize> {
588 self.membership
589 .iter()
590 .enumerate()
591 .filter(|(_, &d)| d > alpha)
592 .map(|(i, _)| i)
593 .collect()
594 }
595 pub fn complement(&self) -> FuzzySet {
597 let membership = self.membership.iter().map(|&d| 1.0 - d).collect();
598 FuzzySet {
599 universe_size: self.universe_size,
600 membership,
601 }
602 }
603 pub fn height(&self) -> f64 {
605 self.membership.iter().cloned().fold(0.0_f64, f64::max)
606 }
607 pub fn support(&self) -> Vec<usize> {
609 self.strong_alpha_cut(0.0)
610 }
611 pub fn core(&self) -> Vec<usize> {
613 self.membership
614 .iter()
615 .enumerate()
616 .filter(|(_, &d)| (d - 1.0).abs() < 1e-9)
617 .map(|(i, _)| i)
618 .collect()
619 }
620 pub fn is_normal(&self) -> bool {
622 (self.height() - 1.0).abs() < 1e-9
623 }
624}
625#[derive(Debug, Clone)]
627pub struct FuzzyTopology {
628 pub universe_size: usize,
629 pub open_sets: Vec<Vec<f64>>,
631}
632impl FuzzyTopology {
633 pub fn new(universe_size: usize) -> Self {
634 let mut ft = FuzzyTopology {
635 universe_size,
636 open_sets: Vec::new(),
637 };
638 ft.open_sets.push(vec![0.0; universe_size]);
639 ft.open_sets.push(vec![1.0; universe_size]);
640 ft
641 }
642 pub fn add_open_set(&mut self, set: Vec<f64>) {
643 assert_eq!(set.len(), self.universe_size);
644 self.open_sets.push(set);
645 }
646 pub fn closed_under_intersection(&self) -> bool {
648 let n = self.open_sets.len();
649 for i in 0..n {
650 for j in i..n {
651 let inter: Vec<f64> = self.open_sets[i]
652 .iter()
653 .zip(self.open_sets[j].iter())
654 .map(|(&a, &b)| a.min(b))
655 .collect();
656 if !self.contains_set(&inter) {
657 return false;
658 }
659 }
660 }
661 true
662 }
663 pub fn closed_under_union(&self) -> bool {
665 let n = self.open_sets.len();
666 for i in 0..n {
667 for j in i..n {
668 let union: Vec<f64> = self.open_sets[i]
669 .iter()
670 .zip(self.open_sets[j].iter())
671 .map(|(&a, &b)| a.max(b))
672 .collect();
673 if !self.contains_set(&union) {
674 return false;
675 }
676 }
677 }
678 true
679 }
680 fn contains_set(&self, s: &[f64]) -> bool {
681 self.open_sets
682 .iter()
683 .any(|o| o.iter().zip(s.iter()).all(|(&a, &b)| (a - b).abs() < 1e-9))
684 }
685}
686pub struct TNormComputer;
688impl TNormComputer {
689 pub fn frank(s: f64, a: f64, b: f64) -> f64 {
693 if s <= 0.0 {
694 return a.min(b);
695 }
696 if (s - 1.0).abs() < 1e-9 {
697 return a * b;
698 }
699 if s > 1e9 {
700 return a.min(b);
701 }
702 let sa = s.powf(a) - 1.0;
703 let sb = s.powf(b) - 1.0;
704 let denom = s - 1.0;
705 if denom.abs() < 1e-15 {
706 return a.min(b);
707 }
708 let inner = 1.0 + sa * sb / denom;
709 if inner <= 0.0 {
710 return 0.0;
711 }
712 inner.log(s).clamp(0.0, 1.0)
713 }
714 pub fn yager(p: f64, a: f64, b: f64) -> f64 {
716 if p <= 0.0 {
717 return a.min(b);
718 }
719 let sum = (1.0 - a).powf(p) + (1.0 - b).powf(p);
720 (1.0 - sum.powf(1.0 / p)).max(0.0).min(1.0)
721 }
722 pub fn schweizer_sklar(p: f64, a: f64, b: f64) -> f64 {
724 if p == 0.0 {
725 return a * b;
726 }
727 if p < 0.0 {
728 let val = (a.powf(p) + b.powf(p) - 1.0).powf(1.0 / p);
729 return val.max(0.0).min(1.0);
730 }
731 let val = (a.powf(p) + b.powf(p) - 1.0).powf(1.0 / p);
732 val.max(0.0).min(1.0)
733 }
734 pub fn check_commutativity<F: Fn(f64, f64) -> f64>(t: &F, samples: &[(f64, f64)]) -> bool {
736 samples
737 .iter()
738 .all(|&(a, b)| (t(a, b) - t(b, a)).abs() < 1e-9)
739 }
740 pub fn check_associativity<F: Fn(f64, f64) -> f64>(t: &F, triples: &[(f64, f64, f64)]) -> bool {
742 triples
743 .iter()
744 .all(|&(a, b, c)| (t(t(a, b), c) - t(a, t(b, c))).abs() < 1e-9)
745 }
746 pub fn check_boundary<F: Fn(f64, f64) -> f64>(t: &F, samples: &[f64]) -> bool {
748 samples.iter().all(|&a| (t(a, 1.0) - a).abs() < 1e-9)
749 }
750 pub fn check_monotonicity<F: Fn(f64, f64) -> f64>(t: &F, samples: &[(f64, f64, f64)]) -> bool {
752 samples
753 .iter()
754 .all(|&(a, b, c)| a > b || t(a, c) <= t(b, c) + 1e-9)
755 }
756}
757#[allow(dead_code)]
759#[derive(Debug, Clone)]
760pub struct GradualElement {
761 pub name: String,
762 pub degree: f64,
763}
764#[allow(dead_code)]
765impl GradualElement {
766 pub fn new(name: &str, degree: f64) -> Self {
767 GradualElement {
768 name: name.to_string(),
769 degree: degree.clamp(0.0, 1.0),
770 }
771 }
772 pub fn is_true(&self) -> bool {
773 self.degree > 0.5
774 }
775 pub fn complement(&self) -> Self {
776 GradualElement::new(&format!("not_{}", self.name), 1.0 - self.degree)
777 }
778 pub fn conjunction(&self, other: &GradualElement) -> GradualElement {
779 let deg = self.degree.min(other.degree);
780 GradualElement::new(&format!("({} AND {})", self.name, other.name), deg)
781 }
782 pub fn disjunction(&self, other: &GradualElement) -> GradualElement {
783 let deg = self.degree.max(other.degree);
784 GradualElement::new(&format!("({} OR {})", self.name, other.name), deg)
785 }
786}
787#[derive(Debug, Clone)]
790pub struct FuzzyMetricSpace {
791 pub points: usize,
792 pub metric: Vec<Vec<Vec<f64>>>,
794 pub t_grid: Vec<f64>,
795}
796impl FuzzyMetricSpace {
797 pub fn new(points: usize, t_grid: Vec<f64>) -> Self {
798 let nt = t_grid.len();
799 FuzzyMetricSpace {
800 points,
801 metric: vec![vec![vec![0.0; nt]; points]; points],
802 t_grid,
803 }
804 }
805 pub fn set_metric(&mut self, x: usize, y: usize, t_idx: usize, value: f64) {
806 self.metric[x][y][t_idx] = value.clamp(0.0, 1.0);
807 self.metric[y][x][t_idx] = value.clamp(0.0, 1.0);
808 }
809 pub fn check_limit_axiom(&self) -> bool {
811 let last = self.t_grid.len() - 1;
812 for x in 0..self.points {
813 for y in 0..self.points {
814 if (self.metric[x][y][last] - 1.0).abs() > 1e-6 {
815 return false;
816 }
817 }
818 }
819 true
820 }
821 pub fn check_diagonal_axiom(&self) -> bool {
823 for x in 0..self.points {
824 for t_idx in 0..self.t_grid.len() {
825 if (self.metric[x][x][t_idx] - 1.0).abs() > 1e-6 {
826 return false;
827 }
828 }
829 }
830 true
831 }
832 pub fn check_non_separability(&self) -> bool {
834 for x in 0..self.points {
835 for y in 0..self.points {
836 if x == y {
837 continue;
838 }
839 let all_one = self.metric[x][y].iter().all(|&v| (v - 1.0).abs() < 1e-6);
840 if all_one {
841 return false;
842 }
843 }
844 }
845 true
846 }
847}
848pub struct LinguisticHedgeApplier;
853impl LinguisticHedgeApplier {
854 pub fn very(degree: f64) -> f64 {
856 degree * degree
857 }
858 pub fn more_or_less(degree: f64) -> f64 {
860 degree.sqrt()
861 }
862 pub fn somewhat(degree: f64) -> f64 {
864 degree.powf(1.0 / 3.0)
865 }
866 pub fn extremely(degree: f64) -> f64 {
868 degree.powi(3)
869 }
870 pub fn not(degree: f64) -> f64 {
872 1.0 - degree
873 }
874 pub fn slightly(degree: f64) -> f64 {
876 degree.powf(1.7)
877 }
878 pub fn indeed(degree: f64) -> f64 {
882 if degree <= 0.5 {
883 2.0 * degree * degree
884 } else {
885 1.0 - 2.0 * (1.0 - degree).powi(2)
886 }
887 }
888 pub fn plus(degree: f64) -> f64 {
890 degree.powf(1.25)
891 }
892 pub fn apply(hedge: &str, degree: f64) -> f64 {
894 match hedge {
895 "very" => Self::very(degree),
896 "more_or_less" | "more-or-less" | "sort_of" => Self::more_or_less(degree),
897 "somewhat" => Self::somewhat(degree),
898 "extremely" => Self::extremely(degree),
899 "not" => Self::not(degree),
900 "slightly" => Self::slightly(degree),
901 "indeed" => Self::indeed(degree),
902 "plus" => Self::plus(degree),
903 _ => degree,
904 }
905 }
906 pub fn apply_chain(hedges: &[&str], degree: f64) -> f64 {
908 hedges.iter().fold(degree, |d, h| Self::apply(h, d))
909 }
910 pub fn apply_to_set(hedge: &str, set: &FuzzySet) -> FuzzySet {
912 let membership = set
913 .membership
914 .iter()
915 .map(|&d| Self::apply(hedge, d))
916 .collect();
917 FuzzySet {
918 universe_size: set.universe_size,
919 membership,
920 }
921 }
922 pub fn exponent(hedge: &str) -> Option<f64> {
924 match hedge {
925 "very" => Some(2.0),
926 "more_or_less" => Some(0.5),
927 "somewhat" => Some(1.0 / 3.0),
928 "extremely" => Some(3.0),
929 "slightly" => Some(1.7),
930 "plus" => Some(1.25),
931 _ => None,
932 }
933 }
934}
935#[allow(dead_code)]
937#[derive(Debug, Clone)]
938pub struct FuzzyRoughApprox {
939 pub universe_size: usize,
940 pub similarity: Vec<Vec<f64>>,
941}
942#[allow(dead_code)]
943impl FuzzyRoughApprox {
944 pub fn new(n: usize) -> Self {
945 let mut sim = vec![vec![0.0; n]; n];
946 for i in 0..n {
947 sim[i][i] = 1.0;
948 }
949 FuzzyRoughApprox {
950 universe_size: n,
951 similarity: sim,
952 }
953 }
954 pub fn set_similarity(&mut self, x: usize, y: usize, val: f64) {
955 self.similarity[x][y] = val.clamp(0.0, 1.0);
956 self.similarity[y][x] = val.clamp(0.0, 1.0);
957 }
958 pub fn lower_approx(&self, a: &[f64]) -> Vec<f64> {
960 (0..self.universe_size)
961 .map(|x| {
962 (0..self.universe_size)
963 .map(|y| {
964 let r = self.similarity[x][y];
965 let ay = a[y];
966 (1.0 - r + ay).min(1.0)
967 })
968 .fold(f64::INFINITY, f64::min)
969 })
970 .collect()
971 }
972 pub fn upper_approx(&self, a: &[f64]) -> Vec<f64> {
974 (0..self.universe_size)
975 .map(|x| {
976 (0..self.universe_size)
977 .map(|y| self.similarity[x][y].min(a[y]))
978 .fold(0.0f64, f64::max)
979 })
980 .collect()
981 }
982}
983#[derive(Debug, Clone)]
985pub struct MamdaniSystem {
986 pub rules: Vec<MamdaniRule>,
987 pub output_size: usize,
988}
989impl MamdaniSystem {
990 pub fn new(output_size: usize) -> Self {
991 MamdaniSystem {
992 rules: Vec::new(),
993 output_size,
994 }
995 }
996 pub fn add_rule(&mut self, rule: MamdaniRule) {
997 self.rules.push(rule);
998 }
999 pub fn infer(&self, input_degrees: &[Vec<f64>]) -> FuzzySet {
1001 let mut agg = FuzzySet::new(self.output_size);
1002 for rule in &self.rules {
1003 let strength = rule
1004 .antecedent_mf
1005 .iter()
1006 .zip(input_degrees.iter().flatten())
1007 .map(|(&a, &b)| a.min(b))
1008 .fold(1.0_f64, f64::min);
1009 for i in 0..self.output_size {
1010 let clipped = rule.consequent.get(i).min(strength);
1011 let current = agg.get(i);
1012 agg.set(i, current.max(clipped));
1013 }
1014 }
1015 agg
1016 }
1017}
1018#[derive(Debug, Clone)]
1020pub struct SugenoSystem {
1021 pub rules: Vec<SugenoRule>,
1022}
1023impl SugenoSystem {
1024 pub fn new() -> Self {
1025 SugenoSystem { rules: Vec::new() }
1026 }
1027 pub fn add_rule(&mut self, rule: SugenoRule) {
1028 self.rules.push(rule);
1029 }
1030 pub fn infer(&self, inputs: &[f64], input_degrees: &[Vec<f64>]) -> f64 {
1032 let mut weighted_sum = 0.0;
1033 let mut weight_total = 0.0;
1034 for rule in &self.rules {
1035 let strength: f64 = rule
1036 .antecedent_mf
1037 .iter()
1038 .zip(input_degrees.iter().flatten())
1039 .map(|(&a, &b)| a.min(b))
1040 .fold(1.0_f64, f64::min);
1041 let z = rule.output_const
1042 + rule
1043 .output_coeffs
1044 .iter()
1045 .zip(inputs.iter())
1046 .map(|(&c, &x)| c * x)
1047 .sum::<f64>();
1048 weighted_sum += strength * z;
1049 weight_total += strength;
1050 }
1051 if weight_total.abs() < 1e-12 {
1052 0.0
1053 } else {
1054 weighted_sum / weight_total
1055 }
1056 }
1057}
1058#[allow(dead_code)]
1060#[derive(Debug, Clone)]
1061pub struct TriangularFuzzyNum {
1062 pub lower: f64,
1063 pub modal: f64,
1064 pub upper: f64,
1065}
1066#[allow(dead_code)]
1067impl TriangularFuzzyNum {
1068 pub fn new(lower: f64, modal: f64, upper: f64) -> Self {
1069 assert!(lower <= modal && modal <= upper);
1070 TriangularFuzzyNum {
1071 lower,
1072 modal,
1073 upper,
1074 }
1075 }
1076 pub fn membership(&self, x: f64) -> f64 {
1077 if x < self.lower || x > self.upper {
1078 0.0
1079 } else if x <= self.modal {
1080 (x - self.lower) / (self.modal - self.lower).max(1e-12)
1081 } else {
1082 (self.upper - x) / (self.upper - self.modal).max(1e-12)
1083 }
1084 }
1085 pub fn add(&self, other: &TriangularFuzzyNum) -> TriangularFuzzyNum {
1086 TriangularFuzzyNum::new(
1087 self.lower + other.lower,
1088 self.modal + other.modal,
1089 self.upper + other.upper,
1090 )
1091 }
1092 pub fn scale(&self, k: f64) -> TriangularFuzzyNum {
1093 if k >= 0.0 {
1094 TriangularFuzzyNum::new(k * self.lower, k * self.modal, k * self.upper)
1095 } else {
1096 TriangularFuzzyNum::new(k * self.upper, k * self.modal, k * self.lower)
1097 }
1098 }
1099 pub fn defuzzify_centroid(&self) -> f64 {
1100 (self.lower + self.modal + self.upper) / 3.0
1101 }
1102 pub fn alpha_cut(&self, alpha: f64) -> (f64, f64) {
1103 let lo = self.lower + alpha * (self.modal - self.lower);
1104 let hi = self.upper - alpha * (self.upper - self.modal);
1105 (lo, hi)
1106 }
1107}