Commits

Pierre Carbonnelle  committed 970d5c7 Draft

e

  • Participants
  • Parent commits 9e9e49f

Comments (0)

Files changed (5)

File Documentation for 0.4.wiki

-This page contains documentation for the older version 0.4.  [[Documentation%20for%200.3|This other page]] is for 0.3
+This page contains documentation for the older version 0.5.  [[Documentation%20for%200.4|This other page]] is for 0.4
 
 == Why use it ? ==
 
 pyDatalog brings [[http://en.wikipedia.org/wiki/Logic_programming|logic programming]] to python.    
 
-[[http://en.wikipedia.org/wiki/Datalog|Datalog]] is a [[http://en.wikipedia.org/wiki/Turing_completeness|non-Turing-complete]] subset of Prolog that is best at :
+[[http://en.wikipedia.org/wiki/Datalog|Datalog]] is a [[http://en.wikipedia.org/wiki/Turing_completeness|non-Turing-complete]] subset of Prolog that complements python very well for:
 * managing large sets of related information (e.g. in data integration or the semantic web). 
-* simulating intelligent behavior (for games), 
+* simulating intelligent behavior (for games or expert systems), 
 * performing recursive algorithms (e.g. in network protocol, code and graph analysis) 
 
 Datalog statements can be specified in any order, eliminating the need for [[http://en.wikipedia.org/wiki/Sequence_diagram|sequence analysis]] and the associated risk of tricky errors.  Datalog programs are often shorter than their python equivalent.  
 
-== Features of version 0.4 ==
+== Features of version 0.5 ==
 
 pyDatalog embeds Datalog in python :
 * you can assert and retract facts: {{{+ p(a)}}} 
 * you can assert logic clauses of the form {{{head <= body}}}, where head is a single predicate with one or more variables, and body is a list of predicates separated by '&' : {{{p(X) <= q(X) & R(X,Y)}}} 
 * each variable in the head must also appear in the body.  The body may include equality and comparison predicates : {{{N1 == N-1}}}, or {{{N > 0}}}
+* you can negate a predicate in the body: {{{not(p(X))}}}
 * the argument of a predicate cannot be another predicate : {{{p(q(a))}}} is not allowed
-* a predicate cannot be negated : {{{not(p(X))}}} is not allowed
 
 The depth of recursion is not limited by the stack size.
 
 
 pyDatalog is open-source (LGPL).
 
-== Tutorial of version 0.4 ==
+== Tutorial of version 0.5 ==
 
 The datalog engine store facts and clauses.  The following statement inserts the fact that Bill is the parent of John Adams:
 
 """)
 }}}
 
-The following example illustrates that the depth of recursion is not limited.
+The following example illustrates the fact that predicate can be negated and that the depth of recursion is not limited.
 
 {{{
 #!python
 pyDatalog.load("""
     + even('0')
     even(N) <= (N > 0) & (N1==N-1) & odd(N1)
-    odd(N) <= (N > 0) & (N2==N-1) & even(N2)
+    odd(N) <= (N > 0) & ~ even (N)
 """)
 print(pyDatalog.ask('even(2000)')) # prints a set with one element: the ('2000',) tuple
 }}}
 
 
-== Reference for version 0.4 ==
+== Reference for version 0.5 ==
 === Methods and class ===
 
 The pyDatalog module has the following functions :
 
 In theory, a code string can contain any python code, as defined in the [[http://docs.python.org/reference/grammar.html|official grammar of Python]]. However, function and variable names that do not start with a '_' are considered Datalog symbols, have special meaning, and should appear only in statements that follow this subset of the python grammar :
 
-{{grammar0.4.png|grammar}}
+{{grammar0.5.png|grammar}}
 
 The terminal symbols in this grammar are defined in [[http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form|BNF]] as follows :
 
 == Table of Contents ==
 * Why use it ?
 * Features
-* Tutorial
+* Example
+* [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Tutorial 1 0.6|Tutorial 1]] - Datalog
+* [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Tutorial 2 0.6|Tutorial 2]] - Datalog in python
 * [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Python%20prolog%20and%20datalog|Python prolog and datalog]]
 * [[Reference]]
 * [[Installation]]
 * [[https://bitbucket.org/account/notifications/send/?receiver=pcarbonn|Support]]
 * [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Roadmap%20and%20change%20log|Roadmap and change log]]
-* [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Documentation%20for%200.3|Documentation for older versions]]
+* [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Documentation%20for%200.5|Documentation for older versions]]
 
 == Why use it ? ==
 
-pyDatalog brings [[http://en.wikipedia.org/wiki/Logic_programming|logic programming]] to python.    
+pyDatalog embeds [[http://en.wikipedia.org/wiki/Logic_programming|logic programming]] in python.  
+It is inspired by [[http://www.datalog20.org/slides/aref.pdf|LogicBlox]].
 
-[[http://en.wikipedia.org/wiki/Datalog|Datalog]] is a [[http://en.wikipedia.org/wiki/Turing_completeness|non-Turing-complete]] subset of Prolog that complements python very well for:
-* managing large sets of related information (e.g. in data integration or the semantic web). 
+[[http://en.wikipedia.org/wiki/Datalog|Datalog]] is a subset of Prolog that complements python very well for:
+* querying large sets of related information (e.g. in data integration or the semantic web). 
 * simulating intelligent behavior (for games or expert systems), 
 * performing recursive algorithms (e.g. in network protocol, code and graph analysis) 
 
-Datalog statements can be specified in any order, eliminating the need for [[http://en.wikipedia.org/wiki/Sequence_diagram|sequence analysis]] and the associated risk of tricky errors.  Datalog programs are often shorter than their python equivalent.  
+Datalog statements can be specified in any order, as formula in a spreadsheet, eliminating the need for [[http://en.wikipedia.org/wiki/Sequence_diagram|sequence analysis]] and the associated risk of tricky errors.  
+Datalog programs are often shorter than their python equivalent.  
 
-== Features of version 0.5 ==
+== Features of version 0.6 ==
 
-pyDatalog embeds Datalog in python :
-* you can assert and retract facts: {{{+ p(a)}}} 
-* you can assert logic clauses of the form {{{head <= body}}}, where head is a single predicate with one or more variables, and body is a list of predicates separated by '&' : {{{p(X) <= q(X) & R(X,Y)}}} 
-* each variable in the head must also appear in the body.  The body may include equality and comparison predicates : {{{N1 == N-1}}}, or {{{N > 0}}}
+pyDatalog embeds logic programming in python:
+* you can assert facts in a datalog knowledge base, and query it.
+* you can define attributes of python classes through logic clauses, and use logic queries on python objects (see example below).
+* you can use logic clauses to query relational database via [[http://sqlalchemy.org|SQLAlchemy]], the [[http://en.wikipedia.org/wiki/Object-relational_mapping|object-relational mapping]] and [[http://en.wikipedia.org/wiki/Data_access_layer|data access layer]] for python.
+
+More specifically:
+* you can assert logic clauses of the form {{{head <= body}}}, where head is a single literal with one or more variables, and body is a list of literals separated by '&' : {{{p(X) <= q(X) & R(X,Y)}}}
+* each variable in the head must also appear in the body.  The body may include equality and comparison predicates : {{{N1 == N-1}}}, or {{{N > 0}}}, and contain lambda expressions
+* you can use prefixed literal (e.g. {{{Employee.name(X,Y)}}}) to refer to python objects in logic clauses, or to run logic queries in python programs
 * you can negate a predicate in the body: {{{not(p(X))}}}
 * the argument of a predicate cannot be another predicate : {{{p(q(a))}}} is not allowed
 
-The depth of recursion is not limited by the stack size.
-
 pyDatalog is fast and lightweight:
 * it is based on the [[http://www.cs.sunysb.edu/~tswift/webpapers/jlp-95.pdf|SLG resolution]] algorithm with [[http://en.wikipedia.org/wiki/Memoization|memoization]]
+* it can run on [[http://pypy.org|pypy]] (python with Just-In-Time compiler)
 * it has less than 2 K lines of code.
 
-pyDatalog has an open architecture:
-* it runs a datalog engine written in python by default
-* it can use the [[http://www.ccs.neu.edu/home/ramsdell/tools/datalog/|datalog engine]] developed in [[http://en.wikipedia.org/wiki/Lua_(programming_language)|lua]] by [[http://www.ccs.neu.edu/home/ramsdell/papers/index.html|John D. Ramsdell]].  Lua is known for its fast, lightweight virtual machine.  This engine is executed by [[http://luajit.org/install.html|luajit]], a JIT compiler for lua, wrapped for python by [[http://pypi.python.org/pypi/lupa/|lupa]]
+The depth of recursion is not limited by the stack size.
 
 pyDatalog is open-source (LGPL).
 
-== Tutorial of version 0.5 ==
+== Example ==
 
-The datalog engine store facts and clauses.  The following statement inserts the fact that Bill is the parent of John Adams:
+Let's define an Employee class, create some objects, and run logic queries on them.
+
+1. define python class and business rules
 
 {{{
 #!python
+
 from pyDatalog import pyDatalog
 
-pyDatalog.load("""
-    + parent(bill,'John Adams') # bill is the parent of John Adams
-""")
+class Employee(pyDatalog.Mixin):   # --> Employee inherits the pyDatalog capability to use logic clauses
+    
+    def __init__(self, name, manager, salary): # method to initialize Employee instances
+        super(Employee, self).__init__() # calls the initialization method of the Mixin class
+        self.name = name
+        self.manager = manager           # direct manager of the employee, or None
+        self.salary = salary             # monthly salary of the employee
+    
+    def __repr__(self): # specifies how to display an Employee
+        return self.name
+
+    @pyDatalog.program() # indicates that the following method contains pyDatalog clauses
+    def _():
+        # the salary class N of employee X is computed as a function of his/her salary
+        Employee.salary_class(X,N) <= Employee.salary(X,N1) & (N==N1//1000)
+        
+        # all the indirect managers Y of employee X are derived from his manager, recursively
+        Employee.indirect_manager(X,Y) <= Employee.manager(X,Y)
+        Employee.indirect_manager(X,Y) <= Employee.manager(X,Z) & Employee.indirect_manager(Z,Y)
 }}}
-Note that the unquoted names must start with lower cases, and that quotes must be used for string with spaces or starting with an uppercase letter.  Comments, line continuation and constant strings adhere to the [[http://docs.python.org/reference/lexical_analysis.html|lexical rules of python]].
 
-You can now query our database of facts :
+2. create python objects
+
 {{{
 #!python
 
-print(pyDatalog.ask('parent(bill,X)')) # prints a set with one element : the ('bill', 'John Adams') tuple
+John = Employee('John', None, 6800)
+Mary = Employee('Mary', John, 6300)
 }}}
 
-Note that variables in the query start with an upper case, as is customary in prolog.
+3. Query the objects using the datalog engine
 
-Logic clauses make the engine smarter.  The following program explains recursively when X is an ancestor of Y : either X is the parent of Y, or X is the parent of a person Z who is an ancestor of Y.  Although it could be entered with the load function we just used, it can be easier to create a pyDatalog program, as follows:  
+The following python statements implicitly use the datalog clauses in the class definition.  
+Notice the similarity to a pyDatalog query.
+
 {{{
 #!python
 
-# specify what an ancestor is
-pyDatalog.load("""
-    ancestor(X,Y) <= parent(X,Y)
-    ancestor(X,Y) <= parent(X,Z) & ancestor(Z,Y)
-""")
-print(pyDatalog.ask('ancestor(bill, X)')) # prints a set with one element: the ('bill', 'John Adams') tuple
+# who has a salary of 6300 ?
+X = pyDatalog.Variable()
+Employee.salary(X, 6300)
+print(X) # prints (Mary,)
+
+# what is the salary class of Mary ?
+Employee.salary_class(Mary, X)
+print(X) # prints (6,)
+
+# Who are the employees with a salary class of 6 ?
+Employee.salary_class(X, 6)
+print(X) # prints (John, Mary)
+
+# who are the indirect managers of Mary (recursively) ?
+Employee.indirect_manager(Mary, X)
+print(X) # prints (John,)
+
 }}}
 
-The following example illustrates the use of formula.
-{{{
-#!python
-# use expressions and recursion to evaluate factorials
-pyDatalog.load("""
-    factorial(N, F) <= (N < 2) & (F==1)
-    factorial(N, F) <= (N > 1) & (N1 == N-1) & factorial(N1, F1) & (F == N*F1)
-""")
-}}}
-Expressions can use the 4 operators (+,-,*,/).  Note that :
-* equality/comparison predicates must be placed between parenthesis
-* the left hand side of the equality/comparison must be a variable
-* the variables on the right hand side must be [[http://en.wikipedia.org/wiki/Free_variables_and_bound_variables|bound]] (pyDatalog does not solve equations !)
+[[https://bitbucket.org/pcarbonn/pydatalog/src/5b2737c709ee/pyDatalog/_example%20SQLAlchemy.py|This similar example]] shows how to write a similar program to add business logic to a relational database using SQLAlchemy.
+[[https://bitbucket.org/pcarbonn/pydatalog/src/5b2737c709ee/pyDatalog/_example%20datalog.py|This one]] is using a pure datalog knowledge base.  These modes can be mixed freely.
 
-You can use lambda functions in expression. For example:
-{{{
-#!python
-
-# specify how a string can be split, reversibly
-pyDatalog.load("""
-    split(X, Y,Z) <= (X == Y+'-'+Z)
-    split(X, Y,Z) <= (Y == (lambda X: X.split('-')[0])) & (Z == (lambda X: X.split('-')[1]))
-""")
-}}}
-
-The following example illustrates the fact that predicate can be negated and that the depth of recursion is not limited.
-
-{{{
-#!python
-# unlimited depth of recursion
-pyDatalog.load("""
-    + even('0')
-    even(N) <= (N > 0) & (N1==N-1) & odd(N1)
-    odd(N) <= (N > 0) & ~ even (N)
-""")
-print(pyDatalog.ask('even(2000)')) # prints a set with one element: the ('2000',) tuple
-}}}
-
+See the [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Tutorial 1 0.6|Tutorial 1]] for more explanations.
 [[Home|Return to top]]

File Installation.wiki

-== Basic Installation ==
+== Installation ==
+* [[http://www.python.org/getit/|install python]]
 * install [[http://pypi.python.org/pypi/pyDatalog/|pyDatalog from pypi]]
+** on Windows : download and run the Ms Windows Installer file
+** on other platforms: download, unpack and "python setup.py install" it
 
 To verify installation, start python IDLE, then type {{{from pyDatalog import pyDatalog}}}.  
 
 
 If you get an error about "six", download it [[http://pypi.python.org/pypi/six/|from pypi]]. 
 
-== Advanced : Installation of the Datalog engine written in lua ==
-This faster engine is based on [[http://docs.python.org/install/index.html|C extensions of python]].  
-
-* install a C compiler for your platform 
-** for Windows, Visual C++ [[http://www.microsoft.com/Express/VC/|Express Edition]] is recommended, others include [[http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspx|Windows SDK]], [[http://www.mingw.org/wiki/InstallationHOWTOforMinGW|minGW]] or [[http://www.cygwin.com/install.html|CygWin]]
-* download and unpack [[http://pypi.python.org/pypi/lupa/|lupa]]
-* download and unpack [[http://luajit.org/install.html|luajit]] in a subdirectory of lupa, and build it
-* build and install lupa
-* finally, install pyDatalog from [[http://pypi.python.org/pypi|pypi]]
-** you may need to download [[https://bitbucket.org/pcarbonn/pydatalog/src/cb5983544b48/pyDatalog/pyDatalog.lua|this file]] too.
-
-I found these resources helpful in resolving problems on Windows :
-
-* [[http://sourceforge.net/p/safelua/wiki/LuaJIT%20binaries/|luaJIT binaries]] (at your own risk)
-* [[http://blog.eddsn.com/2010/05/unable-to-find-vcvarsall-bat/|solution to a Vcvarsall problem]], [[http://stackoverflow.com/questions/8531983/easy-install-u-cython-fails-complaining-about-vcvarsall-bat-and-mno-cygwin|and another]]
-* [[http://www.blitzbasic.com/Community/posts.php?topic=89186|hack when lupa can't find luajit2]]

File Reference.wiki

-== Reference for version 0.5 ==
+== Reference for version 0.6 ==
 === Methods and class ===
 
 The pyDatalog module has the following functions :
 * **load**(code) : where code is a string containing code, as described in the section below. This method is used to add facts and clauses to the default datalog engine.
+* **assert_fact**(predicate_name, *terms) : asserts "predicate_name(terms[0], terms[1], ...)"
 * **ask**(query) : where query is a string containing a literal, i.e. a predicate with variable and/or constant terms. It asks the default datalog engine to return a set of tuples containing the possible answers.  The length of each tuple is the same as the [[http://en.wikipedia.org/wiki/Arity|arity]] of the predicate.
 * **clear**() : removes facts and clauses from the default datalog engine.
-* **program**(datalog_engine) : a decorator that asks datalog_engine to load the code contained in the decorated function as a Datalog program (see advanced topics below).  The datalog_engine parameter is only relevant when [[https://bitbucket.org/pcarbonn/pydatalog/wiki/Installation|the lua engine has been installed]].  If no datalog_engine is provided, it uses the default one.  
-
-The pyDatalog module also has a **Datalog_engine class**.  The python engine can have only one Datalog_engine instance, while the lua engine can have many.  Instances of the Datalog_engine class have the following methods:
-* **load**(code) where code is a string containing code, as described in the section below. This method is used to add facts and clauses to the datalog engine.
-* **ask**(query, _fast=boolean) where query is a string containing a predicate with only variable terms (see below), and _fast is a boolean that requests faster execution (with a risk of stack overflow in case of deep recursion). It returns a set of tuples containing the possible answers.  The length of each tuple is the same as the [[http://en.wikipedia.org/wiki/Arity|arity]] of the predicate.
-* **clear**() : removes facts and clauses from the datalog engine.
+* **program**() : a [[http://docs.python.org/glossary.html#term-decorator|function decorator]]
+ that loads the code contained in the decorated function as a Datalog program (see advanced topics below).
 
 === Grammar ===
 
-In theory, a code string can contain any python code, as defined in the [[http://docs.python.org/reference/grammar.html|official grammar of Python]]. However, function and variable names that do not start with a '_' are considered Datalog symbols, have special meaning, and should appear only in statements that follow this subset of the python grammar :
+In theory, a code string can contain any python code, 
+as defined in the [[http://docs.python.org/reference/grammar.html|official grammar of Python]].
+ However, function and variable names (that are not reserved by python) are considered Datalog symbols, have special meaning, 
+ and should appear only in statements that follow this subset of the python grammar :
 
-{{grammar0.5.png|grammar}}
+{{grammar0.6 a.png|grammar}}
+{{grammar0.6 b.png|grammar}}
 
 The terminal symbols in this grammar are defined in [[http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form|BNF]] as follows :
 
-* predicate ::= [a-f] [0-9a-fA-F_]*
-* constant ::= [a-f] [0-9a-fA-F_]* | ["'] <any char> ["']
+* predicate ::= [a-fA-F] [0-9a-fA-F_]*
+* constant ::= [a-f] [0-9a-fA-F_]* | [[http://docs.python.org/reference/lexical_analysis.html#literals|python literals]]
 * variable ::= [A-F] [0-9a-fA-F_]*
-* python_variable ::= '_' [0-9a-fA-F_]*
+* python_variable ::= [a-fA-F] [0-9a-fA-F_]*
 
-=== Advanced topic - python variables ===
-You can use python variables and [[http://docs.python.org/library/functions.html|builtin functions]] in the datalog program, e.g. to bulk-load facts:
+When loading a datalog program, a symbol can become a python variable.  For example,
 {{{
 #!python
-# bulk-load parents
-_parents = (('edward', 'albert'), ('edward', 'victoria'))
+from pyDatalog import pyDatalog
 
-@pyDatalog.program(datalog_engine)
-def _(): 
-    for _parent in _parents:
-        + parent(_parent[0], unicode(_parent[1]))
+pyDatalog.load('+a(i)')
+@pyDatalog.program()
+def _():
+    for i in range(3):
+        + b(i)
+    
+print(pyDatalog.ask("a('i')")) # prints a set with 1 element : the ('i',) tuple
+print(pyDatalog.ask("b(1)")) # prints a set with 1 element : the (1,) tuple
 }}}
-
-The second line is a [[http://docs.python.org/glossary.html#term-decorator|function decorator]] that says that the next function is a datalog program : it activates the pyDatalog syntax.  Note that python variables must be prefixed with {{{_}}}, to distinguish them from datalog symbols, and that external variables (e.g. _parents) must be global.
-
-Python variables are evaluated when the datalog program is loaded, not at the time a query is made.  They must be either local or global (i.e. not defined in a calling function)
+The {{{for}}} loop assigns an integer to i, which is inserted as a constant in {{{+ b(i)}}}.

File Roadmap and change log.wiki

 == Roadmap ==
 As a general rule, one goal is to support the datalog capabilities of [[http://www.datalog20.org/slides/aref.pdf|LogicBlox]].  
 
-Another goal is to be able to mix logic clauses and queries in python, as illustrated in this example:
-{{{
-#!python
-class Employee(pyDatalog.register): # --> Employee inherits pyDatalog capability to use logic clauses
-    def __init__(self, name, manager, salary):
-        self.name = name
-        self.manager = manager # direct manager of the employee, or None
-        self.salary = salary # monthly salary of the employee
-    def __str__(self): # specifies how to display the employee
-        return self.name
-
-    @pyDatalog.program() # --> the following function contains pyDatalog clauses
-    def _():
-        # the salary class of employee X is computed as a function of his/her salary
-        (Employee.salary_class[X]==N) <= (Employee.salary[X]==N1) & (N==int(N1/1000))
-        
-        # all the indirect managers of employee X are derived from his manager, recursively
-        Employee.indirect_manager(X,Y) <= (Employee.manager[X]==Y)
-        Employee.indirect_manager(X,Y) <= (Employee.manager[X]==Z) & Employee.indirect_manager(Z,Y)
-
-# create 2 employees
-John = Employee('John', None, 6800)
-Mary = Employee('Mary', John, 6300)
-
-# the following python statements implicitly use the datalog clauses
-
-# what is the salary class of John ?
-print(John.salary_class())
-
-# who are the indirect managers of Mary ?
-X=[]
-Mary.indirect_manager(X) # notice the similarity to a pyDatalog clause
-Print(X) # prints (John,)
-
-# Who are the employees with a salary class of 6 ?
-X=[]
-Employee.salary_class(X, 6)
-Print(X) # prints John, Mary
-}}}
-
 Feel free to [[https://bitbucket.org/account/notifications/send/?receiver=pcarbonn|contact me]] for other suggestions.
 
 To do:
-* support of object for terms
-* test compatibility with [[http://pypy.org/|pypy]] and its Just-In-Time compiler
-* debug / trace mode
-* interactive shell
+* calculated class attributes
+* support of function, i.e. predicate with unicity (e.g. father[x] == v is equivalent to father(x,v) with unicity per x)
+* support active clauses to automatically insert facts, à la [[http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.21.1126|statelog]] : {{{ + head <= body}}}
+* support for aggregation predicate (sum, ...)
 * custom predicates written in python
-* use [[http://www.sqlalchemy.org/|sqlalchemy]] for fact database
-* support of function, i.e. predicate with unicity (e.g. father[x] == v is equivalent to father(x,v) with unicity per x)
-* support for aggregation predicate (sum, ...)
-* support active clauses to automatically insert facts, à la [[http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.21.1126|statelog]] : {{{ + head <= body}}}
 * support anonymous variable '_'
 * support zipped results from lambda
+* interactive shell
 * support verbalization of literal : {{{odd(X) == '... is an odd number'}}}
 
 == Change log==
 Latest bitbucket version : see [[https://bitbucket.org/pcarbonn/pydatalog/changesets|commits]]
 
-0.5.0 - 20 June 2012:
+0.6.0 - 
+* use the just-in-time compiler of pypy instead of lupa's.  Stop development on lua engine 
+* support python objects in literals (python engine only)
+* support query of relational database via SQLAlchemy
+* support trace mode
+* Do not accept python variables (starting with _) in pyDatalog.program anymore
+* Equality predicate now accepts that both sides are bound
+* Accept upper case for predicate initials
+* new assert_fact() accepts python objects for terms
+
+[[https://bitbucket.org/pcarbonn/pydatalog/wiki/Documentation%20for%200.5|0.5.0]] - 20 June 2012:
 * support for negation
 
-0.4.0 - 3 May 2012:
+[[https://bitbucket.org/pcarbonn/pydatalog/wiki/Documentation%20for%200.4|0.4.0]] - 3 May 2012:
 * support multi-line strings in pyDatalog.load()
 * go beyond 4 operators in expressions : use lambda
 
 * index the database of facts for improved performance
 * make pyDatalog.clear() work
 
-0.3.0 - 25 Apr 2012:
+[[https://bitbucket.org/pcarbonn/pydatalog/wiki/Documentation%20for%200.3|0.3.0]] - 25 Apr 2012:
 * add the datalog engine written in python. Use it by default
 * use numeric type in results (instead of string types)
 * tested with python 2.7 and 3.2
 
-0.2.1 : 16 Apr 2012
+[[[[https://bitbucket.org/pcarbonn/pydatalog/wiki/Documentation%20for%200.2|0.2.1]] : 16 Apr 2012
 * rename "execute()" into "load()"
 * avoid stack overflow due to deep recursion
 * propose default datalog engine in the decorator, and add pyDatalog.ask(code), 
 0.1.1 : 13 Apr 2012
 * add links to download in Pypi
 
-0.1.0 : first release on Pypi
+[[https://bitbucket.org/pcarbonn/pydatalog/wiki/Documentation%20for%200.1|0.1.0]] : first release on Pypi