You can use the Python input() function (input_raw() in IPython) to request data from the user :
pyDatalog.create_terms('X,Y, quacks, input')quacks(X) <= (input('Does a '+X+' quack ? (Y/N) ')=='Y')Please note that the prompt string contains a variable, X. Without it, e.g. in (X==input('Enter your name : '), the user would be prompted when the clause is declared, not when it is used during query evaluation.
In Python 3, you can use the print() function to show a result on the console:
pyDatalog.create_terms('print')ok(X) <= (0 < X) & (Y==print(X))In Python 2, print is a statement, not a function. So, you would need to create your own print_() function:
def print_(x): print(x)pyDatalog.create_terms('print_')ok(X) <= (0 < X) & (Y==print_(X))To get a full trace of pyDatalog's reasoning, add the following instructions before a query:
import loggingfrom pyDatalog import pyEnginepyEngine.Logging = Truelogging.basicConfig(level=logging.INFO)
A Python program may start several threads. Each thread should have these statements to initialize pyDatalog :
from pyDatalog import pyDatalog, LogicLogic() # initializes the pyDatalog engine
Each thread can then define its own set of clauses, and run queries against them. See the ThreadSafe.py example.
Alternatively, threads can share the same set of clauses, by following these steps :
Logic(True) object to the threads it creates. Logic(arg), where arg is the Logic(True) object it received from the calling thread.Please note that, once passed, the set of clauses should not be changed (such changes are not thread safe).
Finally, a program may switch from one set of clauses to another :
Logic() # creates an empty set of clauses for use in the current thread# add first set of clauses herefirst = Logic(True) # save the current set of clauses in variable 'first'Logic() # first is not affected by this statement# define the second set of clauses heresecond = Logic(True) # save it for later useLogic(first) # now use first in the current thread# queries will now run against the first set of rulesSee the Multi-Model.py example for illustration.
Some applications need to construct datalog statements dynamically, i.e. at run-time. The following interface can then be used.
A Python program can assert fact in the in-memory Datalog database using assert_fact(). The first argument is the name of the predicate :
from pyDatalog.pyDatalog import assert_fact, load, ask# + parent(bill, 'John Adams')assert_fact('parent', 'bill','John Adams') A python program calls the load function to add dynamic clauses :
# specify what an ancestor isload(""" ancestor(X,Y) <= parent(X,Y) ancestor(X,Y) <= parent(X,Z) & ancestor(Z,Y)""")It can now query the database of facts :
# prints a set with one element : the ('bill', 'John Adams') tupleprint(ask('parent(bill,X)')) A predicate such as p(X,Y) or (a.p[X]==Y) can be resolved using a custom-made python method, instead of using logical clauses. Such python resolvers can be used to implement connectors to non-relational databases, or to improve the speed of specific clauses. It has precedence over (and replaces) any clauses in the pyDatalog knowledge base.
Unprefixed resolvers
An unprefixed resolver is used to resolve an unprefixed predicate. It is defined using the @pyDatalog.predicate() decorator, and by adding the arity to the predicate name:
@pyDatalog.predicate()def p2(X,Y): yield (1,2) yield (2,3)print(pyDatalog.ask('p(1,Y)')) # prints == set([(1, 2)])This simple resolver returns all possible results, which pyDatalog further filters to satisfy the query. Often, the resolver will use the value of its X and Y arguments to return a smaller, (if possible exact) set of results. Each argument has a .is_const() method : it returns True if it is a constant, and False if it is an unbound variable. If it is a constant, the value can be obtained with .id (e.g. X.id).
Prefixed resolvers
A prefixed resolver is defined in a class. The following resolver is equivalent, and replaces, Employee.salary_class[X] = Employee.salary[X]//1000.
class Employee(pyDatalog.Mixin): @classmethod def _pyD_salary_class2(cls, X, Y): if X.is_const(): yield (X.id, X.id.salary//1000) else: for X in pyDatalog.metaMixin.__refs__[cls]: Y1 = X.salary // 1000 if not Y.is_const() or Y1==Y.id: yield (X, Y1)This method will be called to resolve Employee.salary_class(X,Y) and (Employee.salary_class[X]==Y) queries. Each resolver must have the same number of arguments as the arity of its predicate. Note that the arity is appended to the end of the method name.
The list of instances in a class cls is available in pyDatalog.metaMixin.__refs__[cls] . For SQLAlchemy classes, queries can be run on cls.session.
A class can also have (or inherit) a generic resolver, called _pyD_query. Its arguments are the predicate name and its arguments.
# generic resolverclass Employee(pyDatalog.Mixin): @classmethod def _pyD_query(cls, pred_name, args): if pred_name == ' Employee.salary_class':
if args[0].is_const(): yield (args[0].id, args[0].id.salary//1000) else: for X in pyDatalog.metaMixin.__refs__[cls]: Y1 = X.salary // 1000 yield (X,Y1)
else: raise AttributeErrorThe best performance is obtained with pypy, the JIT compiler of python. Please note that:
A pyDatalog program will never beat the same program written in pure python, but:
Performance tip: