Upload
jisha-t-shaji
View
130
Download
7
Embed Size (px)
DESCRIPTION
B.tech CS S8 Artificial Intelligence Notes Module 5
Citation preview
1
Module 5
Prolog
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Abstract Data Types (ADTs) in Prolog
%%%%%%%%%%%%%%%%%%% stack operations %%%%%%%%%%%%%%%
% These predicates give a simple, list based implementation of stacks
% empty stack generates/tests an empty stack
% BUILT IN TO SWI PROLOG
%member(X,[X|T]).
%member(X,[Y|T]):-member(X,T).
empty_stack([]).
% member_stack tests if an element is a member of a stack
member_stack(E, S) :- member(E, S).
% stack performs the push, pop and peek operations
% to push an element onto the stack
% ?- stack(a, [b,c,d], S).
% S = [a,b,c,d]
% To pop an element from the stack
% ?- stack(Top, Rest, [a,b,c]).
% Top = a, Rest = [b,c]
% To peek at the top element on the stack
% ?- stack(Top, _, [a,b,c]).
% Top = a
stack(E, S, [E|S]).
42
%%%%%%%%%%%%%%%%%%%% queue operation %%%%%%%%%%%%%%
% These predicates give a simple, list based implementation of FIFO queues
% empty queue generates/tests an empty queue
empty_queue([]).
% member_queue tests if an element is a member of a queue
member_queue(E, S) :- member(E, S).
% add_to_queue adds a new element to the back of the queue
add_to_queue(E, [], [E]).
add_to_queue(E, [H|T], [H|Tnew]) :- add_to_queue(E, T, Tnew).
% remove_from_queue removes the next element from the queue
% Note that it can also be used to examine that element
% without removing it
remove_from_queue(E, [E|T], T).
append_queue(First, Second, Concatenation) :-
append(First, Second, Concatenation).
%%%%%%%%%%%%%%%%%%%% set operations %%%%%%%%%%%%%%%
% These predicates give a simple, list based implementation of sets
% empty_set tests/generates an empty set.
empty_set([]).
member_set(E, S) :- member(E, S).
% add_to_set adds a new member to a set, allowing each element
% to appear only once
add_to_set(X, S, S) :- member(X, S), !.
add_to_set(X, S, [X|S]).
remove_from_set(E, [], []).
43
remove_from_set(E, [E|T], T) :- !.
remove_from_set(E, [H|T], [H|T_new]) :-
remove_from_set(E, T, T_new), !.
% BUILT IN TO SWI PROLOG
/*
union([], S, S).
union([H|T], S, S_new) :-
union(T, S, S2),
add_to_set(H, S2, S_new).
intersection([], _, []).
intersection([H|T], S, [H|S_new]) :-
member_set(H, S),
intersection(T, S, S_new),!.
intersection([_|T], S, S_new) :-
intersection(T, S, S_new),!.
*/
set_diff([], _, []).
set_diff([H|T], S, T_new) :-
member_set(H, S),
set_diff(T, S, T_new),!.
set_diff([H|T], S, [H|T_new]) :-
set_diff(T, S, T_new), !.
subset([], _).
subset([H|T], S) :-
member_set(H, S),
subset(T, S).
equal_set(S1, S2) :-
subset(S1, S2), subset(S2, S1).
%%%%%%%%%%%%%%%%% priority queue operations %%%%%%%%%%
% These predicates provide a simple list based implementation of a priority queue.
% They assume a definition of precedes for the objects being handled
empty_sort_queue([]).
member_sort_queue(E, S) :- member(E, S).
insert_sort_queue(State, [], [State]).
44
insert_sort_queue(State, [H | T], [State, H | T]) :-
precedes(State, H).
insert_sort_queue(State, [H|T], [H | T_new]) :-
insert_sort_queue(State, T, T_new).
remove_sort_queue(First, [First|Rest], Rest).
Semantic Nets and Frames in Prolog
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Alternative search strategies
Basic depth first search algorithm
go(Start, Goal) :-
empty_stack(Empty_been_list),
stack(Start, Empty_been_list, Been_list),
path(Start, Goal, Been_list).
% path implements a depth first search in PROLOG
% Current state = goal, print out been list
path(Goal, Goal, Been_list) :-
reverse_print_stack(Been_list).
path(State, Goal, Been_list) :-
mov(State, Next),
% not(unsafe(Next)),
not(member_stack(Next, Been_list)),
stack(Next, Been_list, New_been_list),
path(Next, Goal, New_been_list), !.
reverse_print_stack(S) :-
empty_stack(S).
reverse_print_stack(S) :-
stack(E, Rest, S),
reverse_print_stack(Rest),
write(E), nl.
Breadth first search algorithm
state_record(State, Parent, [State, Parent]).
go(Start, Goal) :-
empty_queue(Empty_open),
state_record(Start, nil, State),
add_to_queue(State, Empty_open, Open),
empty_set(Closed),
path(Open, Closed, Goal).
path(Open,_,_) :- empty_queue(Open),
write('graph searched, no solution found').
path(Open, Closed, Goal) :-
remove_from_queue(Next_record, Open, _),
69
state_record(State, _, Next_record),
State = Goal,
write('Solution path is: '), nl,
printsolution(Next_record, Closed).
path(Open, Closed, Goal) :-
remove_from_queue(Next_record, Open, Rest_of_open),
(bagof(Child, moves(Next_record, Open, Closed, Child), Children);
Children = []),
add_list_to_queue(Children, Rest_of_open, New_open),
add_to_set(Next_record, Closed, New_closed),
path(New_open, New_closed, Goal),!.
moves(State_record, Open, Closed, Child_record) :-
state_record(State, _, State_record),
mov(State, Next),
% not (unsafe(Next)),
state_record(Next, _, Test),
not(member_queue(Test, Open)),
not(member_set(Test, Closed)),
state_record(Next, State, Child_record).
printsolution(State_record, _):-
state_record(State,nil, State_record),
write(State), nl.
printsolution(State_record, Closed) :-
state_record(State, Parent, State_record),
state_record(Parent, Grand_parent, Parent_record),
member(Parent_record, Closed),
printsolution(Parent_record, Closed),
write(State), nl.
add_list_to_queue([], Queue, Queue).
add_list_to_queue([H|T], Queue, New_queue) :-
add_to_queue(H, Queue, Temp_queue),
add_list_to_queue(T, Temp_queue, New_queue).
Best first search algorithm
%%%%% operations for state records %%%%%%%
% These predicates define state records as an adt
% A state is just a [State, Parent, G_value, H_value, F_value] tuple.
% Note that this predicate is both a generator and
% a destructor of records, depending on what is bound
70
% precedes is required by the priority queue algorithms
state_record(State, Parent, G, H, F, [State, Parent, G, H, F]).
precedes([_,_,_,_,F1], [_,_,_,_,F2]) :- F1 =< F2.
% go initializes Open and Closed and calls path
go(Start, Goal) :-
empty_set(Closed),
empty_sort_queue(Empty_open),
heuristic(Start, Goal, H),
state_record(Start, nil, 0, H, H, First_record),
insert_sort_queue(First_record, Empty_open, Open),
path(Open,Closed, Goal).
% Path performs a best first search,
% maintaining Open as a priority queue, and Closed as a set.
% Open is empty; no solution found
path(Open,_,_) :-
empty_sort_queue(Open),
write("graph searched, no solution found").
% The next record is a goal
% Print out the list of visited states
path(Open, Closed, Goal) :-
remove_sort_queue(First_record, Open, _),
state_record(State, _, _, _, _, First_record),
State = Goal,
write('Solution path is: '), nl,
printsolution(First_record, Closed).
% The next record is not equal to the goal
% Generate its children, add to open and continue
% Note that bagof in AAIS prolog fails if its goal fails,
% I needed to use the or to make it return an empty list in this case
path(Open, Closed, Goal) :-
remove_sort_queue(First_record, Open, Rest_of_open),
bagof(Child, moves(First_record, Open, Closed, Child, Goal), Children),
insert_list(Children, Rest_of_open, New_open),
add_to_set(First_record, Closed, New_closed),
path(New_open, New_closed, Goal),!.
71
% moves generates all children of a state that are not already on open or closed. The only
% wierd thing here is the construction of a state record, test, that has unbound variables in
% all positions except the state. It is used to see if the next state matches something
% already on open or closed, irrespective of that states parent or other attributes Also,I've
% commented out unsafe since the way I've coded the water jugs problem I don't really
% need it.
moves(State_record, Open, Closed,Child, Goal) :-
state_record(State, _, G, _,_, State_record),
mov(State, Next),
% not(unsafe(Next)),
state_record(Next, _, _, _, _, Test),
not(member_sort_queue(Test, Open)),
not(member_set(Test, Closed)),
G_new is G + 1,
heuristic(Next, Goal, H),
F is G_new + H,
state_record(Next, State, G_new, H, F, Child).
%insert_list inserts a list of states obtained from a call to
% bagof and inserts them in a priotrity queue, one at a time
insert_list([], L, L).
insert_list([State | Tail], L, New_L) :-
insert_sort_queue(State, L, L2),
insert_list(Tail, L2, New_L).
% Printsolution prints out the solution path by tracing
% back through the states on closed using parent links.
printsolution(Next_record, _):-
state_record(State, nil, _, _,_, Next_record),
write(State), nl.
printsolution(Next_record, Closed) :-
state_record(State, Parent, _, _,_, Next_record),
state_record(Parent, Grand_parent, _, _, _, Parent_record),
member_set(Parent_record, Closed),
printsolution(Parent_record, Closed),
write(State), nl.
Meta-interpreters in Prolog
Meta-programming, is programming where we treat programs as data. This is
easy in Prolog, as Prolog programs are just Prolog terms. Programs can also be
72
considered as input data for other programs. Prolog programs are sequences of prolog
terms, so prolog programs easily serve as input data. A prolog meta-interpreter uses
program data as a basis for additional computations. In this section, several prolog meta-
interpreters are discussed that modify the computation of prolog goals.
Because it is possible to directly access program code in Prolog, it is easy to write
interpreter of Prolog in Prolog. Such interpreter is called a meta-interpreter. Meta-
interpreters are usually used to add some extra features to Prolog, e.g., to change build-in
negation as failure to constructive negation. The meta-level constructs, especially
‘clause’, is very useful in building meta interpreters, i.e., Prolog interpreters in Prolog.
The key idea is in defining a predicate, called ‘solve(G)’ which solves goal G with
respect to a program P.
The simplest Prolog meta-interpreter is a following program:
solve(Goal):-call(Goal).
However, there is not advantage of using such meta-intepreter as it immediately
calls Prolog interpreter. Much more popular is "vanilla" meta-interpreter that uses
Prolog's build-in unification but enables access to search engine which can be easily
modified (e.g., it is possible to change the order of goals' execution)
solve(true).
solve((A,B)):-
solve(A),solve(B).
solve(A):-
clause(A,B),solve(B).
Note, that vanilla meta-interpreter uses build-in predicate clause(H,B) which finds
a clause in Prolog program with head that unifies with H and body B (if there is no body,
then Body=true). The modified vanilla meta-interpreter can be used to compute "proof"
of the computation:
solve(true, fact).
solve((A,B),(ProofA, ProofB)):-
solve(A, ProofA),solve(B, ProofB).
solve(A, A-ProofB):-
clause(A,B),solve(B, ProofB).
It is also possible to write a meta-interpreter that uses list of goals instead of
traditional conjunction of goals. In some cases, this could be more natural as one does not
need to traverse the structure of goal each time a primitive goal is being found.
solve([]).
solve([A|T]):-
clause(A,B),
73
add_to_list(B,T,NT),
solve(NT).
A clause such as: gp(X, Y) :- p(X, Z) , p(Z, Y). can be interpreted as data, where
:- and , are just infix binary constructors. Also
father(bob,mark). is represented as:
father(bob,mark) :- true.
Consider the following query
?- solve(gp(bob,sue), Proof).
Proof = (gp(bob,sue) :-
(p(bob,mary) :-
(m(bob,mary) :- true)),
(p(mary,sue) :-
(m(mary,sue) :- true)))
?- why(gp(bob,sue)).
gp(bob,sue) is true
because p(bob,mary) is true
because m(bob,mary) is true
because p(mary,sue) is true
because m(mary,sue) is true
yes
A Simple Meta-interpreter
solve(true) :-!.
solve(not A) :- not(solve(A)).
solve((A,B)) :- !,solve(A), solve(B).
solve(A) :- clause(A,B), solve(B).
p(X,Y) :- q(X), r(Y).
q(X) :- s(X).
r(X) :- t(X).
s(a).
t(b).
t(c).
Given below can be used as test cases for above meta-interpreter
test1 :- solve(p(a,b)).
test2 :- solve(p(X,Y)).
test3 :- solve(p(f,g)).
74
Meta-interpreter with user interaction
solve(true) :-!.
solve(not A) :- not(solve(A)).
solve((A,B)) :- !,solve(A), solve(B).
solve(A) :- clause(A,B), solve(B).
solve(A) :- askuser(A).
askuser(A):- write(A),
write('? Enter true if the goal is true, false otherwise'),
nl, read(true).
p(X,Y) :- q(X), r(Y).
q(X) :- s(X).
r(X) :- t(X).
s(a).
t(b).
t(c).
Given below can be used as test cases for above meta-interpreter
test1 :- solve(p(a,b)).
test2 :- solve(p(X,Y)).
test3 :- solve(p(f,g)).
Previous University questions
4-mark questions
1. What is matching?
2. What are abstract data types?
3. With an example, explain how facts are represented in prolog?
4. Explain how frames can be represented in prolog?
5. Explain the use of assert and been predicate in prolog?
Answer:
Predicates
Clauses with the same clause name, the same number of arguments and defined in
the same module are combined in the database and form the definition of a predicate.
The common clause name is called the predicate name or functor of the predicate. The
number of arguments is the arity. For example, the predicate fac/2 is defined by the
collection of all clauses with the clause head fac(Arg1,Arg2), where Arg1 and Arg2 may
be any terms.
75
The separate clauses of a predicate are connected by disjunction, i.e. by inclusive
OR. Clauses with the same clause name but a different number of arguments belong to
different predicates. Likewise, clauses which have the same clause name and the same
arity but are associated with different modules belong to different predicates.
The Prolog predicate concept can be compared to the subprogram concept in
conventional programming languages; we therefore also speak of "calling" a predicate in
Prolog. The predicate concept ensures a high degree of modularity in Prolog programs
and thereby supports structured programming.
Only predicates whose clauses have been included in the database with consult/1,
reconsult/1 or with an assert predicate can be modified, i.e. clauses can be added (assert
predicates) or removed (retract predicates), individual predicates can be deleted
(abolish/1) or replaced (reconsult/1). Assert is used to add a new predicate to the current
database. Been predicate is used to record previously visited states and avoid loops.
The clauses of these predicates can be output with listing/0/1 and analyzed with
clause predicates. Note however that to do so you must call the Prolog system with the -
debug option.
Compiled predicates
IF/Prolog offers a number of ways of compiling user-defined predicates and
including them in the database. This can be done by the system's incremental compiler by
using the assert and consult predicates. In addition, predicate definitions stored in files
can be compiled by the predicate compile/1 or the external compiler (procmp command).
Predicates compiled with consult/1, reconsult/1, compile/1 or procmp are
normally no longer modifiable. However, you can inform the compiler with the
dynamic/1 directive that certain predicates are to remain modifiable. Predicates which
you have generated with assert predicates or which you have declared as modifiable with
other compilation methods are compiled only to the extent where the compilation is
reversible, i.e. where decompilation is possible.
Whenever references are made in this manual to compiled predicates they should
be taken to mean predicates which cannot be decompiled. These can no longer be
modified by simply adding or removing clauses.
12-mark questions
1) Explain how recursive search is carried out in prolog with an example?
2) Write notes on:
a. Meta predicates
b. Meta interpreters
3) Brief on the concept of matching and evaluation in prolog with example?
76
4) With example explain how to represent semantic nets in prolog?
5) Explain various alternative search strategies?
6) Describe how recursive search is used on prolog to solve 3 x 3 knight’s problem?
Answer:
A knight can move two squares either horizontally or vertically followed by one
square in an orthogonal direction as long as it does not move off the board. The
attempt is to find a series of legal moves in which the knight lands on each square of
the chessboard exactly once.
% 3x3 knight's tour
:- dynamic been/1.
path(Z,Z).
path(A,C):- move(A,B), not(been(B)), assert(been(B)), path(B,C).
% initial call is path2(A,B,[A])
path2(Z,Z,Been).
path2(A,C,Been):- move(A,B), not member(B,Been), path2(B,C,[B|Been]).
% initial call is path3(A,B,[A]) AT MOST ONE SOLUTION
path3(Z,Z,Been).
path3(A,C,Been):- move(A,B), not member(B,Been), path3(B,C,[B|Been]),!.
move(1,6).
move(1,8).
move(2,7).
move(2,9).
move(3,4).
move(3,8).
move(4,3).
1 2 3
4 5 6
7 8 9
77
move(4,9).
move(6,7).
move(6,1).
move(7,6).
move(7,2).
move(8,3).
move(8,1).
move(9,4).
move(9,2).
7) Write the prolog code for the wolf, goat, and cabbage problem.
Answer:
This is an example of a production system in Prolog. A farmer with his wolf, goat,
and cabbage come to the edge of a river they wish to cross. There is a boat at the
river's edge, but, of course, only the farmer can row. The boat also can carry only two
things (including the rower) at a time. Devise a sequence of crossings of the river so
that all four arrive safely on the other side of the river. Remembering that If the wolf
is ever left alone with the goat, the wolf will eat the goat. Similarly, if the goat is left
alone with the cabbage, the goat will eat the cabbage.
/*
* This is the code for the Farmer, Wolf, Goat and Cabbage Problem
* using the ADT Stack.
*
* Run this code by giving PROLOG a "go" goal.
* For example, to find a path from the west bank to the east bank,
* give PROLOG the query:
*
* go(state(w,w,w,w), state(e,e,e,e)).
*/
:- [adts]. /* consults (reconsults) file containing the
various ADTs (Stack, Queue, etc.) */
go(Start,Goal) :-
empty_stack(Empty_been_stack),
stack(Start,Empty_been_stack,Been_stack),
path(Start,Goal,Been_stack).
/*
* Path predicates
*/
path(Goal,Goal,Been_stack) :-
78
write('Solution Path Is:' ), nl,
reverse_print_stack(Been_stack).
path(State,Goal,Been_stack) :-
move(State,Next_state),
not(member_stack(Next_state,Been_stack)),
stack(Next_state,Been_stack,New_been_stack),
path(Next_state,Goal,New_been_stack),!.
/*
* Move predicates
*/
move(state(X,X,G,C), state(Y,Y,G,C))
:- opp(X,Y), not(unsafe(state(Y,Y,G,C))),
writelist(['try farmer takes wolf',Y,Y,G,C]).
move(state(X,W,X,C), state(Y,W,Y,C))
:- opp(X,Y), not(unsafe(state(Y,W,Y,C))),
writelist(['try farmer takes goat',Y,W,Y,C]).
move(state(X,W,G,X), state(Y,W,G,Y))
:- opp(X,Y), not(unsafe(state(Y,W,G,Y))),
writelist(['try farmer takes cabbage',Y,W,G,Y]).
move(state(X,W,G,C), state(Y,W,G,C))
:- opp(X,Y), not(unsafe(state(Y,W,G,C))),
writelist(['try farmer takes self',Y,W,G,C]).
move(state(F,W,G,C), state(F,W,G,C))
:- writelist([' BACKTRACK from:',F,W,G,C]), fail.
/*
* Unsafe predicates
*/
unsafe(state(X,Y,Y,C)) :- opp(X,Y).
unsafe(state(X,W,Y,Y)) :- opp(X,Y).
/*
* Definitions of writelist, and opp.
*/
writelist([]) :- nl.
79
writelist([H|T]):- print(H), tab(1), /* "tab(n)" skips n spaces. */
writelist(T).
opp(e,w).
opp(w,e).
reverse_print_stack(S) :-
empty_stack(S).
reverse_print_stack(S) :-
stack(E, Rest, S),
reverse_print_stack(Rest),
write(E), nl.
Artificial Intelligence 19
Sample crossing for
FWGC problem
80
Artificial Intelligence 20
State Space Representation of FWGC problem