flex icon indicating copy to clipboard operation
flex copied to clipboard

FLex: Fuzzy Logic for Elixir

FLex Logo


Valiot Logo

FLex

A toolkit for fuzzy logic, this library includes functions to make fuzzy sets, variables and rules for creating a Fuzzy Logic System (FLS).

The goal of FLex is to easily design and efficiently operate fuzzy logic controllers without relying on external libraries.

Index

  • Features

  • Installation

  • Usage

    • Sets
    • Variables
    • Rules
    • System
    • ANFIS
  • Documentation

  • Contributing

  • License

  • TODO

Features

The following list is the current supported backend for each component of the FLS:

  • Linguistic Rules:

    • Lambda function syntax
    • Tuple syntax
  • Membership functions:

    • Triangular
    • Trapezoidal
    • Saturation
    • Shoulder
    • Gauss
    • Generalized Bell
    • Sigmoid
    • Z-shaped
    • S-shaped
    • Pi-shaped
    • Linear Combination (Takagi-Sugeno, ANFIS only)
  • Fuzzy Inference Systems:

    • Mamdani:

      • Inference:

        • Min
      • Output Combination:

        • Root-sum-square
      • Defuzzification:

        • Centroid
    • Takagi-Sugeno:

      • Inference:

        • Max
        • Product
      • Defuzzification:

        • Weighted average
    • ANFIS:

      • Inference:

        • Max
        • Product
      • Defuzzification:

        • Weighted average
      • Optimization Method:

        • Backpropagation.
        • Hybrid (Backpropagation, LSE).

NOTE: All systems are single output.

Installation

The package can be installed by adding flex to your list of dependencies in mix.exs:

def deps do
  [
    {:flex, "~> 0.1.0"}
  ]
end

Usage


Sets

Step 1: Define all fuzzy sets with Flex.Set.new/1, the following options are require:

  • mf_type - (string) Defines which type of membership function uses the set (e.g., "triangle").
  • tag - (string) defines the linguistic name of the fuzzy set (e.g., "too hot"),
  • mf_params - The parameters of the membership function, see Membership functions.
t_h = Flex.Set.new(tag: "too hot", mf_type: "saturation", mf_params: [-2, 0, -4])
j_r = Flex.Set.new(tag: "just right", mf_type: "triangle", mf_params: [-2, 0, 2])
t_c = Flex.Set.new(tag: "too cold", mf_type: "shoulder", mf_params: [0, 2, 4])

g_h = Flex.Set.new(tag: "getting hotter", mf_type: "saturation", mf_params: [-5, 0, -10])
n_c = Flex.Set.new(tag: "no change", mf_type: "triangle", mf_params: [-5, 0, 5])
g_c = Flex.Set.new(tag: "getting colder", mf_type: "shoulder", mf_params: [0, 5, 10])

co = Flex.Set.new(tag: "cool", mf_type: "saturation", mf_params: [-50, 0, -100])
d_n = Flex.Set.new(tag: "do nothing", mf_type: "triangle", mf_params: [-50, 0, 50])
he = Flex.Set.new(tag: "heat", mf_type: "shoulder", mf_params: [0, 50, 100])

Variables

Step 2: Define all fuzzy variables with Flex.Variable.new/1, the following options are required:

  • :tag - (string) Defines the linguistic name of the fuzzy variable (e.g., "error"),
  • :fuzzy_sets - (list) Defines which type of membership function use the set (e.g., "triangle").
  • :type - (atom) Defines the type of variable (e.g., :antecedent or :consequent),
  • :range - (range) The range in which the variable exists.
fuzzy_sets = [t_h, j_r, t_c]
error = Flex.Variable.new(tag: "error", fuzzy_sets: fuzzy_sets, type: :antecedent, range: -4..4)

fuzzy_sets = [g_h, n_c, g_c]
dt_error = Flex.Variable.new(tag: "dt_error", fuzzy_sets: fuzzy_sets, type: :antecedent, range: -10..10)

fuzzy_sets = [co, d_n, he]
output = Flex.Variable.new(tag: "output", fuzzy_sets: fuzzy_sets, type: :consequent, range: -100..100)

Rules

Currently there are two types of syntax for defining the rules statement:

  • Anonymous function syntax:
  r1 = fn [at1, at2, con] ->
      (at1 ~> "too hot" &&& at2 ~> "getting colder") >>> con ~> "cool"
    end
  • Tuple syntax:
  r1 = {{{{"error", "too hot", "~>"}, {"dt_error", "getting colder", "~>"}, "&&&"}, "output",
      ">>>"}, "cool", "~>"}

Step 3: Define all Linguistic rules with Flex.Rule.new/1, the following options are required:

  • :statement - Defines the rule behavior.
  • :antecedent - (list) Defines the input variables.
  • :consequent - Defines the output variable.
  import Flex.Rule
  r1 =
    {{{{"error", "too hot", "~>"}, {"dt_error", "getting colder", "~>"}, "&&&"}, "output",
      ">>>"}, "cool", "~>"}

  r2 =
    {{{{"error", "just right", "~>"}, {"dt_error", "getting colder", "~>"}, "&&&"}, "output",
      ">>>"}, "heat", "~>"}

  r3 =
    {{{{"error", "too cold", "~>"}, {"dt_error", "getting colder", "~>"}, "&&&"}, "output",
      ">>>"}, "heat", "~>"}

  r4 =
    {{{{"error", "too hot", "~>"}, {"dt_error", "no change", "~>"}, "&&&"}, "output", ">>>"},
      "cool", "~>"}

  r5 =
    {{{{"error", "just right", "~>"}, {"dt_error", "no change", "~>"}, "&&&"}, "output", ">>>"},
      "do nothing", "~>"}

  r6 =
    {{{{"error", "too cold", "~>"}, {"dt_error", "no change", "~>"}, "&&&"}, "output", ">>>"},
      "heat", "~>"}

  r7 =
    {{{{"error", "too hot", "~>"}, {"dt_error", "getting hotter", "~>"}, "&&&"}, "output",
      ">>>"}, "cool", "~>"}

  r8 =
    {{{{"error", "just right", "~>"}, {"dt_error", "getting hotter", "~>"}, "&&&"}, "output",
      ">>>"}, "cool", "~>"}

  r9 =
    {{{{"error", "too cold", "~>"}, {"dt_error", "getting hotter", "~>"}, "&&&"}, "output",
      ">>>"}, "cool", "~>"}

  rule1 = Flex.Rule.new(statement: r1, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule2 = Flex.Rule.new(statement: r2, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule3 = Flex.Rule.new(statement: r3, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule4 = Flex.Rule.new(statement: r4, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule5 = Flex.Rule.new(statement: r5, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule6 = Flex.Rule.new(statement: r6, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule7 = Flex.Rule.new(statement: r7, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule8 = Flex.Rule.new(statement: r8, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
  rule9 = Flex.Rule.new(statement: r9, consequent: output.tag, antecedent: [error.tag, dt_error.tag])

  rules = [rule1, rule2, rule3, rule4, rule5, rule6, rule7, rule8, rule9]

Note: You need to import Flex.Rule module.

System

Step 4: Define FLS with Flex.System.start_link/1 or Flex.System.start_link/2 if you want to overwrite the GenServer options; the following options are require:

  • :rules - Defines the behavior of the system based on a list of rules.
  • :antecedent - (list) Defines the input variables.
  • :consequent - Defines the output variable.
  • :engine_type - Defines the inference engine behavior (default: Mamdini).
  {:ok, s_pid} = Flex.System.start_link(antecedent: [error, dt_error], consequent: output, rules: rules)

Step 5: Fit the FLS with a input vector using Flex.System.compute/2.

  result = Flex.System.compute(s_pid, [-1, -2.5])
  #result ~= -63.4 aprox

In test/system_test.exs there is an example of use, that is based on this example.

ANFIS

An adaptive network-based fuzzy inference system (ANFIS) is a kind of artificial neural network that is based on Takagi–Sugeno fuzzy inference system, this implementation use backpropagation, only Gaussian & Generalized Bell Membership functions are allowed. In examples/anfis_demo1.exs there is an example of use.

Documentation

The docs can be found at https://hexdocs.pm/flex.

Contributing

  • Fork our repository on github.
  • Fix or add what is needed.
  • Commit to your repository.
  • Issue a github pull request (fill the PR template).

License

See LICENSE.

TODO

  • Add more membership functions.
  • Add more inference methods.
  • Add more defuzzification methods.
  • Add helper functions.
  • Add metaprogramming for linguistic rules.