Source code for skfuzzy.control.rule

"""
rule.py : Contains structure to create fuzzy rules.

Most notably, contains the `Rule` object which is used to connect atecedents
with conqeuents in a `ControlSystem`.
"""
from __future__ import print_function, division

import networkx as nx
from .term import (Term, WeightedTerm, TermAggregate, FuzzyAggregationMethod,
                   TermPrimitive)
from .state import StatefulProperty
from .visualization import ControlSystemVisualizer


[docs]class Rule(object): """ Rule in a fuzzy control system, connecting antecedent(s) to consequent(s). Parameters ---------- antecedent : Antecedent term(s) or logical combination thereof, optional Antecedent terms serving as inputs to this rule. Multiple terms may be combined using operators `|` (OR), `&` (AND), `~` (NOT), and parentheticals to group terms. consequent : Consequent term(s) or logical combination thereof, optional Consequent terms serving as outputs from this rule. Multiple terms may be combined using operators `|` (OR), `&` (AND), `~` (NOT), and parentheticals to group terms. label : string, optional Label to reference the meaning of this rule. Optional, but recommended. If provided, the label must be unique among rules in any particular `ControlSystem`. Notes ----- Fuzzy Rules can be completely built on instantatiation or one can begin with an empty Rule and construct interactively by setting `.antecedent`, `.consequent`, and `.label` variables. """ aggregate_firing = StatefulProperty(None)
[docs] def __init__(self, antecedent=None, consequent=None, label=None): """ Rule in a fuzzy system, connecting antecedent(s) to consequent(s). Parameters ---------- antecedent : Antecedent term(s) or combination thereof, optional Antecedent terms serving as inputs to this rule. Multiple terms may be combined using operators `|` (OR), `&` (AND), `~` (NOT), and parentheticals to group terms. consequent : Consequent term(s) or combination thereof, optional Consequent terms serving as outputs from this rule. Multiple terms may be combined using operators `|` (OR), `&` (AND), `~` (NOT), and parentheticals to group terms. label : string, optional Label to reference the meaning of this rule. Optional, but recommended. """ self.aggregation_method = FuzzyAggregationMethod() self._antecedent = None self._consequent = None if antecedent is not None: self.antecedent = antecedent if consequent is not None: self.consequent = consequent if label is not None: self.label = label else: self.label = id(self)
def __repr__(self): """ Concise, readable summary of the fuzzy rule. """ if len(self.consequent) == 1: cons = self.consequent[0] else: cons = self.consequent return "IF %s THEN %s" % (self.antecedent, cons) @property def antecedent(self): """ Antecedent clause, consisting of multiple term(s) in this fuzzy Rule. """ if self._antecedent is None: raise ValueError("Antecedent not set") return self._antecedent @antecedent.setter def antecedent(self, value): """ Method to interactively set Antecedent term(s). """ if not isinstance(value, TermPrimitive): raise ValueError("Unexpected antecedent type") # Should be either Term or TermAggregate self._antecedent = value @property def antecedent_terms(self): """ Utility function to list all Antecedent terms present in this clause. """ # Grab all the terms that make up my antecedent clause terms = [] def _find_terms(obj): if isinstance(obj, Term): terms.append(obj) elif obj is None: pass else: assert isinstance(obj, TermAggregate) _find_terms(obj.term1) _find_terms(obj.term2) _find_terms(self.antecedent) return terms @property def consequent(self): """ Consequent clause, consisting of multiple term(s) in this fuzzy Rule. """ if self._consequent is None: raise ValueError("Consquent not set") return self._consequent @consequent.setter def consequent(self, value): """ Accept consequents in four formats: a) Unweighted single output. e.g.: output['term'] b) Weighted single output e.g.: (output['term']%0.5) c) Unweighted multiple output e.g.: (output1['term1'], output2['term2']) d) Weighted multiple output e.g.: ( (output1['term1']%1.0), (output2['term2']%0.5) ) """ if isinstance(value, Term): self._consequent = [WeightedTerm(value, 1.)] elif isinstance(value, WeightedTerm): self._consequent = [value] elif not hasattr(value, '__iter__'): raise ValueError("Unexpected consequent type") else: # Must be one of formats b) to d) self._consequent = [] for i in value: if isinstance(i, Term): self._consequent.append(WeightedTerm(i, 1.)) elif isinstance(i, WeightedTerm): self._consequent.append(i) else: raise ValueError("Unexpected consequent type") @property def graph(self): """ NetworkX directed graph representing this Rule's connectivity. """ graph = nx.DiGraph() # Link all antecedents to me by decomposing # TermAggregate down to just Terms for t in self.antecedent_terms: assert isinstance(t, Term) graph.add_path([t, self]) graph = nx.compose(graph, t.parent.graph) # Link all consequents from me for c in self.consequent: assert isinstance(c, WeightedTerm) graph.add_path([self, c.term]) graph = nx.compose(graph, c.term.parent.graph) return graph
[docs] def view(self): """ Show a visual representation of this Rule. """ ControlSystemVisualizer(self).view().show()