"The Rete Match Algorithm is an efficient method for comparing a large collection of patterns to a large collection of objects. It finds all the objects that match each pattern. The algorithm was developed for use in production system interpreters, and it has been used for systems containing from a few hundred to more than a thousand patterns and objects" - C. Forgy
Boilerplate/PoC of a version of the Rete Algorithm implementated in Elixir
Rete is a complex stateful algorithm, this is an attempt of reproducing it with some slight modifications, using a functional immutable language such as Elixir/Erlang. Read more about Rete
- Erlang/OTP 22
- Elixir 1.12.1 (compiled with Erlang/OTP 22)
- Retex compiles the rules using a directed acyclic graph data structure
- The activation of nodes is done using a State Monad and Forward Chaining.
- A list of bindings is stored at each active node in order to generate complete matches from partial ones
When compiled, the network will look something like the following:
def deps do
[
{:retex, git: "https://github.com/lorenzosinisi/retex"}
]
end
If you want you can have a predefined generic DSL and the wrapper NeuralBridge so that you don't have to build the rest of the Expert System from zero
def deps do
[
{:neural_bridge, git: "https://github.com/lorenzosinisi/neural_bridge"}
]
end
alias NeuralBridge.{Engine, Rule}
engine = Engine.new("test")
rules = [
Rule.new(
id: 1,
given: """
Person's name is equal "bob"
""",
then: """
Person's age is 23
"""
),
Rule.new(
id: 2,
given: """
Person's name is equal $name
Person's age is equal 23
""",
then: fn production ->
require Logger
bindings = Map.get(production, :bindings)
Logger.info(inspect(bindings))
end
)
]
engine = Engine.add_rules(engine, rules)
engine = Engine.add_facts(engine, "Person's name is \"bob\"")
rule = List.first(engine.rule_engine.agenda)
engine = Engine.apply_rule(engine, rule)
Enum.each(engine.rule_engine.agenda, fn pnode ->
Engine.apply_rule(engine, pnode)
end)
end # will log %{"$name" => "bob"}
alias NeuralBridge.{Engine, Rule}
engine = Engine.new("doctor_AI")
engine =
Engine.add_rules(engine, [
Rule.new(
id: 1,
given: """
Patient's fever is greater 38.5
Patient's name is equal $name
Patient's generic_weakness is equal "Yes"
""",
then: """
Patient's diagnosis is "flu"
"""
),
Rule.new(
id: 2,
given: """
Patient's fever is lesser 38.5
Patient's name is equal $name
Patient's generic_weakness is equal "No"
""",
then: """
Patient's diagnosis is "all good"
"""
)
])
engine =
Engine.add_facts(engine, """
Patient's fever is 39
Patient's name is "Aylon"
Patient's generic_weakness is "Yes"
""")
## contains Patient's diagnnosis
[
%_{
action: [
%Retex.Wme{
identifier: "Patient",
attribute: "diagnosis",
value: "flu"
}
],
bindings: %{"$name" => "Aylon"}
}
] = engine.rule_engine.agenda
- Run
mix test
- Run
elixir benchmark/rule_chain.exs
- Use at your own risk
- This is just a template for complexer implementations of the described algorithms
## For more on Rete algorithm