rust_fuzzylogic/
mamdani.rs

1// Mamdani-style implication and rule behavior primitives.
2
3use std::{borrow::Borrow, collections::HashMap, hash::Hash};
4
5#[cfg(feature = "inference-mamdani")]
6use crate::{
7    antecedent::{eval_antecedent, Antecedent},
8    error::{FuzzyError, MissingSpace},
9    prelude::*,
10    sampler::UniformSampler,
11    variable::Variable,
12};
13
14/// Implication modes supported by a Mamdani rule.
15pub enum Implication {
16    Clip,
17    Product,
18}
19
20/// Output clause of a fuzzy rule referencing a linguistic variable and term.
21pub struct Consequent {
22    pub var: String,
23    pub term: String,
24    //pub weight: Float,
25    //pub imp: Implication,
26}
27
28/// Full fuzzy rule pairing an antecedent with one or more consequents.
29pub struct Rule {
30    pub antecedent: Antecedent,
31    pub consequent: Vec<Consequent>,
32}
33
34//Mamdani Inference Engine
35#[cfg(feature = "inference-mamdani")]
36impl Rule {
37    /// Evaluate the antecedent against crisp input values to obtain activation.
38    pub fn activation<KI, KV>(
39        &self,
40        input: &HashMap<KI, Float>,
41        vars: &HashMap<KV, Variable>,
42    ) -> Result<Float>
43    where
44        KI: Eq + Hash + Borrow<str>,
45        KV: Eq + Hash + Borrow<str>,
46    {
47        eval_antecedent(&self.antecedent, input, vars)
48    }
49
50    /// Apply the selected implication operator to produce discretized membership outputs.
51    pub fn implicate<KV>(
52        &self,
53        alpha: Float,
54        vers: &HashMap<KV, Variable>,
55        sampler: &UniformSampler,
56    ) -> Result<HashMap<String, Vec<Float>>>
57    where
58        KV: Eq + Hash + Borrow<str>,
59    {
60        let mut result_map: HashMap<String, Vec<Float>> = HashMap::new();
61
62        for i in 0..self.consequent.len() {
63            let mut result_vec = vec![0.0; sampler.n];
64
65            let (dom_min, dom_max) = vers
66                .get(&self.consequent[i].var.as_str())
67                .ok_or(FuzzyError::NotFound {
68                    space: MissingSpace::Var,
69                    key: self.consequent[i].term.clone(),
70                })?
71                .domain();
72
73            let step = (dom_max - dom_min) / sampler.n as Float;
74
75            for k in 0..sampler.n {
76                let x = dom_min + (k as Float * step);
77                result_vec[k] = vers
78                    .get(&self.consequent[i].var.as_str())
79                    .ok_or(FuzzyError::NotFound {
80                        space: MissingSpace::Var,
81                        key: self.consequent[i].term.clone(),
82                    })?
83                    .eval(&self.consequent[i].term, x)?
84                    .min(alpha);
85            }
86
87            result_map.insert(self.consequent[i].var.to_string(), result_vec);
88        }
89        return Ok(result_map);
90        //TODO: Return type should be hashmap<string, Vec<Float>> where string signifies the variable(eg "fanspeed")
91    }
92}