View Source Wongi.Engine (Wongi.Engine v0.9.13)
This is a pure-Elixir forward chaining inference engine based on the classic Rete algorithm.
It's derived from an earlier Ruby library and has a similar interface.
Usage
The following examples will assume a prelude of
import Wongi.Engine
import Wongi.Engine.DSL
which comprises the entire public interface of the library.
First, an engine instance needs to be created:
engine = new()
Knowledge management
All knowledge in the system is represented as a set of triples in the form of
{subject, predicate, object}
. Any complex properties or relationships
between entities can be broken down to this form, in which case the
relationship members will take on the role of subjects and objects, and the
type of the relationship will be the predicate.
Any Elixir term except the atom :_
can be used as any element of a triple,
although predicates naturally tend to be atoms.
References can be used to naturally represent anonymous graph nodes.
Facts are added into the system with assert/2
or assert/4
:
engine = engine |> assert(:earth, :satellite, :moon)
# or
engine = engine |> assert({:earth, :satellite, :moon})
# or
engine = engine |> assert([:earth, :satellite, :moon])
# or
engine = engine |> assert(WME.new(:earth, :satellite, :moon))
WME (working memory element) is the standard Rete term for "fact". You would
rarely need to construct Wongi.Engine.WME
instances by hand, but you might
retrieve them from the engine and use in further function calls.
Similarly, retract/2
or retract/4
remove facts from the system.
Searching
select/2
and select/4
can be used to return a set of facts matching a
template. A template is a triple where some of the elements can be the special
placeholder value :_
.
An enumerable of all facts matching the template is returned:
[fact] =
engine
|> select(:earth, :satellite, :_)
|> Enum.to_list()
IO.inspect(fact.object)
# => :moon
Rules
Rules allow expressing more complex conditions than a single template.
A rule is constructed like this:
rule = rule("optional name", forall: [
matcher1,
matcher2,
...
])
IO.inspect(rule.ref)
# => #Reference<...>
The ref
field is going to be used later to retrieve the results of rule
execution.
The rule can then be installed into the engine:
engine = engine |> compile(rule)
Alternatively, this form can be used if you don't want an intermediate variable for the rule, although it is less pipeable:
{engine, ref} =
engine
|> compile_and_get_ref(rule(forall: [...]))
The forall
section of a rule consists of a list of matchers (more fully
documented in Wongi.Engine.DSL
) that express some sort of condition. The
simplest matcher is Wongi.Engine.DSL.has/3
which passes if a fact matching
its template exists.
A crucial part of matching is the variable bindings. A variable is specified
using Wongi.Engine.DSL.var/1
. The first time a variable is encountered, it
is bound to the matched value. Subsequent matches will only succeed if the
value is the same as the initially bound one.
:_
can be used as a placeholder variable that matches anything and is not
bound to any value.
rule = rule(forall: [
has(:_, :satellite, var(:satellite)),
has(var(:satellite), :mass, var(:mass))
])
engine =
new()
|> compile(rule)
|> assert(:earth, :satellite, :moon)
|> assert(:moon, :mass, 7.34767309e22)
The results of rule execution can be retrieved using tokens/2
, which returns
an enumerable. A token represents a single possible execution of the matcher
sequence. Our set of facts satisfies the rule exactly once, so we expect
exactly one token. The bound variables can then be inspected on it:
[token] = engine |> tokens(rule.ref) |> Enum.to_list()
IO.inspect(token[:satellite])
# => :moon
IO.inspect(token[:mass])
# => 7.34767309e22
Generation
In addition to passively examining the results, it is also possible for a rule to perform some actions when it is fully satisfied. Generating additional facts is one such action.
For example, we can add a rule that generates a fact about the gravitational pull on the satellite:
rule =
rule(
forall: [
has(var(:planet), :satellite, var(:satellite)),
has(var(:planet), :mass, var(:planet_mass)),
has(var(:satellite), :mass, var(:sat_mass)),
has(var(:satellite), :distance, var(:distance)),
assign(:pull, &(6.674e-11 * &1[:sat_mass] * &1[:planet_mass] / :math.pow(&1[:distance], 2)))
],
do: [
gen(var(:satellite), :pull, var(:pull))
]
)
engine =
new()
|> compile(rule)
|> assert(:earth, :satellite, :moon)
|> assert(:earth, :mass, 5.972e24)
|> assert(:moon, :mass, 7.34767309e22)
|> assert(:moon, :distance, 384_400.0e3)
[wme] = engine |> select(:moon, :pull, :_) |> Enum.to_list()
IO.inspect(wme.object)
# => 1.9819334566450407e20
The generated facts keep track of the rule that generated them and get automatically retracted if the conditions are no longer satisfied.
If a fact has been generated by a rule and also asserted manually, it also needs to be retracted by both means to be removed from the system.
Summary
Functions
Returns an engine with the given fact added to the working memory.
Returns an engine with the given fact added to the working memory.
Returns an engine with the given rule installed.
Returns an engine with the given rule installed and the rule reference.
Creates a new engine instance.
Returns all production node references.
Returns an engine with the given fact removed from the working memory.
Returns an engine with the given fact removed from the working memory.
Returns a set of all facts matching the given template.
Returns a set of all facts matching the given template.
Returns a set of all tokens for the given production node reference.
Types
@type rule() :: Wongi.Engine.DSL.Rule.t()
@type t() :: Wongi.Engine.Rete.t()
@type wme() :: Wongi.Engine.WME.t()
Functions
Returns an engine with the given fact added to the working memory.
Returns an engine with the given fact added to the working memory.
Returns an engine with the given rule installed.
See Wongi.Engine.DSL
for details on the rule definition DSL.
Returns an engine with the given rule installed and the rule reference.
The rule reference can be used to retrieve production tokens using tokens/2
.
See Wongi.Engine.DSL
for details on the rule definition DSL.
@spec new() :: t()
Creates a new engine instance.
Returns all production node references.
Returns an engine with the given fact removed from the working memory.
Returns an engine with the given fact removed from the working memory.
Returns a set of all facts matching the given template.
Returns a set of all facts matching the given template.
@spec tokens(t(), reference()) :: MapSet.t(Wongi.Engine.Token.t())
Returns a set of all tokens for the given production node reference.