Current section
Files
Jump to
Current section
Files
test/system_test.exs
defmodule SystemTest do
use ExUnit.Case
import Flex.Rule
alias Flex.{EngineAdapter.TakagiSugeno, Rule, Set, System, Variable}
alias Flex.EngineAdapter.{Mamdani, TakagiSugeno}
doctest Flex
# http://robotics.ee.uwa.edu.au/courses/faulttolerant/notes/FT5.pdf
setup do
t_h = Set.new(tag: "too hot", mf_type: "saturation", mf_params: [-2, 0, -4])
j_r = Set.new(tag: "just right", mf_type: "triangle", mf_params: [-2, 0, 2])
t_c = Set.new(tag: "too cold", mf_type: "shoulder", mf_params: [0, 2, 4])
fuzzy_sets = [t_h, j_r, t_c]
error = Variable.new(tag: "error", fuzzy_sets: fuzzy_sets, type: :antecedent, range: -4..4)
t_h = Set.new(tag: "getting hotter", mf_type: "saturation", mf_params: [-5, 0, -10])
j_r = Set.new(tag: "no change", mf_type: "triangle", mf_params: [-5, 0, 5])
t_c = Set.new(tag: "getting colder", mf_type: "shoulder", mf_params: [0, 5, 10])
fuzzy_sets = [t_h, j_r, t_c]
dt_error =
Variable.new(tag: "dt_error", fuzzy_sets: fuzzy_sets, type: :antecedent, range: -10..10)
t_h = Set.new(tag: "cool", mf_type: "saturation", mf_params: [-50, 0, -100])
j_r = Set.new(tag: "do nothing", mf_type: "triangle", mf_params: [-50, 0, 50])
t_c = Set.new(tag: "heat", mf_type: "shoulder", mf_params: [0, 50, 100])
fuzzy_sets = [t_h, j_r, t_c]
output =
Variable.new(tag: "output", fuzzy_sets: fuzzy_sets, type: :consequent, range: -100..100)
r1 = fn [at1, at2, con] ->
(at1 ~> "too hot" &&& at2 ~> "getting colder") >>> con ~> "cool"
end
r2 = fn [at1, at2, con] ->
(at1 ~> "just right" &&& at2 ~> "getting colder") >>> con ~> "heat"
end
r3 = fn [at1, at2, con] ->
(at1 ~> "too cold" &&& at2 ~> "getting colder") >>> con ~> "heat"
end
r4 = fn [at1, at2, con] ->
(at1 ~> "too hot" &&& at2 ~> "no change") >>> con ~> "cool"
end
r5 = fn [at1, at2, con] ->
(at1 ~> "just right" &&& at2 ~> "no change") >>> con ~> "do nothing"
end
r6 = fn [at1, at2, con] ->
(at1 ~> "too cold" &&& at2 ~> "no change") >>> con ~> "heat"
end
r7 = fn [at1, at2, con] ->
(at1 ~> "too hot" &&& at2 ~> "getting hotter") >>> con ~> "cool"
end
r8 = fn [at1, at2, con] ->
(at1 ~> "just right" &&& at2 ~> "getting hotter") >>> con ~> "cool"
end
r9 = fn [at1, at2, con] ->
(at1 ~> "too cold" &&& at2 ~> "getting hotter") >>> con ~> "heat"
end
rule1 =
Rule.new(statement: r1, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule2 =
Rule.new(statement: r2, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule3 =
Rule.new(statement: r3, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule4 =
Rule.new(statement: r4, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule5 =
Rule.new(statement: r5, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule6 =
Rule.new(statement: r6, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule7 =
Rule.new(statement: r7, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule8 =
Rule.new(statement: r8, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule9 =
Rule.new(statement: r9, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rules = [rule1, rule2, rule3, rule4, rule5, rule6, rule7, rule8, rule9]
%{ant: [error, dt_error], cons: output, rules: rules, error: error, dt_error: dt_error}
end
test "Setup for the fuzzy logic system", %{ant: ant, cons: output, rules: rules} do
{:ok, s_pid} = System.start_link(antecedent: ant, consequent: output, rules: rules)
state = :sys.get_state(s_pid)
assert is_list(state.antecedent)
assert is_map(state.consequent)
assert is_list(state.rules)
end
test "Compute an output with an input vector", %{ant: ant, cons: output, rules: rules} do
{:ok, s_pid} = System.start_link(antecedent: ant, consequent: output, rules: rules)
output = System.compute(s_pid, [-1, -2.5])
assert Float.floor(output, 1) == -63.4
end
test "Change Engine Inference", %{ant: ant, cons: output, rules: rules} do
{:ok, s_pid} = System.start_link(antecedent: ant, consequent: output, rules: rules)
assert System.set_engine_type(s_pid, TakagiSugeno) == :ok
assert System.set_engine_type(s_pid, Mamdani) == :ok
assert System.set_engine_type(s_pid, Another) == {:error, :einval}
end
test "Setup with AST", %{ant: ant, cons: output, error: error, dt_error: dt_error} do
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 =
Rule.new(statement: r1, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule2 =
Rule.new(statement: r2, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule3 =
Rule.new(statement: r3, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule4 =
Rule.new(statement: r4, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule5 =
Rule.new(statement: r5, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule6 =
Rule.new(statement: r6, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule7 =
Rule.new(statement: r7, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule8 =
Rule.new(statement: r8, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rule9 =
Rule.new(statement: r9, consequent: output.tag, antecedent: [error.tag, dt_error.tag])
rules = [rule1, rule2, rule3, rule4, rule5, rule6, rule7, rule8, rule9]
{:ok, s_pid} = System.start_link(antecedent: ant, consequent: output, rules: rules)
output = System.compute(s_pid, [-1, -2.5])
assert Float.floor(output, 1) == -63.4
end
end