Alda includes an extension of DistAlgo with rules. This implementation supports Python 3.7 but can also run with Python 3.8 and 3.9.
To use the extension with rules, install XSB:
- download XSB from https://sourceforge.net/projects/xsb --- click "Download" for Windows, or download from "Source(GIT)" for Unix,
- run the downloaded .exe file for Windows, or follow the instructions in Section 2.1 in the manual (just paragraph 1, items 1 and 4) for Unix, and
- add the path for the XSB executable to the
PATHenvironment variable.
Consider computing the transitive closure of edges in a graph. Given a graph with a set of edges, each from a vertex to another, the transitive closure is the set of all pairs of vertices such that there is a path from the first vertex to the second vertex following a sequence of edges.
The following Alda program computes the transitive closure of the set e of edges. Given predicate edge that asserts whether there is an edge from a first vertex to a second vertex, rule set trans_rules defines predicate path that asserts whether there is a path from a first vertex to a second vertex by following the given edges.
def rules(name=trans_rules):
path(x,y), if_(edge(x,y))
path(x,y), if_(edge(x,z), path(z,y))
e = {(7,2), (7,3), (2,3), (2,4), (7,5), (3,6)}
print(infer(rules=trans_rules, bindings=[('edge',e)], queries=['path']))The first rule says that there is a path from x to y if there is an edge from x to y. The second rule says that there is a path from x to y if there is an edge from x to z and there is a path from z to y.
Function infer uses rule set trans_rules, takes edge to be true for edges in e, and returns the set of pairs for which path is true.
A set of rules can be specified using a rule set of the following form:
def rules(name=rsname):
rule+rsname is the name of the rule set and uses the same naming convention as Python identifiers; name= is optional. rule+ is one or more rules.
Each rule is of the following form, meaning that if hypothesis_1 through hypothesis_n all hold, then conclusion holds:
conclusion, if_(hypothesis_1, hypothesis_2, ..., hypothesis_n)Each hypothesis and conclusion in a rule is an assertion of the following form, possibly preceded with not:
pred(arg_1,...,arg_m)predis a predicate. A predicate can be a global variable, object field, or local variable of a rule set`.- Each
arg_kis a variable, a constant, or a wildcard_meaning the argument can be any value. A constant can be an integers,None,True,False, or a quoted string. All variables in the conclusion must be in a hypothesis.
In a rule set, predicates not in any conclusion are called base predicates of the rule set, and the other predicates are called derived predicates of the rule set.
One can do inference using rules by calling a built-in inference function infer, of the following form
infer(rules=rsname,
bindings=[('pred_1',sexp_1),...,('pred_i',sexp_i)],
queries=['query_1',...,'query_j'])rsnameis the name of a rule set.bindingsis an optional keyword argument that specifies the assignment to each base predicatepred_kin the rule set with the value of a set-valued expressionsexp_k. Each predicatepred_kneeds to be a quoted string. Ifpred_kis not local to rule setrsname, the assignment topred_kcan be omitted.queriesis an optional keyword argument that specifies the queries to be made. Eachquery_kneeds to be a quoted string of the formpred(arg_1,...,arg_m), wherepredis a predicate in rule setrsnameandarg_kis a constant or a wildcard_; the value of a variable or generally an expressionecan be used to constrain a query argument by constructing a query string withstr(e)for the corresponding argument. Wildcard_represents a distinct variable. Querypred(_,...,_)can be abbreviated aspred. Whenqueriesis omitted,infertreats all derived predicates as queries.- Return value: for each value
kfrom 1 toj,inferreturns the result ofquery_kas thekth component of the return value. The result of a query withldistinct variables that are not constants is a set of tuples oflcomponents, one for each of the distinct variables in their order of first occurrence in the query. Whenlequals 1, the set of tuples is reduced to a set of elements in the tuples.
When using rules with only non-local predicates, the derived predicates are automatically updated when any of the base predicate is updated, without explicit calls to infer.
Usage: python -m da --rules <filename>
where <filename> is the name of an Alda program that uses rules.
Save the example program to a file, say named trans.da. Then running the command
>>> python -m da --rules trans.da
produces the output
{(7, 4), (2, 4), (7, 3), (2, 3), (7, 6), (2, 6), (7, 2), (3, 6), (7, 5)}