Create a DCG Parser in PROLOG - parsing

I have to implement an a context-free parser in PROLOG that uses a grammar that can generate:
I saw a tutorial.
I went in a library.
In library a tutorial I saw.
(I know it's not correct grammatically, but I need to see how to match the pattern)
I receive an input as a query - let's suppose it it the first sentence - and I have to print the number of applications of rules for a successful parsing, false otherwise.
In order to achieve this, I found these grammars:
s(X,Z):- vp(Y,Z), np(X,Y).
np(X,Z):- det(X,Y), n(Y,Z).
np(X,Z):- det(X,Y), n(Y,Z), np(X,Z).
vp(X,Z):- det(X,Y), v(Y,Z).
det([i|W],W).
det([a|W],W).
det([in|W],W).
n([tutorial|W],W).
n([library|W],W).
v([went|W],W).
v([saw|W],W).
It works for the first 2 sentences, but I don't know how to make it work for the last one and I don't know how to print the number of applications of rules for a successful parsing.
Thank you!

This will help with the number of applications of rules for a successful parsing. However, as you can see, it will always be the same number as the quantity of words in the sentence. What I did was to implement a 'counter' in the parameters of each rule and each time a 'base rule' succeed it increase the value of the 'counter'.
det([i|W], W, A, R) :- R is A + 1.
det([a|W], W, A, R) :- R is A + 1.
det([in|W], W, A, R) :- R is A + 1.
n([tutorial|W], W, A, R) :- R is A + 1.
n([library|W], W, A, R) :- R is A + 1.
v([went|W], W, A, R):- R is A + 1.
v([saw|W], W, A, R):- R is A + 1.
np([], R, R).
np(X, A, R2):- det(X, Y, A, R), np(Y, R, R2).
np(X, A, R3):- det(X, Y, A, R), n(Y, Z, R, R2), np(Z, R2, R3).
vp(X, Z, R2):- det(X, Y, 0, R), v(Y, Z, R, R2).
s(X, R2):- atomic_list_concat(L,' ', X), vp(L, Z, R), np(Z, R, R2), !.
Here are the results. Results.
As you can see the last sentence still failing, that is because if you follow the flow of the algorithm or calls of it you can see that the rule 's' calls the rule 'vp' which only admits a 'det' follow by a 'v', so if you see the first word of third sentence which is 'In', 'det' in 'vp' will work, but the next word that is 'library' will not success on 'v', because 'library' is not a verb, so that' s why it fail. To conclude, if you want the third sentence to succeed you will have to do some changes to your grammars.
By the way there is better way, probably a lit bit more complex to achieve, but once you understand how to use it, will be faster to work and easier to create a complex grammar this by using Prolog DCG https://www.swi-prolog.org/pldoc/man?section=DCG. I mentioned in case you did not know about this.

Related

Take out common variables using Z3

I have a formlua in DNF form, say:
abcx + abcy + abz
Is there any way to take out the common variables, to get the follwing formula:
ab (cx + cy + z)
A followup question, can it be done recursively, like
ab ( c(x+y) + z)
Sure.. Here's one way:
from z3 import *
a, b, c, x, y, z = Ints('a b c x y z')
print simplify(a*b*c*x + a*b*c*y + a*b*z, hoist_mul=True)
This prints:
a*b*(c*(x + y) + z)
which is exactly what you're looking for.
And for your next question, how did I find about hoist_cmul=True argument? Simply run:
help_simplify()
at your Python prompt, and it'll list you all the options simplify takes.
Note that you should in general not count on what the simplifier will give you. It's mostly heuristic driven, and in the presence of other terms what you get may not match what you expected. (It'll of course still be an equivalent expression.) There's no notion of "simplest" when it comes to arithmetic expressions, and what you consider simple and what z3 considers simple may not necessarily match.

Closure of rational languages under morphism

I have to prove that the set of rational or regular languages is closed by morphism on their alphabet.
i.e. that the image of a rational language by a morphism is still rational.
h being a morphism from Σ to Σ', my idea is to start with an automaton A and to construct an automaton A' which recognizes the language h(L(A)).
I use the same initial and final states then, for any transition (q, a, q') in A, I consider 3 cases :
if h(a) = ε I add the states q, q' (if they do not already exist in A') and an ε transition (q, ε, q')
if h(a) = b ∈ Σ', I add the states q, q' (if they do not already exist in A') and a transition (q, b, q')
if h(a) = b_1b_2...b_n ∈ Σ'*, I add the states q, q' (if they do not already exist in A') plus n-1 new states and n transitions from (q, b_1, q_1) to (q_{n-1}, b_n, q')
Then it's "easy" to prove that h(L(A)) is included in L(A') following the construction steps, however I'm struggling to prove the converse, i.e. that L(A') is included in h(L(A))

Agda: How to apply +-right-identity to match types

Somewhere in my code, I have a hole which expects a natural number, let's call it n for our purposes. I have a function which returns me a n + 0.
Data.Nat.Properties.Simple contains a proof +-right-identity of the following type:
+-right-identity : ∀ n → n + 0 ≡ n
I'm not familiar enough with the Agda syntax and stdlib yet to know how to easily use this proof to convince the type checker that I can use my value.
More generally, how to I use a relation x ≡ y to transform a given x into y?
I found an answer in this thread: Agda Type-Checking and Commutativity / Associativity of +
For future readers, the keyword I was looking for was rewrite.
By appending rewrite +-right-identity n to the pattern matching (before the = sign), Agda "learned" about this equality.

How to prove commutative property for rational number in Agda?

I am trying to prove commutative property for agda. I tried to explore the standard library but there is lot of complex thing which i could not understand.
I tried in this way --
comm : (a b : Q) -> (a + b) === (b + a)
the problem here is + which is not defined over Q in library. Can't we proof this without defining + over Q.
Please guide me.
You cannot prove this without first defining +.
If you get confused exploring the standard library I suggest you try to prove something easier first, in order to become more acquainted with Agda, before tackling this.
Of course you can't prove commutativity of an undefined function _+_; as a stupid counter-example, would you expect to be able to prove (a - b) == (b - a)? If not, why not? _-_ is just as much of an undefined function as _+_ at this point; it just has a different name...
Note that you can define addition for ℚ using elementary school math:
n ÷ p + m ÷ q = (n * q + m * p) ÷ (p * q)
and simplifying it by dividing both n * q + m * p and p * q with their GCD. I have already explained the details of this last step in this answer.

Find all possible paths w/o loops in Graph in Prolog

I got an homework assignment for my logic course, but more or less don't have any clue how to solve it...
With a query like
?- find(a,[r(a,[b,d]),r(b,[a,c,e]),r(c,[b]),r(d,[a,e]),
r(e,[b,d,f]),r(f,[e,g]),r(g,[f])],Path).
Prolog should return all possible paths in the given graph. The terms r(X,List) define the graph, meaning that the nodes in List can be reached from node X. In this case, the output would be:
Path = [a,b,c] ;
Path = [a,b,e,d] ;
Path = [a,b,e,f,g] ;
Path = [a,d,e,b,c] ;
Path = [a,d,e,f,g] ;
false.
Although I get the hang of the numerous solutions here on SE and on the web in general to similar problems, I'm somehow too dumb to figure out how to work with the definition of the graph in this assignment.
I figure that find(Start,...) should be called recursively with all members of the list in r(Start,List), but as a total newbie to Prolog(we just did the standard family tree stuff...) I don't know how to do that.
Any help would be really appreciated. I'm aware of the fact that I don't have much to start with, but I already spent half a night trying to figure something out and up to now I don't have a clue.
/Edit:
For starters, I think I'll need some kind of base case to abort the recursion.
I think it should be either
find([],_,_).
because I guess that the last recursive call wouldn't have anything to start with, or
find(_,[],_).
assuming that the list of the terms defining adjacent nodes should be empty when the program finished processing it.
Now the actual call. Probably something like
find(Start,[r(Start,[Adjacent|Restadj])|Rest],Path):-
find(???).
My problems here are the following:
-How do I make the program use the members of the list in the r(...) term as the next Start?
-How do I check if a node has already been "visited"/ How can I remove a node from a specific list in r
-How do I put the found nodes into the Path list? Simply append? Or execute the recursive call with something like [Path|Start]?
As you see, it's not much. Some suggestive questions would be nice, since Prolog seems quite interesting and therefore fun to learn...
After spending some time with the neat PDT-Eclipse trace tool, I think I understood what the program is doing. What I dont't get at this point is why the last node always gets lost. After backtracking fails, for example because r(c,[b]) is the next found term and memberchk(b,[b]) fails because of the negation(that's what I thing + does) and no other term with r(c,X) can be found, it starts over with looking for other possibilities to go from node b, which has adjacent nodes left in r(b,[...]). But why does the program forget to put node c into the Path list? Is there a possibility to do some kind of if-then-else in case
member(r(Node, Adjacent), Graph),
member(AdjNode, Adjacent),
\+ memberchk(AdjNode, Seen),
fails, to still append the last node to Path?
I suspect what's tripping you up here is that instead of getting the data out of the database, you're having to find it from within an explicit data structure. A first crack at this might look like this:
find(_, _, []).
find(Node, Graph, [Node|Path]) :-
member(r(Node,Adjacent), Graph),
member(AdjNode, Adjacent),
find(AdjNode, Graph, Path).
See how I'm using member/2 to find data from within the graph. This solution isn't correct though, because it loops. An improvement might be this:
find(Node, Graph, Path) :- find(Node, Graph, Path, []).
find(_, _, [], _).
find(Node, Graph, [Node|Path], Seen) :-
member(r(Node, Adjacent), Graph),
member(AdjNode, Adjacent),
\+ memberchk(AdjNode, Seen),
find(AdjNode, Graph, Path, [Node|Seen]).
This one is basically the same as the above version except it has a "seen" list to track where it has already been. This still doesn't produce the output you want, but I think it will be enough to get you on the right track.
Edit in response to your edit,
For starters, I think I'll need some kind of base case to abort the recursion.
Yes. I chose your first case because I don't think you can safely "consume" the graph during traversal. I suppose you could use select/3 in lieu of member/2 and pass the graph-without-this-node onward. That might be an interesting thing to try (suggestion!).
How do I make the program use the members of the list in the r(...) term as the next Start?
As demonstrated, use member/2 to retrieve things from the graph. It's funny, because you used the exact word for the predicate you need. :)
How do I check if a node has already been "visited"/ How can I remove a node from a specific list in r
As demonstrated in my second set of code, you have another parameter for your auxiliary predicate, and use memberchk/3 or member/3.
How do I put the found nodes into the Path list? Simply append? Or execute the recursive call with something like [Path|Start]?
I went with the recursive call. append/3 would be more expensive.
Edit: Using findall/3 per Will's comment, we can find all the paths at once:
all_paths(From, Graph, Paths) :- findall(Path, find(From, Graph, Path), Paths).
You can invoke this like so:
?- all_paths(a, [r(a,[b,d]),r(b,[a,c,e]),r(c,[b]),r(d,[a,e]),
r(e,[b,d,f]),r(f,[e,g]),r(g,[f])], AllPaths).
I haven't tested that but it should work.
building on the excellently clear code by Daniel Lyons, a breadth first search:
all_paths(Node, Graph, Paths) :-
bfs(Graph, [[Node]-[]], R, []), % or dfs(...)
maplist( fst, Paths, R).
fst(A, A-_). % utility
pair(B, A, A-B). % helpers
add(LS,H,[H|LS]). %
bfs(_G, [], Z, Z). % queue is empty
bfs(Graph, [H|Q], [H|R], Z) :-
H = Path-Seen, Path = [Node|_],
findall( Next, member(r(Node, Next), Graph), NS),
flatten_diff( NS, Seen, WS), % working set of nodes
maplist( add(Path), WS, PS), % new paths
maplist( pair([Node|Seen]), PS, QH), % new addition to the queue
%% append( QH, Q, Q2), % DFS
append( Q, QH, Q2), % BFS
bfs(Graph, Q2, R, Z).
(not tested). flatten_diff(A,B,C) should flatten list of lists A, while removing the elements of it that appear also in list B, producing the list C as the result.
As PeterPanter has noticed, Daniel Lyons's code needs a little tweaking, to not exclude the very last node in its resulting paths.
find(Node, Graph, [Node|Path]) :- find(Node, Graph, Path, []).
find(_, _, [], _).
find(Node, Graph, [AdjNode|Path], Seen) :-
member(r(Node, Adjacent), Graph),
member(AdjNode, Adjacent),
\+ memberchk(AdjNode, Seen),
find(AdjNode, Graph, Path, [Node|Seen]).
There are no empty paths produced now, and it works as expected:
11 ?- find(a,[r(a,[b,d]),r(b,[a,c,e]),r(c,[b]), r(d,[a,g]),
r(e,[b,d,f]),r(f,[e,g]),r(g,[f])], Path).
Path = [a] ;
Path = [a, b] ;
Path = [a, b, c] ;
Path = [a, b, e] ;
Path = [a, b, e, d] ;
Path = [a, b, e, d, g] ;
Path = [a, b, e, d, g, f] ;
Path = [a, b, e, f] ;
Path = [a, b, e, f, g] ;
Path = [a, d] ;
Path = [a, d, g] ;
Path = [a, d, g, f] ;
Path = [a, d, g, f, e] ;
Path = [a, d, g, f, e, b] ;
Path = [a, d, g, f, e, b, c] ;
false.

Resources