310 likes | 441 Views
Notes for CS3310 Artificial Intelligence Part 5: Prolog arithmetic and lists. Prof. Neil C. Rowe Naval Postgraduate School Version of July 2009. Prolog arithmetic. Comparisons are written in infix form, not the usual prefix form:
E N D
Notes for CS3310 Artificial IntelligencePart 5: Prolog arithmetic and lists • Prof. Neil C. Rowe Naval Postgraduate School Version of July 2009
Prolog arithmetic • Comparisons are written in infix form, not the usual prefix form: • A == 3 (A=3 also works), A > 3, X < Y, X =< 3, Y >= 4 • Arithmetic assignment is infix too, with operators +, -, *, and /. • For instance: X is (Y * Z) + 3. • This is one-way assignment: Right-side variables must be bound. • The left-side variable usually is unbound. Otherwise the expression checks whether the value of a variable is equal to that of an arithmetic calculation. • Useful: the built-in predicate var of one argument, which succeeds if its argument is unbound.
Example: abs (absolute value) • abs(X,X) :- X>0. • abs(X,AX) :- X=<0, AX is 0-X. • Behavior: • ?-abs(3,A). • A=3. • ?-abs(B,3). • B=3. • ?-abs(-3,C). • C=3. • ?-abs(D,-3). • ! Error in arithmetic expression: not a number
Examples: mod (remainder after division) and factorial • Examples:?-mod(3,5,A).A=3.?-mod(7,5,B).B=2.?-mod(-7,5,C).C=3 • ?- factorial(5,F). • F=120 • Write definitions for these.
Example: better_add (more flexible addition) • ?-better_add(2,4,A). • A=6 • ?-better_add(3,A,5). • A=2 • better_add(X,Y,S) :- \+ var(X), \+ var(Y), var(S), S is X+Y. • better_add(X,Y,S) :- \+ var(X), var(Y), \+ var(S), Y is S-X. • better_add(X,Y,S) :- var(X), \+ var(Y), \+ var(S), X is S-Y. • better_add(X,Y,S) :- \+ var(X), \+ var(Y), \+ var(S), S2 is X+Y, S=S2.
Examples of Prolog list notation • [a,b,c]: a list of three constants a, b, and c • [a,b,X]: a list of three items a, b, and some unspecified item represented by a variable X • [X | Y]: a list whose first item is represented by variable X, and whose rest-of-list is represented by variable Y (Y is a list of zero or more items) • [a,b,c | Y]: a list whose first three items are a,b,c, and whose rest is represented by some variable Y • [ ]: the empty list (a list of no items)
Database: employees([tom, dick, harry, mary]). Queries: ?- employees(L). L=[tom, dick, harry, mary] ?- employees([X | L]) X = tom, L = [dick, harry, mary] ?- employees([X, Y, Z | L]). X = tom, Y = dick, Z = harry, L = [mary] ?- employees([X, dick | L]). X = tom, L = [harry,mary] ?- employees([X, tom | L]). no ?- employees([X, Y, Z, W | L]) X=tom, Y=dick, Z=harry, W=mary, L=[ ] ?- employees([X, Y, Z, W, A | L]) no ?- employees(L), write([joe, jim | L]), nl. [joe,jim,tom,dick,harry, mary] L=[tom,dick,harry,mary] ("nl" does carriage return) Example queries with lists
First and last • first([X | L],X). • last([X], X). • last([X | L], Y) :- last(L, Y). • employees([tom,dick,harry,mary]). • Examples: • ?- first([a,b,c], A). • A=a • ?- last([a,b,c], B). • B=c • ?- employees(EL), first(EL, E). • EL=[tom,dick,harry,mary], E=tom • ?- employees(EL), last(EL, E). • EL=[tom,dick,harry,mary], E=mary
The member predicate • member(X, [X | L]). • member(X, [Y | L]) :- member(X, L). • ?- member(c,[a,b,c,d]). X=c, Y=a, L=[b,c,d] • ?- member(c,[b,c,d]). X=c, Y=b, L=[c,d] • ?- member(c,[c,d]). X=c, Y=c, L=[d] • ? -member(E, [a,b,c]). • E=a; [line 1] • E=b; [line 2, recursion uses line 1] • E=c; [line 2, recursion uses line 2 again, which then uses line 1] • no
More uses of "member” • ?-member(a,L). • L=[a | _015]; • L=[_016,a | _017] • ?-member(a,[L]). • L=a; • no • ?-member(a,[a,b,a,c,a]). • yes; • yes; • yes; • no
Useful way to trace variables in procedural calls. Draw a box for each procedure call, inside the box for the calling procedure; allocate space for each variable. a(R,S) :- b(R), c(R,T), c(T,R). c(V,W) :- f(V,W), not(f(W,V)),b(W). b(U) :- d(U), e(U). d(1). e(1). f(2,1). Query: ?- a(1,X). Digression: Box diagrams for procedural analysis query: X a call; R: S: T: b call; U: c call; V: W: b call; U: c call; V: W: b call; U:
Example for box diagram for inheritance recursion • Assume query: ?- owns(W,radio_6391). • part_of(radio_6391, car_117654). • part_of(car_117654, nps_fleet). • owns(nps, nps_fleet). • owns(P,X) :- part_of(X,Y), owns(P,Y).
The “append” predicate • The definition of “append” is built-in in most Prolog dialects -- but here’s how you could define it otherwise. Examining this definition should make it clear how it works. • append([],L,L). • append([X|L1],L2,[X|L]) :- append(L1,L2,L).
?- append([a,b], [c,d,e], L). L = [a,b,c,d,e]; no ?- append(a, [c,d,e], L). no ?- append([a,b,c], [d,e], [X,Y|L3]). X = a Y = b L3 = [c,d,e]; no ?- append(L, [r,s], [a,b,r,s]). L = [a,b]; no ?- append(L1,L2,[a,b,c,d]). L1 = [] L2 = [a,b,c,d]; L1 = [a] L2 = [b,c,d]; L1 = [a,b] L2 = [c,d]; L1 = [a,b,c] L2 = [d]; L1 = [a,b,c,d] L2 = []; no ?- append([I1|L1],[I2|L2], [a,b,c,d]). I1 = a L1 = [] I2 = b L2 = [c,d]; I1 = a L1 = [b] I2 = c L2 = [d]; I1 = a L1 = [b,c] I2 = d L2 = []; no Demo of the built-in append predicate
Using "member", "append", and "delete" with a database • Database: • engineering_depts([ec,me,aa,cs]) • science_depts([ph,or,oc,mr]) • (a) Ask whether CS is an engineering department. • (b) Ask whether CS is an engineering or science department. • (c) Make a list of all the engineering and science departments besides CS.
Exercises with "append” • Using only append on the right side, define: • (a) front(List1, List2) (whether List1 items are the front of List2) • (b) member(Item, List) • (c) deleteone(List, Item, Newlist) (deletes first occurrence of an item in a list) • (d) substitute(List, Olditem, Newitem, Newlist) (replaces the first occurrence of Olditem by Newitem in List) • (e) twomembers(Item1, Item2, List) (gives two items in a list such that the first item occurs somewhere before the second item)
You can't rebind a bound variable in Prolog • ?- p(X,Y), p(Y,Z). Here you bind Y in the first predicate expression. The second Y refers to earlier value. You can't force Y to a new value. That's how specifications are -- a variable means the same thing everywhere. • ?- Y is 10, Y is Y+1. Similarly, this always fails because Y can't be rebound. The second "is” becomes a check whether Y is equal to Y+1, which is impossible. • ?- p(L), append(L1,L2,L), append(L3,L,L1). Similarly, this try to split a list into three pieces always fails. Once L is bound in p, you can't set it to something new (a sublist of L1). • Hence you need additional variables in Prolog to handle new values.
Defining delete, length, and reverse • This deletes all occurrences from a list. • delete([],X,[]). • delete([X|L],X,NL) :- delete (L,X,NL). • delete([Y|L],X,[Y|NL]) :- \+ X=Y, delete(L,X,NL). • This counts the number of items in a list. • length([],0). • length([X|L],N) :- length(L,N2), N is N2+1. • This reverses a list. • reverse(L,R) :- reverse2(L,[],R). • reverse2([],R,R). • reverse2([X|L],M,R) :- reverse2(L,[X|M],R).
Recursive-rule exercise • Define a "sort(List,Slist)" that uses insertion sort to sort List into Slist.
Comparing speed of Prolog and C • Same algorithms were used in both languages, but linked lists were used in Prolog for C arrays. Prolog is better than C for several programs, and not much worse otherwise.
Three new terms • Temporal logic: Rules for reasoning about time. Typically they explain time phenomenon like "before", "until", and "periodically". • Constraints: Predicate expressions that restrict solutions to some problem. • Constraint programming: Programming that focuses on satisfying constraints on a solution. Examples are scheduling, resource allocation, and certain aspects of computer vision.
Example: reasoning about time • Assume we have a database of facts of the form: event(<name>,<start-time>,<end-time>) where the last two argument are floating-point numbers representing the number of days since January 1, 1950. • We want to define the following: • before(X,Y): X is before Y • after(X,Y): X is after Y --Use "before" to define it • during(X,Y): X is during Y • between(X,Y,Z): Y is between X and Z • firstevent(X): X is the first known event • Also, why can't we handle because(X,Y)?
Answer to reasoning about time • (Quintus Prolog "less than or equals" is "=<".) • before(X,Y) :- event(X,SX,EX), event(Y,SY,EY), EX =< SY. • after(X,Y) :- before(Y,X). • during(X,Y) :- event(X,SX,EX), event(Y,SY,EY), • SY =< SX, EX =< EY. • between(X,Y,Z) :- before(X,Y), before(Y,Z). • between(X,Y,Z) :- before(Z,Y), before(Y,X). • firstevent(X) :- event(X,SX,EX), \+ before(W,X).
Example: scheduling program • This picks 5 meeting times during a week. • schedule(Times) :- Times=[T1,T2,T3,T4,T5], • classtime(T1), \+ occupied(T1), • classtime(T2), \+ occupied(T2), • classtime(T3), \+ occupied(T3), • classtime(T4), \+ occupied(T4), • classtime(T5), \+ occupied(T5), • \+ duplication(Times), • \+ two_consecutive_hours(Times), • \+ three_classes_same_day(Times). • classtime([Day,Hour]) :- member(Day, [monday, tuesday, wednesday, thursday, friday]), member(Hour, [800, 900, 1000, 1100, 1200, 1300, 1400, 1500]).
The scheduling program, cont. • two_consecutive_hours(TL) :- member([Day,Hour1],TL), member([Day,Hour2],TL), H2 is Hour1+100, H2=Hour2. • three_classes_same_day(TL) :- member([Day,Hour1],TL), member([Day,Hour2],TL), member([Day,Hour3],TL), \+ duplication([Hour1,Hour2,Hour3]). • duplication([X | L]) :- member(X,L). • duplication([X | L]) :- duplication(L). • member(X,[X | L]). • member(X,[Y | L]) :- member(X,L).
The scheduling program, continued • Sample facts about times not available: • occupied([X,900]). • occupied([X,1000]). • occupied([X,1200]). • occupied([X,1500]). • occupied([wednesday,Y]). • occupied([friday,Y]). • First three answers of the program with above facts: • | ?- schedule(S). • S = [[monday,800],[monday,1100],[tuesday,800], [tuesday,1100],[thursday,800]] ; • S = [[monday,800],[monday,1100],[tuesday,800], [tuesday,1100],[thursday,1100]] ; • S = [[monday,800],[monday,1100],[tuesday,800], [tuesday,1100],[thursday,1300]] ;
Exercises with the scheduling program (mutually exclusive) • (a) Modify it so all schedules avoid Friday for any database. • (b) Modify it so all classes are on different days. • (c) Modify it so all class hours are within 2 hours apart. • (d) Paraphrase in 20 words or less what the following routine does. Assume it is called last in schedule with the calling expression \+ test([T1,T2,T3,T4,T5]). • test(TL) :- member([D1,U1],TL), member([D1,U2],TL), member([D2,U3],TL), member([D2,U4],TL), \+ U1=U2, \+ U3=U4, \+ D1=D2.
Improving the speed of scheduling (1) • betterschedule([T1,T2,T3,T4,T5]) :- • classtime(T1), \+ occupied(T1), • classtime(T2), \+ occupied(T2), • \+ duplication([T1,T2]), • classtime(T3), \+ occupied(T3), • \+ duplication([T1,T2,T3]), • classtime(T4), \+ occupied(T4), • \+ duplication([T1,T2,T3,T4]), • classtime(T5), \+ occupied(T5), • \+ duplication([T1,T2,T3,T4,T5]), • \+ two_consecutive_hours([T1,T2,T3,T4,T5]), • \+ three_classes_same_day([T1,T2,T3,T4,T5]). • Note this is faster than the original (and shorter) program.
Improving the speed of scheduling (2) • betterschedule2([T1,T2,T3,T4,T5]) :- • classtime(T1), \+ occupied(T1), • classtime(T2), \+ occupied(T2), before(T1,T2), • classtime(T3), \+ occupied(T3), before(T2,T3), • classtime(T4), \+ occupied(T4), before(T3,T4), • classtime(T5), \+ occupied(T5), before(T4,T5), • \+ two_consecutive_hours([T1,T2,T3,T4,T5]), • \+ three_classes_same_day([T1,T2,T3,T4,T5]). • before([Day1,_],[Day2,_]) :- • append(_,[Day1|L2], [monday,tuesday,wednesday,thursday,friday]), • append(_,[Day2|_],L2). • before([Day,Hour1],[Day,Hour2]) :- Hour1 < Hour2. • This program is faster still.
NPS class scheduling (2009) • Automated class scheduling at NPS uses several sophisticated algorithms. • Students and professors specify preferences (days and times, rooms, consecutive classes). • The program first tries to assign class sections to rooms ignoring students and professors. • If this works, students then assigned to class sections. • If this works, professors are assigned to sections. • Any failures are flagged and options can be selected: Ignoring preferences, reassigning students to sections, introducing new sections, etc. Then scheduling is run again.