defmodule Flex.Rule do alias Flex.Rule @moduledoc """ An interface to create Linguistic Rules. """ defstruct statement: nil, antecedent: nil, consequent: nil @typedoc """ Linguistic Rule struct. - `:statement` - Rules behavior. - `:antecedent` - (list) Input variables. - `:consequent` - Output variable. """ @type t :: %__MODULE__{ statement: fun() | tuple(), antecedent: [Flex.Variable.t(), ...], consequent: Flex.Variable.t() } @doc """ Creates a Linguistic Rule. The following options are require: - `:statement` - Defines the rule behavior. - `:antecedent` - (list) Defines the input variables. - `:consequent` - Defines the output variable. """ def new(params) do rule = Keyword.fetch!(params, :statement) antecedent = Keyword.fetch!(params, :antecedent) consequent = Keyword.fetch!(params, :consequent) %Rule{statement: rule, antecedent: antecedent, consequent: consequent} end @doc """ Fuzzy AND operator (product). """ def tau(a, b), do: a * b @doc """ Fuzzy AND operator. """ def a &&& b, do: min(a, b) @doc """ Fuzzy OR operator. """ def a ||| b, do: max(a, b) @doc """ Fuzzy THEN operator. """ def a >>> b do case b.type do :antecedent -> raise("only the consequent variable can use the THEN operation") :consequent -> %{b | rule_output: a} end end @doc """ Fuzzy IS operator. """ def a ~> b do case a.type do :antecedent -> a.mf_values[b] :consequent -> new_values = Map.get(a.mf_values, b, []) ++ [a.rule_output] mf_values = Map.put(a.mf_values, b, new_values) %{a | mf_values: mf_values} end end @doc """ Fuzzy Rules AST (Tuple). """ def statement({arg1, arg2, "tau"}, args), do: tau(statement(arg1, args), statement(arg2, args)) def statement({arg1, arg2, "&&&"}, args), do: statement(arg1, args) &&& statement(arg2, args) def statement({arg1, arg2, "|||"}, args), do: statement(arg1, args) ||| statement(arg2, args) def statement({var_tag, set_tag, "~>"}, args) when is_binary(var_tag) do fuzzy_var = Map.get(args, var_tag, :error) fuzzy_var ~> set_tag end def statement({consequent, set_tag, "~>"}, args), do: statement(consequent, args) ~> set_tag def statement({arg1, con_tag, ">>>"}, args) do val = statement(arg1, args) consequent = Map.get(args, con_tag) val >>> consequent end def statement(arg, _args), do: arg @doc """ Gets the arguments of the Fuzzy Rule """ def get_rule_parameters([], _antecedent, lt_ant_vars), do: lt_ant_vars def get_rule_parameters([tag | tail], antecedent, lt_ant_vars) do f_var = Map.get(antecedent, tag) lt_ant_vars = lt_ant_vars ++ [f_var] get_rule_parameters(tail, antecedent, lt_ant_vars) end end