Database Manipulation Section 7.4
Prolog Knowledge Base • Knowledge base = database • set of facts/rules in the program • Can add/subtract facts and rules at run time • Adding facts/rules • assert, asserta, assertz • Subtracting facts/rules • retract, retractall
Asserting a Fact • Just tell prolog that it’s so ?- raining. ERROR: Undefined procedure: raining/0 ?- assert(raining). Yes ?- raining. Yes • Prolog didn’t know about raining/0
Preparing Prolog for a Predicate • Need to tell Prolog that the predicate exists • helpful also to say that it may have rules added • Give a directive in the program file • a command to Prolog :- dynamic raining/0. • Says that raining/0 is a predicate • prevents the error message
Dynamic Predicates • Can be done during session ?- dynamic foggy/0. Yes ?- foggy. No • Note how we call goals in a file :- dynamic foggy/0. • can do that with any query
Asserting Rules • Just give rule instead of fact • use parentheses if rule is compound (to prevent confusion about the comma) ?- assert(raining). ?- assert(bad :- raining ; foggy). ?- assert(nice :- (sunshine, \+ cold)). ?- bad. Yes
Assertion Order • assert/1 puts the fact/rule in the database • order is implementation dependent • (SWI-Prolog puts it at the end) • asserta/1 puts fact/rule in front • assertz/1 puts fact/rule at end
Assertion Order ?- assert(num(1)). ?- assertz(num(2)). ?- asserta(num(0)). ?- assertz(num(3)). ?- num(X). X = 0 ; X = 1 ; X = 2 ; X = 3 Adds num(1) to database Adds num(2) to end of DB Adds num(0) to front of DB Adds num(3) to end of DB num(0). num(1). num(2). num(3).
Assertion Order Exercise ?- asserta(what(1)). ?- assertz(what(2)). ?- asserta(what(3)). ?- assertz(what(4)). ?- asserta(what(5)). ?- what(X). X = ? ; X = ? ; X = ? ;
Exercise • Write a predicate that asks the user for a person’s parents & asserts those facts ?- add_parents(mark). Who is mark’s father? bob. Who is mark’s mother? isabel. Yes ?- father(mark, Dad), mother(mark, Mom). Dad = bob, Mom = isabel
Solution ask_parents(Person) :- ask_for_who(Person, father, Dad), ask_for_who(Person, mother, Mom), assert(father(Person, Dad)), assert(mother(Person, Mom)). ask_for_who(Person, Role, Name) :- write(‘Who is ’), write(Person), write(‘\’s ’), write(Role), write(‘? ’), read(Name).
Retraction • Tell Prolog to remove a fact/rule ?- raining. Yes ?- retract(raining). Yes ?- raining. No
Retracting Rules • As for asserting rules • use parentheses if body is compound • body may be a variable/partly instantiated ?- retract(bad :- raining ; foggy). ?- retract(nice :- Body). Body = sunshine, \+ raining Yes
Retraction Order • From first to last ?- retract(num(N)), retract(num(M)). N = 0 M = 1 Yes • retract fails if no clause matches
Retracting All Clauses • rectractall/1 retracts multiple clauses • all clauses with head matching the argument ?- num(N). N = 0 ?- retractall(num(N)). Yes ?- num(N). No Note: also gets rules ?- assert(bad :- member(1,)). Yes ?- bad. Yes ?- retractall(bad). Yes ?- bad. No
Asserting and Retracting • Used for AI programs that learn • create a new rule & add it to the database • forget an old rule • Can also be used for efficiency • asserta solutions previously found • found before general code called
Naïve Fibonacci fib(1, 1). fib(2, 1). fib(N, F) :- N > 2, N1 is N – 1, fib(N1, F1), N2 is N – 2, fib(N2, F2), F is F1 + F2.
Trace fib(5,F) fib(5, F0) fib(4, F1) fib(3, F2) fib(2, F3) F3 = 1 fib(1, F4) F4 = 1 fib(2, F5) F5 = 1 fib(3, F2) fib(2, F3) F3 = 1 fib(1, F4) F4 = 1 fib(3, F) gets calculated again extra work done much worse as #s get bigger
Assertional Fibonacci (8.5.4) fib2(1, 1). fib2(2, 1). fib2(N, F) :- N > 2, N1 is N – 1, fib2(N1, F1), N2 is N – 2, fib2(N2, F2), F is F1 + F2, asserta( fib2(N, F) ). % remember the result % at the beginning
Trace fib2(5,F) fib2(5, F0) fib2(4, F1) fib2(3, F2) fib2(2, F3) F3 = 1 fib2(1, F4) F4 = 1 asserta( fib2(3, 2) ) fib2(2, F5) F5 = 1 asserta( fib2(4, 3) ) fib2(3, F6) F6 = 2 asserta( fib2(5, 5) ) Saves work from calculating fib(3) Matches asserted fact – no need to recalculate
Problems for Asserting Solutions • Backtracking gives multiple solutions • saved solution found first • then solution re-calculated • and re-asserted: solution there twice, now • next time there’ll be three solutions • Adding a cut to the solution might help • asserta( fib2(F, N) :- ! ).
Generating Facts • Multiplication table make_table :- \+ ( L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], member(X, L), member(Y, L), Z is X * Y, assert(product(X, Y, Z)), fail ).
Using the Multiplication Table • Now have 100 multiplication facts ?- product(X, Y, 12). X = 2, Y = 6 ; X = 3, Y = 4 ; X = 4, Y = 3 ; X = 6, Y = 2 ; No
Asserts on Calls in Progress • Suppose assert or assertz a clause of a predicate that’s in progress • is that clause considered for that call? strange(N) :- assertz(strange(N)), fail. • Does strange(3) succeed or fail? • or throw an exception
Speed of Dynamic Predicates • Dynamic predicates may be slower than static (dynamic = can change, static = can’t) • Prolog may have a better way for remembering solutions • remember or record predicates to memorize • recall or recorded to recall • forget or erase to forget
Remembering Solutions • One clause (near front) to look for remembered solutions • cut if find one • Remember solution after it’s calculated
Remembering Fibonacci fib_mem(0, 1). fib_mem(1, 1). fib_mem(N, F) :- recorded(fibonacci, fib(N, F)), !. fib_mem(N, F) :- N > 1, succ(N1, N), fib_mem(N1, F1), succ(N2, N1), fib_mem(N2, F2), F is F1 + F2, recorda(fibonacci, fib(N, F)).
Recording • recorda/2, recordz/2 store 2nd argument • use 1st argument as a key • you can record same fact under multiple keys • key is integer or atom (or term – but no good) • recorded/2 retrieves fact • needs key – only retrieves facts under that key • matches fact – only those that match selected
Erasing/Forgetting • recorded/3 also gives a memory location • Can use the location to erase the record ?- recorded(fibonacci, fib(500,_), Mem), erase(Mem). Yes • Can only erase one item at a time • don’t erase anything more than once
Versions of Fibonacci • Vanilla version is very slow • and only works on a few numbers • Remembering version quite fast • even faster when asking a second time • Single recursion version very fast • but second time takes as long as first ?- time(fib(20, F)), time(fib2(20, F)), time(fib_mem(20, F)).
Looking at Code • Program can inspect its own code • clause/2 returns clauses of program • 1st argument is the head of the rule • 2nd argument is the body • at least one argument must be given
Clause/2 sibs(Sib1, Sib2) :- parent(Sib1, P), parent(Sib2, P), different(Sib1, Sib2). different(X, Y) :- X \= Y. ?- clause(sibs(_, _), Body). Body = parent(_1,_2), parent(_3,_2), different(_1,_3) ?- clause(Head, _A \= _B). Head = different(_1,_2) Note: this last one doesn’t seem to work in SWI-Prolog
Compound Bodies • Body is a term – a comma term ?- (a, b, c, d) = (X, Y). X = a Y = b, c, d • Needs to be in parentheses when 2nd arg. ?- clause(sibs(_,_), (parent(_,_), _) ). Yes Is there a clause of sibs/2 with a body like parent(_,_), …
Bodies of Facts • Puts true/0 in for body of fact parent(mark, alex). parent(bob, mark). parent(X, brian) :- parent(X, mark). ?- clause(parent(X, Y), Body). X = mark, Y = alex, Body = true ; X = bob, Y = mark, Body = true ; X = _1, Y = brian, Body = parent(_1,mark)
Looking at Built-in Code • Some built-in predicates are available ?- clause(member(X,Y), Body). X = _1, Y = [_1|_2], Body = true ; X = _1, Y = [_2|_3], Body = member(_1, _3) • Others are hidden ?- clause(clause(A,B), Body). ERROR: No permission to access private_procedure `clause/2'
Modifying Code • Replace every rule of the form • H :- …, different(X,Y), … • with • H :- …, X \= Y, … • (where the other bits don’t change) • Maybe for efficiency • H should be specified (else errors ensue)
Method for Modifying Code • Find a rule with matching head • Note: predicate must be dynamic • If it has a different(X,Y) in it, • replace it with X \= Y • retract old rule • assert new rule
Modification Predicate modify_rule(Head, Old, New) :- clause(Head, Body), replace_code(Body, Old, New, NewBody), retract(Head :- Body), assert(Head :- NewBody). replace_code((Old,More), Old, New, (New,More)). replace_code((H,OldT), Old, New, (H,NewT)) :- replace_code(OldT, Old, New, NewT). replace_code(Old, Old, New, New).
Modifying Code ?- modify_rule(sib(_,_), different(X,Y), X\=Y). before: sib(A, B) :- parent(A,C), parent(B,C), different(A,B). • Problem – only modifies one clause! after: sib(A, B) :- parent(A,C), parent(B,C), A \= B.
Modifying All Clauses • Prolog prints bindings • ask for another solution • Prolog backs up to clause/2 & gets another rule • then changes that rule • Keep asking for answers until all done • it works – will stop when no rule has Old in it • it’s sort of like a loop
“Failure Driven Loops” • Need to make a change, then fail • Prolog will try another clause • Have predicate make a change, then fail • it will change all the rules before saying No • Says No even tho’ it worked! • if we put \+ in front, it’ll say Yes instead! • that’s more intuitive for user
Modify All modify_all(Head, Old, New) :- \+ modify_and_fail(Head, Old, New). modify_and_fail(Head, Old, New) :- modify_rule(Head, Old, New), fail. OR Just modify_all(Head, Old, New) :- \+ (modify_rule(Head, Old, New), fail).
Remaining Problem • too_different/3 is not fully changed • replace_code only changes first Old to New • backtracking doesn’t pick up new clause • Try to fix by making replace_code change all • unexpected side-effect: all X\=Y made the same • too_different(A,A,A) :- A\=A, A\=A, A\=A. • Too complicated for us!
Next Time • Definite Clause Grammars