prolog for linguists symbolic systems 139p/239p john dowding week 10, december 3, 2001...
TRANSCRIPT
Prolog for LinguistsSymbolic Systems 139P/239P
John Dowding
Week 10, December 3, 2001
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
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).
Iterative Deepening Translator
Turn something like this:
is_number(0).
is_number(s(Number)):-
is_number(Number).
Into something like this:
is_number(_, 0, _):-
!,
assert_once(more_search),
fail.
is_number(0, Bound, RestBound):-
RestBound is Bound – 1.
is_number(s(Number), Bound, RestBound):-
NextBound is Bound – 1,
is_number(Number, NextBound, RestBound).
Iterative Deepening Translator (cont)
id_consult_term((NT --> Rule)):-!,grammar_rule_body(Rule, Body, Start, End),make_nonterminal(NT, Start, End, Goal),id_consult_term((Goal :- Body)).
id_consult_term((:- Goal)):-!,call(Goal).
id_consult_term((Goal :- Body)):-!,id_add_base_clause(Goal),make_id_goal(Goal, NewGoal, Bound, RemainingBound),id_consult_body(Body, NewBody, NextBound, RemainingBound),assertz((NewGoal :- (NextBound is Bound - 1, NewBody))).
id_consult_term(Fact):-id_add_base_clause(Fact),make_id_goal(Fact, NewGoal, Bound, RemainingBound),assertz((NewGoal :- (RemainingBound is Bound - 1))).
Iterative Deepening Translator (cont)
id_consult_body((Body1, Body2), (NewBody1,NewBody2), Bound, RemainingBound):-
!,
id_consult_body(Body1, NewBody1, Bound, NextBound),
id_consult_body(Body2, NewBody2, NextBound, RemainingBound).
id_consult_body(Goal, NewGoal, Bound, Remaining):-
make_id_goal(Goal, NewGoal, Bound, Remaining).
id_add_base_clause/1
id_add_base_clause(Goal):-
functor(Goal, Functor, Arity),
NewArity is Arity + 2,
functor(BaseGoal, Functor, NewArity),
BoundArg is Arity + 1,
arg(BoundArg, BaseGoal, 0),
BaseBody = (!, assert_once(more_search), fail),
\+ clause(BaseGoal, BaseBody),
!,
asserta((BaseGoal :- BaseBody)).
id_add_base_clause(_Goal).
make_id_goal/4
make_id_goal(Goal, NewGoal, Bound, Remaining):-functor(Goal, Functor, Arity),NewArity is Arity + 2,functor(NewGoal, Functor, NewArity),BoundArg is Arity + 1,arg(BoundArg, NewGoal, Bound),arg(NewArity , NewGoal, Remaining),copy_n_args(Arity, Goal, NewGoal).
copy_n_args(0, _Goal, _NewGoal):-!.
copy_n_args(Index, Goal, NewGoal):-arg(Index, Goal, Arg),arg(Index, NewGoal, Arg),NextIndex is Index - 1,copy_n_args(NextIndex, Goal, NewGoal).
Could have just used make_nonterminal/4
Missed opportunity for generalization…
% this is the lazy way to do this using univ.
make_nonterminal(NT, Start, End, Goal):-
NT =.. List,
append(List, [Start,End], FullList),
Goal =.. FullList.
id_call/1
id_call(Goal):-start(Start),increment(Increment),make_id_goal(Goal, NewGoal, Bound, Remaining),id_call(NewGoal, Start, Bound, Remaining, Increment).
id_call(Goal, Bound, Bound, Remaining, Increment):-retractall(more_search),call(Goal),Remaining < Increment.
id_call(Goal, CurrentBound, Bound, Remaining, Increment):-more_search,NextBound is CurrentBound + Increment,id_call(Goal, NextBound, Bound, Remaining, Increment).
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
Clausal Form and Prolog
(A1 A2 … An) :- (B1 B2 … Bm)
Prolog is restricted to clauses where N <= 1. That is, Prolog cannot prove disjunctions
When M=0, we call this a fact.When N=0, we call this a goal.If M=0 and N=0, we have an empty clause, which is false.The restriction to definite clauses means Prolog cannot represent all first order theories. But, definite clauses are still powerful enough to prove theorems about all computable functions.
Prolog and Theorem Proving
Suppose that we want to prove that a formula B is a logical consequence of a consistent set of clauses S: S = {A1 … AN}
Then, adding B to S should make it inconsistentS’= {A1 … AN, :- B}
This is how Prolog proves theorems, it adds the negation of the Goal to the set of clauses, and proves that it is inconsistent.
Resolution Rule for Predicate Logic
A1 …Ai AN A B1 … BN Ai’
(A1 … B1 … BN … AN A)
If Ai unifies with Ai’, yielding the substitution function
Prolog Theorem Proving
Prolog uses a restricted form of the Resolution Rule as it’s only inference ruleStarting with a Goal (a disjunction of negative literals):- B1 … BN
One application of the resolution rule to the left-most negative literal, and a rule of the formB1’ :- C1 … CM, where B1 unifies with B1’, with substitution
Yields:- (C1 … CM B2 … BN)
This continues until the the empty clause (false) is derivedThe history of substitution functions is used to produce an “answer”, effectively a counter-example to the claim that B1 … BN is inconsistent.
DCG with yes/no and wh- questions
Yes/no questions feature AUX-inversionDoes John like Molly?
The AUX (do, be, can, have, will, etc.) moves before the subject, but still has to agree in number and person:Does he like Molly?
Do you like Molly?
Do they like Molly?
Does the woman like Molly?
Do the women like Molly?
We will add a force marker (decl, ynq, whq) to our QLFs.
Yes/no Questions
s_base(GapsIn, GapsOut, [decl, QLF]) --> declarative_sentence(GapsIn, GapsOut, QLF).
s_base(GapsIn, GapsOut, [ynq, QLF]) -->yes_no_question(GapsIn, GapsOut, QLF).
yes_no_question(GapsIn, GapsOut, QLF) -->aux(Person, Number, _Tense),np(nom, Person, Number, GapsIn, GapsNext, NP, NPVar),vp(_, _, GapsNext, GapsOut, NPVar, VP),{conjoin(NP, VP, QLF)}.
Wh-question
One way to analyse wh-questions is as a wh-noun phrase followed by a sentence containing a gap:Who likes Molly?
Who does Molly like?
English requires the AUX to be inserted for non-subject gaps:Who likes Molly?
Who does Molly like?
*Who Molly likes?
Wh-questions
wh_question(GapsIn, GapsOut, QLF) -->wh_np(Person, Number, NP, NPVar),yes_no_question([np_gap(_Case, Person, Number, NPVar)|GapsIn], GapsOut, YNQQLF),{conjoin(NP, YNQQLF, QLF)}.
wh_question(GapsIn, GapsOut, QLF) -->wh_np(Person, Number, NP, NPVar),declarative_sentence([np_gap(nom, Person, Number, NPVar)|GapsIn], GapsOut, DeclQLF),{conjoin(NP, DeclQLF, QLF)}.
Wh-NPs (cont)
I got lazy on the different forms of wh- NPs
wh_np(third, singular, qterm(wh, NPVar, who), NPVar) -->
[who].
wh_np(third, singular, qterm(wh, NPVar, what), NPVar) -->
[what].
Other things to talk about?
Any other questions?
Prolog compilation
1st argument indexing
Prolog Modules
Prolog Libraries
Or, if-then, if-then-else
statistics[0,2], and how to time things
Prolog compilation
Modern Prolog implementions use the Warren Abstract Machine (WAM) Invented by David Warren, one of the founders of Quintus
Combines to a portable byte-code, which is then interpreted
Some implementations (like sometimes SICStus) compile down to native code.
All modern Prolog implementations support first-argument indexing.
1st Argument indexing
Prolog treats the 1st argument to a predicate specially
Uses the functor, and arity of the 1st argument term to index the predicate
If you are expecting a particular argument position to be an input position,
And that argument differentiates the clauses of the predicates,
Then make that the 1st argument
1st Argument Indexing
Consider:list_length([], 0).list_length([_Head|Tail], N):-
list_length(Tail, PartialN),N is N + 1.And,
member(Element, [Element|_List]).member(Element, [_Head|List]):-
member(Element, List).
1st Argument Indexing (cont)
n(bed, singular, bed(X), X).n(beds, plural, bed(X), X).n(book, singular, book(X), X).n(books, plural, book(X), X).n(cat, singular, cat(X), X).n(cats, plural, cat(X), X).n(desk, singular, desk(X), X).n(desks, plural, desk(X), X).n(dog, singular, dog(X), X).n(dogs, plural, dog(X), X).
Prolog Modules
Keeps Prolog predicates in separate name spaces
File based
Optional
By default, predicates are in the module user.
Directives to define a module::- module(ModuleName, PredicateList).
defines a Module with a set of Public predicates
Directives to access predicates from another module::- use_module(ModuleName).
:- use_module(ModuleName, [Pred1/Arity1, …, PredN/ArityN]).
Modularized Tokenizer
Add to the top of tokenizer.pl (before any predicates):
:- module(tokenizer, [tokenize_file/2, tokenize/2]).
To use it in grammar.pl:
:- use_module(tokenizer, [tokenize/2]).
Prolog Libraries
Prolog has a large set of library predictes
All are defined using the Module system
The libraries are not defined in the Prolog library, and will vary significantly between Prolog versions
So, using libraries will make your Program less portable
Access a library with use_module:- use_module(library(ModuleName)).
:- use_module(library(ModuleName), PredicateList).
Special Syntax: or
Or uses the syntax (Goal1 ; Goal2) and creates a new choice point.Defined as though:(Goal1 ; _Goal2) :- call(Goal1).(_Goal1 ; Goal2):- call(Goal2).
Except that cuts in Goal1 or Goal2 are transparent through the ;/2.
Use these carefully because they can make your programs hard to read and understand.
Special Syntax: if-then
If-then uses the special syntax (Condition -> Goal)
Defined as though:(Condition -> Goal) :- call(Condition), !, call(Goal).
Except that any cuts in Condition or Goal are transparent through ->/2.
This is sometimes used for it’s little cut of any choice points in the Condition (called a snip).
This is not logical if-then, since if Condition fails, then (Condition ->Goal) fails.
Special Syntax: if-then-else
If-then-else uses the special syntax:(Condition -> Goal1; Goal2)
Defined as though:(Condition ->Goal1; _Goal2) :-
call(Condition),!,call(Goal1).
(_Condition ->_Goal1; Goal2) :-call(Goal2).
Except that cuts in Condition, Goal1, and Goal2 are transparent through ->/2 and ;/2.
Programming Tips on or, if-then, if-then-else
You never really need them, but they’re handy when your lazy.
They can make your program harder to read and understand.
Use white-space to make the special syntax stand out.( goal1(…)
; goal2(…))
(condition(…) ->
result(…)
; otherwise(…))
Never nest or combine them.
If the Condition contains only built-in predicates, then the compiler can often be clever when compiling them.
Timing and Statistics
Built-in statistics/1 gives lots of information about time and memory use.
statistics/2 gives you information about specific values.
statistics(runtime, [BaseTime, TimeSinceLastCall]).
Times are measured in milliseconds
Timing Predicates
% time(+Goal, +Iterations, -RunTime).time(Goal, Iterations, RunTime):- statistics(runtime, [StartTime, _]), iterate(Iterations, Goal), statistics(runtime, [EndTime, _]), RunTime is (EndTime - StartTime) / Iterations.
iterate(0, _):- !.iterate(Counter, Goal):- call(Goal), !, NextCounter is Counter - 1, iterate(NextCounter, Goal).