Try the all-new QuickBooks Online for FREE.  No credit card required.


Document Sample
ps12 Powered By Docstoc
					                 Practice Session no. 12 – Prolog І І
Relational Logic Programming (RPL) as a Database Language

RPL – Relational Logic Programming – programs specify relations among entities.
A relational LP program is a proper subset of Full LP: No function symbols.
Therefore, it cannot describe data structures; only relations.

RPL can be developed as a database language: For example, Datalog is a deductive
database language, which is RLP, with negation, and specific database inspired
Datalog consists of 2 types of components:
      Extensional DB – facts. Represent the relations (tables) in the database.
      Intentional DB – rules. Represent additional relations (views) that can be
         defined on the basis of the data in the tables (the extensional database).
      The relational algebra operations: union, Cartesian product, diff, projection,
         selection, and join can be implemented in logic programming.
We will demonstrate with an example, similar to the one given in assignment 5.

Extensional DB

The database contains the tables: employee, children and departmentBuilding.
Then the actual tuples (e.g. rows) of the tables can be represented as facts:

    % employee(ID, Name, Department, Age, Salary)/5
    % Represents the employee table.
    %     ID, Age, Salary: Type is number, identifies employee.
    %     Name, Department: Type is Symbol.
    1. employee(4356291, david, history, 32, 10000).
    2. employee(6547209, rina, biology, 45, 15000).

Programming convention:
1. Procedure definitions: All facts and rules for a specific procedure have the same
predicate name and are written as a numbered, contiguous block.
2. Every procedure definition is preceded with a signature specification, containing:
signature and arity, explanation and typing.
    % children(Name, Department, ChildName)/3
    % Represents the employees’ childrens table.
    %      Name, Department, ChildName: Type is Symbol.
    1. children(david, history, moshe).
    2. children(rina, biology, rachel).
    3. children(rina, biology, tzvi).

    % departmentBuilding(Department, Building)/2
    % Represents the association of departments to buildings.
    %     Department, Building: Type is Symbol.
    1. departmentBuilding(history, b60).
    2. departmentBuilding(biology, b25).

Intentional DB

1. Selection:

    % employee_select_by_age(ID, Name, Department, Age, Salary)/5
    % Filter employee table by age. See employee typing.
    1. employee_select_by_age(ID, Name, Department, Age, Salary):-
           employee(ID, Name, Department, Age, Salary),

    ?- employee_select_by_age(ID, Name, Department, Age, Salary).

    ID = 6547209,
    Name = rina,
    Department = biology,
    Age = 45,
    Salary = 15000 ;


What would happen if we reverse the order of the formulas in the body of the rule?

    1. employee_select_by_age(ID, Name, Department, Age, Salary):-
           employee(ID, Name, Department, Age, Salary).

 During evaluation the variable Age will not be instantiated, and this will cause a
runtime error.

    ?- employee_select_by_age(ID, Name, Department, Age, Salary).
    ERROR: >/2: Arguments are not sufficiently instantiated
    ^ Exception: (9) _G536>40 ? abort
    % Execution Aborted

Conclusion: When system predicates (language primitives) are involved, the
order of rules counts.

2. Projection:

 % employee_project(ID, Name, Age)/3
 % Project columns from employee table. See employee typing.
 1. employee_project(ID, Name, Age):-
        employee(ID, Name, Department, Age, Salary).

    -? employee_project(ID, Name, Age).

    ID = 4356291,
    Name = david,
    Age = 32;

    ID = 6547209,
    Name = rina,
    Age = 45;


% children_project(Name, Department)/3
% Project columns from children table. See children typing.
1. children_project(Name, Department):-
           children(Name, Department, ChildName).

    ?- children_project(Name, Department).

    Name = david,
    Department = history ;

    Name = rina,
    Department = biology ;

    Name = rina,
    Department = biology ;


         The tuple <rina, biology> appears twice. Because we do not explicitly
          construct the new relation as a set.

3. Cartesian production:

    % employee_X_departmentBuilding(ID, Name, EmpDepartment, Age, Salary,
                                      BuildingDepartment, Building)/7
    % Perform Cartesian production between employee and buildingDepartment tables.
    % See employee and buildingDepartment typing.
    1. employee_X_departmentBuilding(ID, Name, EmpDepartment, Age, Salary,
                                      BuildingDepartment, Building):-
           employee(ID, Name, EmpDepartment, Age, Salary),
           departmentBuilding(BuildingDepartment, Building).

Since there are 2 employees and 2 department buildings then the result will include
2*2 tuples.

4. Natural Join:

    % employee_join_departmentBuilding(ID, Name, Department, Age, Salary,
    % Join employee and buildingDepartment tables on Department attribute.
    % See employee and buildingDepartment typing.
    1. employee_join_departmentBuilding(ID, Name, Department, Age, Salary,
           employee(ID, Name, Department, Age, Salary),
           departmentBuilding(Department, Building).

The tuples of the new relation are the combined tuples of employee and
departmentBuilding tables, that have a common value for the Department attribute.

    ?- employee_join_departmentBuilding(ID, Name, Department, Age, Salary, Building).

    ID = 4356291,
    Name = david,
    Department = history,
    Age = 32,
    Salary = 10000,
    Building = b60 ;

    ID = 6547209,
    Name = rina,
    Department = biology,
    Age = 45,
    Salary = 15000,
    Building = b25 ;


Note: The SQL embedding does not provide a constructor for every SQL operation.
Instead, a specific operation application rule is written for every case. In that sense,
the Scheme SQL formulation is more powerful.

Full Logic Programming (FPL) – Adding data structures
Full logic programming – programs can specify data structures over entities.
    1. Additional constant symbol: Functor (function symbol), for creation of
        structured data. Every functor has an arity: number of arguments.

      For example, tree(Element, Left, Right) – a binary tree, with Element as the root,
      and Left and Right as its sub-trees.
      tree is a functor with arity 3.
      Note: Functors are data constructors.

      2. There is an additional expression kind: Term.
         Inductive definition:
              Basis: Individual constant symbols and variables are terms.

               Inductive step: For terms t1, …, tn, and a functor f, f(t1, …, tn) is a
      3. The arguments to the predicate symbols in an atomic formula are terms.
      For example,
          tree(5,tree(8,void,void),tree(9,void,tree(3,void,void))) is a term.
          member(X, tree(X, Left, Right)) is an atomic formula with terms as
      Note: The syntax of terms and atomic formulas is identical. The distinction is
      made by the syntactic context.
      1. Facts, rules and queries consist of atomic formulas.
      2. Arguments to a predicate symbol in an atomic formula are terms.
      3. Terms can be nested.
      4. Atomic formulas cannot be nested.

The presence of function symbols complicates the unification step in the abstract
interpreter. Recall that in every loop round, a query goal (an atomic formula) is
unified (matched) with a rule head (an atomic formula). The unification operation, if
successful, produces a substitution (most general unifier) for the variables in the
atomic formulas.

For example,
       Unify( r(p(X, 10, f(X)), W), r(p(Y,Y,Z), f(Z)) )
       yields the substitution: { X=10, Y=10, Z=f(10), W=f(f(10)) }

Note: The LP operational semantics guarantees that the two unified formulas have no
   variables in common.

Dictionaries (trees):
Finding and adding a value in a dictionary (an binary search tree)

    % lookup(Key, Tree,Val)/3
    %      Key is the key of Val in Tree
    %      Key: Type is Number.
    %      Tree: Type is key-binary-tree.
    %      Constraint: Key and the tree root key must be instantiated.
    1. lookup(Key, tree(Key,Value, Left, Right), Value).
    2. lookup(Key, tree(Key1, Value1, Left, Right), Value) :-
           Key < Key1, lookup(Key, Left, Value).
    3. lookup(Key, tree(Key1, Value1, Left, Right), Value) :-
                    Key > Key1, lookup(Key, Right, Value).

                ?- lookup(3, tree(4, 4, tree(2,2, nil, tree(3,3,nil,nil)), tree(5,5,nil,nil)), Value).

                ?- lookup(1, D, fifi), lookup(2, D, mumu), lookup(1,D, X).
                D=tree(1, fifi, _C, tree(2, mumu, _B, _A)), X=fifi.

                                                            lookup(1, D, fifi),
         Rule 1                                           lookup(2, D, mumu),
         Key_1=1,                                            lookup(1,D, X).
         D= tree(1, fifi, Left_1, Right_1)
                                              lookup(2, tree(1, fifi, Left_1, Right_1), mumu),
                                                lookup(1, tree(1, fifi, Left_1, Right_1), X).                                         Rule 3
Rule 2                                                                                                                                Key_3=2,
Key_2=2,                                                                                                                              Key1_3=1,
Key1_2=1,                                                                                                                             Value1_3=fifi,
Value1_2=fifi,                                                                                                                        Left_3= Left_1,
Left_2= Left_1,                                                                                                                       Right_3=Right_1
Right_2= Right_1                                                                                                                      Value_3=mumu
                                        2 < 1,                                                                         2 > 1,
                       lookup(2, Left_1, mumu),                                                      lookup(2, Right_1, mumu),
               lookup(1, tree(1, fifi, Left_1, Right_1), X).                                  lookup(1, tree(1, fifi, Left_1, Right_1), X).

                               FAIL          Rule 1                                                  lookup(2, _Right_1, mumu),
                                                                                               lookup(1, tree(1, fifi, Left_1, Right_1), X).
                                             Right_1 = tree(2, mumu, Left_4, Right_4)

                                                                                        lookup(1, tree(1, fifi, Left_1, tree(2, mumu, Left_4, Right_4)), X).
                                 Rule 1
                                 Left_5= Left_1,
                                 Right_5= tree(2, mumu, Left_4, Right_4)
                                 X= Value_5=fifi                                               SUCCESS
                                                                                               D= tree(1, fifi, Left_5, tree(2, mumu, Left_4, Right_4)),
                                                                                               X = fifi

                         Search tree for query ?- lookup(1, D, fifi), lookup(2, D, mumu), lookup(1, D, X).

               Note: Search trees include all other possibilities for rule selection.
               For example, in the lookup tree -- the 1st goal can trigger the 2nd and
               the 3rd rules as well.
               Prolog performs a depth first search on this tree.
               We do not draw it in full, only the first successful path.

Natural numbers – Multiplication:
Recall the definition of natural_number and plus

% natural_number (X)/1
%       X is a natural_number.
1. natural_number(0).
2. natural_number(s(X)):-natural_number(X).

% Plus(X,Y,Z)/3
%       Z is the sum of X and Y.
%       X,Y,Z: Type is natural_number.
1. plus(0, X, X) :- natural_number(X).
2. plus(s(X), Y, s(Z)) :- plus(X, Y, Z).

    % Times(X,Y,Z)/3
    %       Z = X*Y
    %       X,Y,Z: Type is natural_number.
    1. times(0, X, 0) :- natural_number(X).
    2. times(X,0,0) :- natural_number(X).
    3. times(s(X), Y, Z) :- plus(Y, Z1, Z), times(X, Y, Z1).

          Relation-oriented nature– no explicit computation direction

    ?-times(s(s(0)), s(s(s(0))), Answer).   % 2*3=6
    Answer= s(s(s(s(s(s(0)))))))

    ?-times(Number1, s(s(s(0))), s(s(s(s(s(s(0))))))).    % 6:3 = 2
    Number1= s(s(0)).

    ?-times(Number1, Number2, s(s(s(s(0))))).             % Number1 * Number2 = 4
    Returns all combinations of 2 numbers, such that their multiplication results in 4.

Concatenation of lists:

          % append(List1, List2, List3)/3
          %      List3 is the concatenation of List1 and List2.
          %      List1, list2, List3: Type is List.
          1. append( [], Xs, Xs).
          2. append( [X | Xs], Y, [X | Zs] ) :- append(Xs, Y, Zs).

?- append([a,b], [c], X).        - addition of two lists
    X = [a, b, c] ;
?- append(Xs, [a,d], [b,c,a,d]).         - finds a difference between lists
    Xs = [b, c] ;

?- append(Xs, Ys, [a,b,c,d]). - divides a list into two lists.
     For a list of length n there are n+1 answers.
     Is the search tree finite? Yes.
     Success or failure tree? Finite successful search tree: A finite tree with a
       successful path

     ?- append(Xs, Ys, Zs).
          What are the answers? All combinations of lists such that Xs and Ys are
            the two parts of Zs.
          How many answers? Infinite. The answers are not fully instantiated.

              Xs = [],
              Ys = Zs ;

              Xs = [_G396],
              Zs = [_G396|Ys] ;

              Xs = [_G396, _G402],
              Zs = [_G396, _G402|Ys] ;

              Xs = [_G396, _G402, _G408],
              Zs = [_G396, _G402, _G408|Ys]…

              Is the search tree finite? Success or failure tree? An infinite success tree.

Reversing lists:

    % reverse(List1, List2)/2
    % recursive reverse (using append in each recursion step)
    %      List2 is the reverse of List1.
    %      List1, list2: Type is List.
    1. reverse( [], [] ).
    2. reverse([X|Xs], Ys) :- reverse(Xs, Zs), append(Zs, [X], Ys).

     wrong_reverse( [], [] ).
     wrong_reverse( [H | T], R):-
        reverse(T, S), append(S, H, R).

      What is wrong with wrong_reverse?
     It will not cause run time errors, but will return the wrong structure.

    ?- wrong_reverse([1,2,3,4],R).

    R = [4, 3, 2|1]

              % reverse(List1, List2)/2 – second version
              % reverse-accumulate -- iterative. List2 is the reverse of List1.
              %      List1, list2: Type is List.
              1. reverse(Xs, Ys) :- reverse(Xs, [], Ys).

              % reverse(List1, Acc, List2)/3
              1. reverse([], Acc, Acc).
              2. reverse([X|Xs], Acc, Ys) :- reverse(Xs, [X|Acc], Ys).

                  ?- reverse([a,b,c,d], R). - R=[d,c,b,a]
                          Rule 1
                                             reverse([a,b,c,d], [], R).              Search tree for query
                          Ys_1 = R
                                                                                     ?- reverse([a,b,c,d],R).
                     Rule 2                                                           Reverse iterative version
                     X_2=a,                 reverse([b,c,d], [a | []], R).
Rule 2               Ys_2=R                 reverse([c,d], [b|[a]], R).                  Rule 2
Xs_3=[c,d],            Rule 2               reverse([d], [c,b,a], R).                    Xs_5=[],
Acc_3=[a]              X_4=c,                                                            Acc_5= [c,b,a],
Ys_3=R                 Xs_4=[d],                                                         Ys_5=R
                       Acc_4=[ b,a],        reverse([], [d,c,b,a], R).
                                                                                          Rule 1
                                            true                                          Acc_6= [d,c,b,a],
                                                     R =[d,c,b,a]

                  ?- reverse(R, [a,b,c,d]). - R=[d,c,b,a]
                   Is the search tree Finite? Successful? Failure?
                      The search tree is an infinite successful tree.
                   In which circumstances we will explore an infinite branch?
                  After finding the first successful result the tree has branches which are infinite.
                   Here is a demonstration of the behaviour, when it is traced:

     ?- trace.
      [trace] ?- reverse(R, [a,b,c,d]).
        Call: (7) reverse(_G506, [a, b, c, d]) ? creep
        Call: (8) reverse(_G506, [], [a, b, c, d]) ? creep
        Call: (9) reverse(_G584, [_G583], [a, b, c, d]) ? creep
        Call: (10) reverse(_G590, [_G589, _G583], [a, b, c, d]) ? creep
        Call: (11) reverse(_G596, [_G595, _G589, _G583], [a, b, c, d]) ? creep
        Call: (12) reverse(_G602, [_G601, _G595, _G589, _G583], [a, b, c, d]) ?
        Exit: (12) reverse([], [a, b, c, d], [a, b, c, d]) ? creep
        Exit: (11) reverse([a], [b, c, d], [a, b, c, d]) ? creep
        Exit: (10) reverse([b, a], [c, d], [a, b, c, d]) ? creep
        Exit: (9) reverse([c, b, a], [d], [a, b, c, d]) ? creep
        Exit: (8) reverse([d, c, b, a], [], [a, b, c, d]) ? creep
        Exit: (7) reverse([d, c, b, a], [a, b, c, d]) ? creep

     R = [d, c, b, a] ;
     ( Rule 2 is being activated infinitely, after first unification with rule 1)
     Redo: (12) reverse(_G602, [_G601, _G595, _G589, _G583], [a, b, c, d]) ?
       Call: (13) reverse(_G608, [_G607, _G601, _G595, _G589, _G583], [a, b, c,
      d]) ? creep
       Call: (14) reverse(_G614, [_G613, _G607, _G601, _G595, _G589, _G583],
      [a, b, c, d]) ? creep
       Call: (15) reverse(_G620, [_G619, _G613, _G607, _G601, _G595, _G589,
      _G583], [a, b, c, d]) ? creep
       Call: (16) reverse(_G626, [_G625, _G619, _G613, _G607, _G601, _G595,
      _G589, _G583], [a, b, c, d]) ? creep
       Call: (17) reverse(_G632, [_G631, _G625, _G619, _G613, _G607, _G601,
      _G595, _G589|...], [a, b, c, d]) ? creep

The circuit database with functors:
In the first implementation of the logical circuits example treated the circuit as a black
box. When finding the and gate for example, we didn’t get an indication of its
structure, even tough its implicitly used in finding the answer. We add an extra goal in
each rule in the database. The basic components are represented by constant symbols.

 % resistor(R, Node1, Node2)
 % R is a resistor between Node1 and Node2.
 1. resistor(r1, power,n1).
 2. resistor(r2, power,n2).

 %    transistor(T, Gate, Source, Drain)
 %    T is a transistor whose gate is Gate, source is Source and drain is Drain.
 3.    transistor(t1,n2,ground,n1).
 4.    transistor(t2,n3,n4,n2).
 5.    transistor(t3,n5,ground,n4).

 % inverter(I, Input, Output)
 % I is an inverter which inverts Input to Output
 6. inverter(inv(T,R), Input,Output) :-
     transistor(T, Input,ground,Output),
     resistor(R, power,Output).

 % nand_gate(Nand, Input1,Input2,Output)
 % Nand is a gate forming logical nand, Output of Input1 and Input2
 7. nand_gate(nand(T1,T2,R), Input1,Input2,Output) :-
    resistor( R,power,Output).

 % and_gate(And,Input1,Input2,Output)
 % And is a gate forming logical and, Output of Input1 and Input2
 8. and_gate(and(N,I), Input1,Input2,Output) :-
    nand_gate(N, Input1,Input2,X),
    inverter(I, X,Output).

The query and_gate(G,In1, In2, Out).
Returns {G=and(nand(t3,t2,r2), inv(t1, r1)), In1=n3, In2=n5, Out=n1}
The structure of G reflects accurately the functional composition of the and-gate.

Divide a list in halves
Sometimes, it is possible to combine both the accumulator and the composition of
substitutions to achieve the solution. Look at the definition of halve which also
exploits the build-in unification to test whether both halves have the same length.

 % halve(Full, Left ,Right)/3
 %       Right and Left are the 2 halves of Full and have equal lengths.
 %       Full, Right, Left: Type is List.
 1. halve(Full, Left, Right) :- halve(Full, Full , Left, Right).
 % halve(Full, RightAcc, Left ,Right)/3
 1. halve ([],Right, [],Right). % for lists of even length
 2. halve ([_X], Right, [],Right). % for lists of odd length
 3. halve ([_,_|Rest2], [X|Rest1], [X|L1], Right) :- halve(Rest2, Rest1, L1, Right).
 % first parameter – remove 2 items each times from the list.
 % second parameter – remove 1 item each time. The right half list will be returned at end of process.
 % third parameter – Accumulate the left half list. Each iteration adds the removed element X.
 % forth parameter – The variable for the right half list is carried though the iteration until its instantiated by rules1
 or rule2.

Sorting lists – merge sort
 % merge_sort(List1, List2)/2
 %      List2 is the sorted version of List1.
 %      List1, list2: Type is List.
 1. merge_sort([],[]). % empty list is already sorted
 2. merge_sort([X],[X]). % single element list is already sorted
 3. merge_sort(List, Sorted):-
        halve(List,L1,L2), % list with at least two elements is divided into two parts
        merge_sort(L2,Sorted2), % then each part is sorted
        merge(Sorted1,Sorted2,Sorted).              % and sorted parts are merged

 % merge(List1, List2, MergedList)/3
 % merges t sorted lists.
 %      List1, list2, MergedList: Type is List.
 1. merge([],L,L).
 2. merge(L,[],L):-L\=[].
 3. merge([X|T1],[Y|T2],[X|T]):-X=<Y,merge(T1,[Y|T2],T).
 4. merge([X|T1],[Y|T2],[Y|T]):-X>Y,merge([X|T1],T2,T).


Shared By: