630 likes | 754 Views
Prolog for Linguists Symbolic Systems 139P/239P. John Dowding Week 8, November 26, 2001 jdowding@stanford.edu. Office Hours. We have reserved 4 workstations in the Unix Cluster in Meyer library, fables 1-4 Office Hours this Thursday, 4:30-5:30 Contact me to make other arrangements.
E N D
Prolog for Linguists Symbolic Systems 139P/239P John Dowding Week 8, November 26, 2001 jdowding@stanford.edu
Office Hours • We have reserved 4 workstations in the Unix Cluster in Meyer library, fables 1-4 • Office Hours this Thursday, 4:30-5:30 • Contact me to make other arrangements
Course Schedule • Oct. 8 • Oct. 15 • Oct. 22 • Oct. 29 • Nov. 5 (double up) • Nov. 12 • Nov. 26 (double up) – Iterative Deepening and Logic • Dec. 3 No class on Nov. 19
Review of Last Week • Last week we covered two techniques in DCGs • Syntactic Gaps • Quasi-Logical Form • Instead of just reviewing that material, I did the homework exercises starting from the grammar including gaps and QLFs.
Examples: The man likes the cats qterm(the, X, man(X)), qterm(the,Y,cat(Y)), likes(X, Y) The man that likes the cats sleeps qterm(the,X,man(X), qterm(the,Y,cat(Y)), likes(X,Y), sleeps(X) The man that the cats like sleeps qterm(the,X, man(X)), qterm(the,Y, cat(Y)), likes(Y,X), sleeps(X)
QLF bits qterm(Quantifier, Variable, Predicate) 1-place predicates for nouns: cat(X) man(X) 1- and 2- place predicates for verbs likes(X,Y) sleeps(X)
Problem 1: Optional Determiners det(plural, qterm(null, Var, Pred), Pred, Var) --> []. det(Number, qterm(Det, Var, Pred), Pred, Var) --> [Det], {det(Det, Number)}. det(a, singular). det(all, plural). det(an, singular). det(each, singular). det(every, singular). det(the, _).
Problem 2: Noun Phrase Adjectives % Adjective Phrase Rules adjp_star(_, true) --> []. adjp_star(Var, ResultQLF) --> adjp(Var, AdjpQLF), adjp_star(Var, RestQLF), {conjoin(AdjpQLF, RestQLF, ResultQLF)}. % conjoin two LFs conjoin(true, LF, LF):- !. conjoin(LF, true, LF):- !. conjoin(LF1, LF2, (LF1,LF2)).
Problem 2 (cont) adjp(Var, ResultQLF) --> advp_star(AdjQLF, ResultQLF), [Adj], {adj(Adj), AdjQLF =.. [Adj, Var]}. advp_star(AdjQLF, AdjQLF) --> []. advp_star(AdjQLF, ResultQLF) --> advp(AdjQLF, AdvpQLF), advp_star(AdjQLF, RestQLF), {conjoin(AdvpQLF, RestQLF, ResultQLF)}.
Problem 2 (cont) advp(QLF, AdvQLF) --> [Adv], {adv(Adv), AdvQLF =.. [Adv, QLF]}. adv(very). adv(surprisingly). adj(big). adj(fat). adj(fluffy). adj(furry). adj(old). adj(young).
Problem 2 (cont) np_base(Case, third, Number, GapsIn, GapsOut, ResultQLF, Var) --> det(Number, QLF, Pred, Var), adjp_star(Var, AdjpQLF), n(Number, Pred, Var), {conjoin(QLF, AdjpQLF, NextQLF)}, np_right_modifiers(Case, third, Number, GapsIn, GapsOut, Var, RMQLF), {conjoin(NextQLF, RMQLF, ResultQLF)}.
Problem 3: Proper Names np_base(_Case, third, singular, Gaps, Gaps, true, ProperName) --> [ProperName], {proper_name(ProperName)}. proper_name(john). proper_name(mary). proper_name(molly). proper_name(me).
Problem 4: Pronouns np_base(Case, Person, Number, Gaps, Gaps, qterm(pro, Pronoun, ProVar), ProVar) --> [Pronoun], {pronoun(Pronoun, Person, Number, Case)}. pronoun(i, first, singular, nom). pronoun(me, first, singular, acc). pronoun(we, first, plural, nom). pronoun(us, first, plural, acc). pronoun(you, second, plural, _Case). pronoun(he, third, singular, nom). pronoun(him, third, singular, acc). pronoun(she, third, singular, nom). pronoun(her, third, singular, acc). pronoun(it, third, singular, _Case). pronoun(they, third, plural, nom). pronoun(them, third, plural, acc).
Problem 5: PP modifiers to NP and VP np_right_modifiers(_Case, _Person, _Number, Gaps, Gaps, _Var, true) --> []. np_right_modifiers(Case, Person, Number, GapsIn, GapsOut, Var, QLF) --> np_right_modifier(Case, Person, Number, GapsIn, GapsNext, Var, ThisQLF), np_right_modifiers(Case, Person, Number, GapsNext, GapsOut, Var, RestQLF), {conjoin(ThisQLF, RestQLF, QLF)}. np_right_modifier(Case, Person, Number, GapsIn, GapsOut, Var, QLF) --> relative_clause(Case, Person, Number, GapsIn, GapsOut, Var, QLF). np_right_modifier(_Case, _Person, _Number, GapsIn, GapsOut, Var, QLF) --> pp(Var, GapsIn, GapsOut, QLF).
Problem 5: PP pp_base(Var, GapsIn, GapsOut, ResultQLF) --> [Prep], {preposition(Prep)}, np(acc, _Person, _Number, GapsIn, GapsOut, QLF, NPVar), {Rel =.. [Prep, Var, NPVar], conjoin(QLF, Rel, ResultQLF)}.
Problem 5 – PP modifying VP vp_right_modifiers(Pred, Gaps, Gaps, Pred) --> []. vp_right_modifiers(Pred, GapsIn, GapsOut, QLF) --> vp_right_modifier(Pred, GapsIn, GapsNext, ThisQLF), vp_right_modifiers(Pred, GapsNext, GapsOut, RestQLF), {conjoin(ThisQLF,RestQLF, QLF)}. vp_right_modifier(Pred, GapsIn, GapsOut, QLF) --> pp(Pred, GapsIn, GapsOut, QLF).
Problem 5 (cont) vp_base(Person, Number, GapsIn, GapsOut, SubjVar, ResultQLF) --> v(Person, Number, intransitive, SubjVar^Pred), vp_right_modifiers(Pred, GapsIn, GapsOut, ResultQLF). vp_base(Person, Number, GapsIn, GapsOut, SubjVar, QLF) --> v(Person, Number, transitive, SubjVar^ObjVar^Pred), np(acc, _, _Number, GapsIn, GapsNext, ObjQLF, ObjVar), vp_right_modifiers(Pred, GapsNext, GapsOut, PredQLF), {conjoin(ObjQLF, PredQLF, QLF)}.
Problem 6: Copula vp_base(Person, Number, GapsIn, GapsOut, SubjVar, QLF) --> [Copula], {copula(Copula, Person, Number, _)}, predicates(SubjVar, Number, GapsIn, GapsOut, QLF). predicate(SubjVar, Number, GapsIn, GapsOut, ResultQLF ) --> np(acc, _Person, Number, GapsIn, GapsOut, QLF, PredVar), {conjoin(QLF, equals(SubjVar, PredVar), ResultQLF)}. predicate(SubjVar, _Number, Gaps, Gaps, QLF ) --> adjp(SubjVar, QLF). predicate(SubjVar, _Number, GapsIn, GapsOut, QLF ) --> pp(SubjVar, GapsIn, GapsOut, QLF).
Problem 7: Indirect Objects vp_base(Person, Number, GapsIn, GapsOut, SubjVar, QLF) --> v(Person, Number, indirect, SubjVar^ObjVar^IndirObjVar^Pred), np(acc, _, _, GapsIn, Gaps1, ObjQLF, ObjVar), [to], np(acc, _, _, Gaps1, Gaps2, IndirObj, IndirObjVar), {conjoin(ObjQLF, IndirObj, ObjQLFs)}, vp_right_modifiers(Pred, Gaps2, GapsOut, PredQLF), {conjoin(ObjQLFs, PredQLF, QLF)}. v(gives, singular, indirect, X^Y^Z^gives(X,Y,Z)). v(give, plural, indirect, X^Y^Z^gives(X,Y,Z)). v(sells, singular, indirect, X^Y^Z^sells(X,Y,Z)). v(sell, plural, indirect, X^Y^Z^sells(X,Y,Z)).
Problem 8: Dative Transformation vp_base(Person, Number, GapsIn, GapsOut, SubjVar, QLF) --> v(Person, Number, indirect, SubjVar^ObjVar^IndirObjVar^Pred), np(acc, _, _, GapsIn, Gaps1, IndirObj, IndirObjVar), np(acc, _, _, Gaps1, Gaps2, ObjQLF, ObjVar), {conjoin(ObjQLF, IndirObj, ObjQLFs)}, vp_right_modifiers(Pred, Gaps2, GapsOut, PredQLF), {conjoin(ObjQLFs, PredQLF, QLF)}.
Problem 9: Conjunction • Handled conjoined s, np, n, and vp s(GapsIn, GapsOut, QLF) --> s_base(GapsIn, GapsTemp, SQLF), conjunction, s(GapsTemp, GapsOut, RestQLF), {conjoin(SQLF, RestQLF, QLF)}. s(GapsIn, GapsOut, QLF) --> s_base(GapsIn, GapsOut, QLF). s_base(GapsIn, GapsOut, QLF) --> np(nom, Person, Number, GapsIn, GapsNext, NP, NPVar), vp(Person, Number, GapsNext, GapsOut, NPVar, VP), {conjoin(NP, VP, QLF)}.
Problem 9 (cont) np(Case, Person, Number, GapsIn, GapsOut, QLF, SemanticVar) --> np_base(Case, Person, Number, GapsIn, GapsOut, QLF, SemanticVar). np(Case, Person, plural, GapsIn, GapsOut, QLF, SemanticVar) --> np_base(Case, Person, _, GapsIn, GapsTemp, QLF1, Sem1), [and], np(Case, Person, _, GapsTemp, GapsOut, QLF2, Sem2), {conjoin(QLF1, QLF2, TempQLF), conjoin(TempQLF, and(Sem1, Sem2, SemanticVar), QLF)}. np(Case, Person, Number, GapsIn, GapsOut, QLF, SemanticVar) --> np_base(Case, Person, Number, GapsIn, GapsTemp, QLF1, Sem1), [or], np(Case, Person, Number, GapsTemp, GapsOut, QLF2, Sem2), {conjoin(QLF1, QLF2, TempQLF), conjoin(TempQLF, or(Sem1, Sem2, SemanticVar), QLF)}.
Iterative Deepening • Addresses depth-first run-away • Or exploring an infinite part of the search tree first • If the program has a solution, ID will help you find it • Puts a depth bound on the computation • Increases depth bound when no solutions are found
Successor Arithmetic • Can we do arithmetic, fully reversible, in pure Prolog? • Add a depth bound to each predicate: is_number(0). is_number(s(Number)):- is_number(Number). • Becomes is_number(0, s(_Bound)). is_number(s(Number), s(Bound)):- is_number(Number, Bound).
Successor Arithmetic (cont) add(0, Sum, Sum):- is_number(Sum). add(s(Addend1), Addend2, s(Sum)):- add(Addend1, Addend2, Sum). • Becomes add(0, Sum, Sum, s(Bound)):- is_number(Sum, Bound). add(s(Addend1), Addend2, s(Sum), s(Bound)):- add(Addend1, Addend2, Sum, Bound).
Successor Arithmetic (cont) % mult(?Term1, ?Term2, ?Product) mult(Term1, Term2, Product):- start(Start), mult_id(Term1, Term2, Product, Start). mult_id(Term1, Term2, Product, DepthBound):- mult(Term1, Term2, Product, DepthBound). mult_id(Term1, Term2, Product, DepthBound):- increment(DepthBound, Increment), add(DepthBound, Increment, NewDepthBound), mult_id(Term1, Term2, Product, NewDepthBound). start(s(0)). increment(Bound, s(Bound)).
Problems • Wastes a lot of work • Returns duplicate results • Never terminates • Bounded computations that terminate without ID aren’t terminating now mult(s(s(0)), s(s(0)), X) • We can address the returning duplicate results issue, and stay in ‘pure Prolog’
Counting deductions • Put a bound not only on depth, also on number of computational steps • Use threading to keep track of how many steps have been used • Only return a solution if it was found for the first time during this iteration • That is, is required Depth < Steps < NextDepth steps, • Or, RemainingDepth < Increment
mult/3 (again) % mult(?Term1, ?Term2, ?Product) mult(Term1, Term2, Product):- start(Start), mult_id(Term1, Term2, Product, Start). mult_id(Term1, Term2, Product, DepthBound):- mult(Term1, Term2, Product, DepthBound, RemainingBound), increment(Increment), less_than(RemainingBound, Increment). mult_id(Term1, Term2, Product, DepthBound):- increment(Increment), add(DepthBound, Increment, NewDepthBound), mult_id(Term1, Term2, Product, NewDepthBound).
mult/3 (cont) % mult(+Term1, +Term2, ?Product, +Bound) mult(0, Term, 0, s(Bound), RemainingBound):- is_number(Term, Bound, RemainingBound). mult(s(Term1), Term2, Product, s(Bound), RemainingBound):- mult(Term1, Term2, Partial, Bound, NextBound), add(Term2, Partial, Product, NextBound, RemainingBound). % add(?Addend1, ?Addend2, ?Sum, +Bound, -RemainingBound) add(0, Sum, Sum, s(Bound), RemainingBound):- is_number(Sum, Bound, RemainingBound). add(s(Addend1), Addend2, s(Sum), s(Bound), RemainingBound):- add(Addend1, Addend2, Sum, Bound, RemainingBound).
How much wasted work? • Assuming an increment of 1 • Assuming a balanced binary search tree, then • Searching a depth of N should take O(2^N) steps • The prior N-1 passes should take O(2^N-1 + 2^N-2 + … + 2^1) steps ~= O(2^N) steps • Under these assumptions, finding a solution using iterative deepening would take roughly twice as many steps • To reduce the wasted work, increase the increment
Termination? • Can we also solve the termination problem in pure Prolog? • No, because we can’t tell the difference between a genuine failure, and a failure due to the depth bound • To do this, we need to save some information across failure • For instance, using assert/1.
Iterative Deepening Pure Prolog Interpreter • We can solve this in a general way by writing a specialized Prolog interpreter in Prolog. • Using a new Prolog built-in clause/2: • clause(Goal, Body) When (Goal :- Body) is in the Prolog database, and is declared dynamic. • The Body for a fact is defined to be true. • For now, we will only worry about interpreting pure Prolog, no Prolog built-ins or cut.
id_call/[1,3] id_call(Goal):- start(Start), increment(Increment), id_call(Goal, Start, Increment). id_call(Goal, Bound, Increment):- retractall(more_search), id_interpret(Goal, Bound, Remaining), Remaining < Increment. id_call(Goal, Bound, Increment):- more_search, NextBound is Bound + Increment, id_call(Goal, NextBound, Increment).
id_interpret/3 id_interpret(_Goal, 0, _Remaining):- !, assert_once(more_search), fail. id_interpret(true, Bound, Remaining):- !, Remaining is Bound - 1. id_interpret((Goal1,Goal2), Bound, Remaining):- !, Bound1 is Bound - 1, id_interpret(Goal1, Bound1, Bound2), id_interpret(Goal2, Bound2, Remaining). id_interpret(Goal, Bound, Remaining):- NextBound is Bound - 1, clause(Goal, Body), id_interpret(Body, NextBound, Remaining).
Assignment • 3rd assignment to be handed in • Modify consult_file/1 to create id_consult_file/1, which adds iterative deepening to the pure Prolog programs as they are being read in. • Provide a definition for id_call/1 which calls the iterative deepening versions of these predicates. • Be sure to handle both regular Prolog clauses with :- and DCG rules with . • Test it by using your DCG from assignment 2 for generation.
More on Iterative Deepening • Iterative Deepening, and techniques like it, can be used to implement various other sorts of search problems • Shortest path though a graph • Minimal edit distance
edge(a,b). edge(b,c). edge(c,i). edge(b,d). edge(i,h). edge(d,h). edge(a,e). edge(f,d). edge(e,f). edge(f,g). edge(g,h). path(Start, Finish, [Start,Finish]):- edge(Start,Finish). path(Start, Finish, [Start|Rest]):- edge(Start, Next), path(Next, Finish, Rest). Shortest Path through a DAG
Minimal Edit Distance • The minimal edit distance between two sequences of symbols is the smallest number of insertions, deletions, and substitutions required to make the sequences identical. • The set of edits may not be unique minimal_edit_distance([a,b,c,d],[e,b,c,f,g], Edits). Edits = [substitute(a,e), insert(f), substitute(d,g)]
minimal_edit_distance/3 minimal_edit_distance(List1, List2, Edits):- minimal_edit_distance(List1, List2, 0, Edits). minimal_edit_distance(List1, List2, Cost, Edits):- edit_distance(List1, List2, Cost, Edits), !. minimal_edit_distance(List1, List2, Cost, Edits):- NextCost is Cost + 1, minimal_edit_distance(List1, List2, NextCost, Edits).
edit_distance/4 edit_distance([], [], _Cost, []). edit_distance(List1, List2, Cost, Edits):- Cost >= 0, edit(List1, List2, NewList1, NewList2, ThisCost, ThisEdit), append(ThisEdit, NextEdits, Edits), NewCost is Cost - ThisCost, edit_distance(NewList1, NewList2, NewCost, NextEdits). edit([Head|Tail1], [Head|Tail2], Tail1, Tail2, 0, []). edit([Head|Tail1], Tail2, Tail1, Tail2, 1, [delete(Head)]). edit(Tail1, [Head|Tail2], Tail1, Tail2, 1, [insert(Head)]). edit([Head1|Tail1], [Head2|Tail2], Tail1, Tail2, 1, [substitute(Head1, Head2)]).
Logic and Prolog • Review Propositional and Predicate Logic • Introduce Clausal Form • Resolution Rule and Theorem Proving • This material is from Ch. 10 in Clocksin and Mellish
Propositional Logic • The logic of statements • Lower case letters (p, q, r, s…) represent propositions, that may be either true or false. • A formula is either a proposition, or a combination of propositions combined using connectives like and, or, and not: p q p q • p p q p q
Theories and Theorems • A Theory is a set of axioms plus a set of inference rules • A formula is a theorem in the theory if it can be derived from the axioms using the inference rules • Modus Ponens: p p q q
Resolution Rule for Propositional Logic A1 …Ai AN A B1 … BN Ai A1 … B1 … BN … AN A
Only really need , , and • The connectives and can be reduced to , , and • ( p q ) is the same as (p q) • (p q) is the same as (p q) (p q)
Predicate Logic • Logic of Terms • Instead of atomic propositions, we now have predicates applied to 1st order terms • A 1st order term is either • a constant • a variable • a function symbol and N arguments, which are 1st order terms • An atomic formula is a predicate symbol followed by N arguments,which are 1st order terms • A formula can be an atomic formula, or built up using connectives , , , and and quantifiers and
Clausal Form • A 1st order formula can be expressed in a normal form clausal form, of the form ( (A1 A2 … An) :- (B1 B2 … Bn) (C1 C2 … Cn) :- (D1 D2 … Dn) …) • We’ll derive clausal form in a series of steps • Removing implications • Move negations inwards • Remove existential quantifiers • Moving universal quantifiers outwards • Distributing over • Putting into clauses
Step 1: Removing implications • Since we know that and can be expressed using only , , and , rewrite the formula to remove them x.man(x) human(x) becomes x. man(x) human(x)
Step 2: Moving negations inwards • We can use these equalities to move negations inwards such that we only negate atomic formulas • () = • () = x. = x. x. = x. • We will refer to negated atomic formulas as negative literals, and unnegated atomic formulas as positive literals.