prolog dcg restriction - prolog

I would like to use DCGs as a generator. As of now, the syntax is
s-->a,b.
a-->[].
a-->a,c.
c-->[t1].
c-->[t2].
b-->[t3].
b-->[t4].
I would like to generate all s where the length of a is < someNumber.
Using ?- phrase(a,X),length(X,Y),Y<4. i can get all a with less than 4 items. However, when all combinations are exhausted, the system (SWI-Prolog 6.2.5) seems to stall.
Sometimes ago, a similar question was asked here. However, being new to Prolog i am not able to make it work with the grammar above. Any ideas?
Update: There was a comment by (canrememberthename) which got deleted, somehow. Anyway, it was suggested to use between(1,4,Y),length(X,Y),phrase(a,X). to set limits. This worked nicely, after changing my code to a-->c,a.

The first steps into Prolog are often a bit difficult. But you are on the right track:
Currently, your problem is that you get some answers/solutions as expected but then the system stalls ; in fact, it is in an infinite loop. You found that out by patiently hitting SPACE. That might work with a tiny set of solutions but it will be tedious with a larger one. Just think of looking at all sentences shorter than 20.
There is a simple keyboard and carpal tunnel friendly way to simulate hitting space: Simply add a goal false at the end like so:
?- phrase(a,X),length(X,Y),Y<4, false.
What could Prolog answer to such a question? Since there is false at the end, Prolog has not much choice: Either it answers false itself ; or it loops (or produces an error or produces some side-effect). And in this case, it loops.
Now, we can narrow down the problem, by adding further false goals into your program.
?- phrase(a,X),length(X,Y),false, Y<4, false.
loops.
To make this better readable, I will only use one false goal and strike through the remaining part of the query:
?- phrase(a,X),length(X,Y),false, Y<4.
loops.
Let's reduce that even further:
?- phrase(a,X),false,length(X,Y),Y<4.
loops.
They all loop! But we get some interesting insight: Since these false-adorned queries do not terminate, also the original program does not terminate. So when you look at a query, and the very first goal does not terminate for itself, it follows that the entire query will not terminate (see the fine print at the end for more).
Therefore: You have to address the first goal somehow!
My first attempt is to exchange length and phrase:
?- length(X,Y), phrase(a,X), Y<4.
Will this work? Just look at the first goal which loops:
?- length(X,Y), false, phrase(a,X), Y<4.
loops.
So this again will not terminate.
You have to change the program again:
?- between(1,3,Y), length(X,Y), false, phrase(a,X).
false.
So this terminates. And if there will be a termination problem, phrase(a,X) has now to take the blame:
?- between(1,3,Y), length(X,Y), phrase(a,X), false.
loops.
You might be tempted to look at actual answers:
?- between(1,3,Y), length(X,Y), phrase(a,X).
Y = 1, X = [t1]
; Y = 1, X = [t2]
; resource_error(local_stack). % ERROR: Out of local stack
And you might conclude that this behavior is worse than your original definition. After all, we have now less answers than before. But exactly that kind of reasoning does not help you to improve termination: With false both are of the same kind of incorrectness and you have to address this first. So by hiding the answers you can better concentrate on the rest.
But why is your grammar problematic? We can continue with our technique to insert goals false to narrow it down. But this time within your grammar. Your program adorned with goals false is called a failure-slice.
Just a practical remark: When I insert goals false into your program, I will save the program and type make in SWI: In this manner the program is rapidly recompiled.
After trying a bit, I got the following minimal failure-slice. Note that within a DCG, false has to be written as {false}.
?- between(1,3,Y), length(X,Y), phrase(a,X), false
s--> {false}, a,b.
a-->[], {false}.
a-->a,{false}, c.
c-->{false}, [t1].
c-->{false}, [t2].
b-->{false}, [t3].
b-->{false}, [t4].
Almost all your codebase are belong to false! So you have to address the tiny visible remaining part. It would be pointless to change something somewhere else. This a --> a, ... must be changed! And in fact changing it to a --> c, s solves the problem.
Why did you write a --> a, c. in the first place? I suspect that you wanted to enumerate all solutions in a fair manner. The new version doesn't:
?- phrase(a,X).
X = []
; X = [t1]
; X = [t1,t1]
; X = [t1,t1,t1]
; X = [t1,t1,t1,t1]
; X = [t1,t1,t1,t1,t1]
; X = [t1,t1,t1,t1,t1,t1]
; X = [t1,t1,t1,t1,t1,t1,t1]
; ... .
That looks very intimidating. In fact, it looks wrong. Doesn't it? But don't let you confuse by this: We have here an infinite set of sentences. So the only correct response by Prolog is to produce infinitely many answers. For, if they would be finite, some list would be missing! But of course, you want to see them enumerated in a fair manner. To get this, simply write:
?- length(X,N), phrase(a,X).
X = [], N = 0
; X = [t1], N = 1
; X = [t2], N = 1
; X = [t1,t1], N = 2
; X = [t1,t2], N = 2
; X = [t2,t1], N = 2
; X = [t2,t2], N = 2
; X = [t1,t1,t1], N = 3
; ... .
This is a major point in Prolog programs: Always go for the best (possible) termination property first. And do not look at the precise order how Prolog enumerates answers. For, if a program has better termination properties, it is next-to-trivial to use it to enumerate all solutions in a fair manner. But: A program that enumerates infinitely many solutions in a fair manner at the expense of termination cannot be used in more interesting cases.
Fine Print See this answer.

the nonterminal a//0 is both left recursive and 'epsilon' (generate the empty sequence), and phrase/2 will loop immediately after the empty production.
You can solve your problem bounding list' length:
?- between(1,4,Y),length(X,Y),phrase(a,X).
and, as you have already done, removing the left recursion.

Related

Prolog "," operator not working as intended?

I'm super unfamiliar with prolog but I need it for a school project. So, if I use unclear language or incorrect terms, please be understanding. My trouble is that my code is not working how I intended it at all. Here is a bit of my code in the knowledge base:
output(mn,hp,hp,hp).
output(hn,mn,hp,lp).
output(ln,hp,mp,hp).
input(lp,mp,mp,lp):-output(hn,mn,hp,lp).
And here is my query:
?- input(X,mp,mp,lp),output(hn,mn,hp,lp).
I would expect it to return X = lp, as the if-then sentence in the knowledgebase states. However, it returns X=hp, X=mp, and X=lp, which is not what I intended.
I used "trace" to see how it reasoned, and I saw that it called for other outputs. I thought the "," operator meant "and", so it would only look for the output with the values (hn,mn,hp,lp). How can I get my code to only look for if-then sentences with the correct output values? Do I have an incorrect understanding of the "," operator or is the problem due to my knowledge base?
EDIT: Here is the link to my code in SWISH: https://swish.swi-prolog.org/p/i%20wanna%20bash%20my%20head%20in.pl#&togetherjs=AQ4zzkAQk4
EDIT 2: My aim with this project is the following: I'm trying to explain a fuzzy cognitive map with this program. I want the program to determine the values of missing input concepts given the output concepts. So, I need the program to look for the clause with the correct values in output(), and then determine the values inside the relevant input().
With the whole program (88 lines):
?- output(hn,mn,hp,lp).
true.
So this can be completely disregarded in your query - it's true no matter what (constants only) and has no further effect.
Then what counts is:
?- input(X,mp,mp,lp).
X = mp ;
X = lp ;
X = hp ;
false.
No need for Prolog. Use text search to confirm that:
$ grep "mp,mp,lp" proggy.pl
input(mp,mp,mp,lp):-output(mn,hp,hp,hp).
input(mp,mp,lp,hp):-output(hn,lp,hp,hp).
input(mp,mp,lp,mp):-output(hn,mp,hp,hp).
input(lp,mp,mp,lp):-output(hn,mn,hp,lp).%this one is a pain in my ass
input(hp,mp,mp,lp):-output(ln,hp,mp,hp).
input(mp,mp,lp,lp):-output(ln,hp,mp,hp).
of the above, these three lines
input(mp,mp,mp,lp):-output(mn,hp,hp,hp).
input(lp,mp,mp,lp):-output(hn,mn,hp,lp).%this one is a pain in my ass
input(hp,mp,mp,lp):-output(ln,hp,mp,hp).
<-><------>
X MATCH
will match the query: 3 results with X from mn,hn,ln.
Working as expected.
Here is a simpler query:
?- input(X,mp,mp,lp).
This succeeds three times:
X = mp ;
X = lp ;
X = hp ;
false.
I assume you understand why this is: There are several matching clauses for input with different values for X and with bodies that succeed.
Now, you are right that , is conjunction, and that in general a goal A, B can succeed less often than A or B individually, but never more often. A and B typically constrain each other. But, very importantly: If A and B do not share any variables, then they are independent. In this case, the sequence of answers will be the Cartesian product of the answers for A and the answers for B. If A alone succeeds N times and B alone succeeds M times, then A, B without a shared variable will succeed N * M times.
You can only expect to see fewer solutions for a query of the form ?- input(X,mp,mp,lp), OtherGoal. if OtherGoal also contains an occurrence of X. In your query this is not the case. The first conjunct succeeds three times, the second succeeds once, so overall you have 3 * 1 = 3 successes, as you observed.
So the question is: What are you trying to express with your query? You cannot express something like "give me solutions for input, but only by applying certain clauses of output".

Finding whether a number is a multiple of another

Looking at the code below:
multiple(X,0).
multiple(X,Y) :- lt(0,X), lt(0,Y), diff(Y,X,D), multiple(X,D).
There happens to be something wrong. For your reference:
lt/2 is whether the first argument is less than the second.
diff/3 is whether the third argument is equal to the first argument minus the second.
lt/2 and diff/3 are defined correctly.
Is there a logical mistake in the definition? Is assuming that 0 is the multiple of every number problematic or is the logical mistake somewhere else? I get correct answers but the query goes to infinite loop I think.
EDIT:
here are the other definitions.
natNum(0).
natNum(s(X)) :- natNum(X).
lt(0,s(X)) :- natNum(X).
lt(s(X),s(Y)) :- lt(X,Y).
sum(0,X,X).
sum(s(X),Y,s(Z)) :- sum(X,Y,Z).
diff(X,Y,Z) :- sum(Z,Y,X).
?- multiple(X, s(s(s(s(s(s(0))))))).
where s(0) is 1, s(s(0)) is 2 etc. It gives all the desired answers for X but after the last answer, it gets stuck. I assume in an infinite recursive loop?
What is happening in your program? Does it loop forever, or does it only take some time since you haven't updated your hardware in recent decades? We cannot tell. (Actually, we could tell by looking at your program, but that is much too complex for the moment).
What we can do with ease is narrow down the source of this costly effort. And this, without a deep understanding of your program. Let's start with the query:
?- multiple(X, s(s(s(s(s(s(0))))))).
X = s(0)
; X = s(s(0))
; X = s(s(s(0)))
; X = s(s(s(s(s(s(0))))))
; loops. % or takes too long
Isn't there an easier way to do this? All this semicolon typing. Instead, simply add false to your query. In this manner the solutions found are no longer shown and we can concentrate on this annoying looping. And, if we're at it, you can also add false goals into your program! By such goals the number of inferences might be reduced (or stays the same). And if the resulting fragment (called a failure-slice) is looping, then this is a reason why your original program loops:
multiple(_X,0) :- false.
multiple(X,Y) :- lt(0,X), false, lt(0,Y), diff(Y,X,D), multiple(X,D).
natNum(0) :- false.
natNum(s(X)) :- natNum(X), false.
lt(0,s(X)) :- natNum(X), false.
lt(s(X),s(Y)) :- false, lt(X,Y).
?- multiple(X, s(s(s(s(s(s(0))))))), false.
loops.
Do your recognize your program? Only those parts remained that are needed for a loop. And, actually in this case, we have an infinite loop.
To fix this, we need to modify something in the remaining, visible part. I'd go for lt/2 whose first clause can be generalized to lt(0, s(_)).
But wait! Why is it OK to generalize away the requirement that we have a natural number? Look at the fact multiple(X,0). which you have written. You have not demanded that X is a natural number either. This kind of over-generalizations often appears in Prolog programs. They improve termination properties at a relatively low price: Sometimes they are too general but all terms that additionally fit into the generalization are not natural numbers. They are terms like any or [a,b,c], so if they appear somewhere you know that they do not belong to the solutions.
So the idea was to put false goals into your program such that the resulting program (failure-slice) still loops. In the worst case you put false at a wrong place and the program terminates. By trial-and-error you get a minimal failure-slice. All those things that are now stroked through are irrelevant! In particular diff/3. So no need to understand it (for the moment). It suffices to look at the remaining program.

Prolog predicate different solution cases

I have 2 simple rules, which defines if 2 variables are in relation ship
grandfather: grandfather(X,Y):-male(X), parent(X,Z), parent(Z,Y).
father: svekr(X,Y):-male(X), female(Y),parent(X,Z),(marriage(Z,Y);marriage(Y,Z)).
for grandfather, when I'm trying to get all possible results using ; , program gives me answer yes.
This is what I'm talking about:
As you can see, program understands that there is no more possible solutions, so the program gives final answer is X = pat and Y = tony without confirmation.
But for father in law it goes like this:
This time, program does ask, if there is a need to find another solution it will fail and answer is no.
So my quesstion is, why in one case it gives YES with no confirmation and in other case it gives NO with confirmation?
Full code in here https://pastebin.com/9HpEnuAz
Consider
a(1).
a(2).
resulting in
?- a(X).
X = 1 ;
X = 2.
true.
Prolog knows that there is no rule after a(2), so it can give a definite answer.
Now, on the other hand:
a(1).
a(2).
a(3) :- 0 > 1.
Here, after a(2), we see another rule, giving another choice point. This is like a closed door - you can choose to try to open it, but you don't know whether anything is behind at or whether it will open at all. However, Prolog sees the door and asks you if it should try:
a(X).
X = 1 ;
X = 2 ;
false.
Since this particular door is locked (0 is not greater than 1), it responds with false, meaning "I failed to find another answer".
You can usually just ignore extra choice points although there can be performance implications sometimes.

Determining successor in prolog using recursion

I'm trying (failing) to understand an exercise where I'm given the following clauses;
pterm(null).
pterm(f0(X)) :- pterm(X).
pterm(f1(X)) :- pterm(X).
They represent a number in binary, eg. f0(null) is equivalent to 0, f1(null) is equivalent to 1, etc.
The objective is to define a predicate over pterm such that one is the successor of the other when true. It seems like a relatively simple exercise but I'm struggling to get my head around it.
Here is the code I've written so far;
incr(X,Y) :- pterm(f0(X)), pterm(f1(Y)).
incr(X,Y) :- pterm(f0(f1(X))), pterm(f1(f1(Y))).
Having tested this I know it's very much incorrect. How might I go about inspecting the top level arguments of each pterm?
I've made minimal progress in the last 4 hours so any hints/help would be appreciated.
1)
I'll start with the "how to inspect" question, as I think it will be the most useful. If you're using swi-prolog with xpce, run the guitracer:
?- consult('pterm'). % my input file
% pterm compiled 0.00 sec, 5 clauses
true.
?- guitracer.
% The graphical front-end will be used for subsequent tracing
true.
?- trace. % debugs step by step
true.
[trace] ?- pterm(f0(f1(null))). % an example query to trace
true.
A graphical interface will come up. Press the down arrow to unify things step by step. What's going on should make sense fairly quickly.
(use notrace. and nodebug. appropriately to exit trace and debug modes afterwards).
2) You seem to misunderstand how predicates work. A predicate is a logical statement, i.e. it will always return either true or false. You can think of them as classical boolean functions of the type "iseven(X)" (testing if X is even) or "ismemberof(A,B)" (testing if A is a member of B) etc. When you have a rule like "pred1 :- pred2, pred3." this is similar to saying "pred1 will return true if pred2 returns true, and pred3 returns true (otherwise pred1 returns false)".
When your predicates are called using constants, checking its truth value is a matter of checking your facts database to see if that predicate with those constants can be satisfied. But when you call using variables, prolog goes through a wild goose chase, trying to unify that variable with all the allowable stuff it can link it to, to see if it can try to make that predicate true. If it can't, it gives up and says it's false.
A predicate like incr(X,Y) is still something that needs to return true or false, but, if by design, this only becomes true when Y is the incremented version of X, where X is expected to be given at query time as input, then we have tricked prolog into making a "function" that is given X as input, and "returns" Y as output, because prolog will try to find an appropriate Y that makes the predicate true.
Therefore, with your example, incr(X,Y) :- pterm(f0(X)), pterm(f1(Y)). makes no sense, because you're telling it that incr(X,Y) will return true for any X,Y, as long as prolog can use X to find in the fact database any pterm(f0(X)) that will lead to a known fact, and also use Y to find a pterm(f1(Y)) term. You haven't made Y dependent on X in any way. This query will succeed for X = null, and Y = null, for instance.
Your first clause should be something like this.
incr(X,Y) :- X = pterm(f0(Z)), Y = pterm(f1(Z)).
where = performs unification. I.e. "find a value for Z such that X is pterm(f0(Z)), and for the same value of Z it also applies that Y = pterm(f1(Z))."
In fact, this could be more concisely rewritten as a fact:
incr( pterm(f0(Z)), pterm(f1(Z)) ).
3)
Your second clause can be adapted similarly. However, I'm not sure if this is correct in terms of the logic of what you're trying to achieve (i.e. binary arithmetic). But I may have misunderstood the problem you're trying to solve.
My assumption is that if you have (0)111, then the successor should be 1000, not 1111. For this, I would guess you need to create a predicate that recursively checks if the incrementation of the digits below the currently processed one results in a 'carried' digit.
(since the actual logic is what your assignment is about, I won't offer a solution here. but hope this helps get you into grips with what's going on. feel free to have a go at the recursive version and ask another question based on that code!)

Prolog Backtracking On Finding A Solution And Returning False

I'm taking a crack at Prolog (using SWI-Prolog) and everything works like I want it to, i.e., the logic is calculated correctly and it finds the right solutions but the whole backtracking thing is screwing with me.
Here's the code:
tall(X) :- skinny(X) ; eatless(X).
eatless(X) :- playsmore(X).
playsmore(X) :- hasxbox(X) ; hasplaystation(X).
skinny(a).
vegetarian(a).
hasxbox(b).
eatsburger(c).
hasplaystation(d).
list_all_tall :- forall(tall(Tall), writeln(Tall)).
Very basic stuff. Here's what I get as a result of my queries:
?- tall(a).
true ; % Note 1
false.
?- tall(b).
true ; % Note 2
false.
?- tall(c).
false.
?- tall(d).
true.
As you can see from Notes 1 and 2, it waits for me to hit ; to move on and then considers the first solution as null and eventually outputs false.
I can use cuts to control this behavior better but I also want the following commands to work properly:
?- tall(X).
X = a ;
X = b ;
X = d.
And:
?- list_all_tall.
a
b
d
true.
These two commands give the solution exactly the way I want. Its just the ones for Notes 1 and 2 that are driving me up the wall. Is there a way that I can keep the functionality as it is right now for tall(X). and list_all_tall., while fixing the functionality of tall(a). and tall(b). to my liking, i.e., the program should exit with a true. after I ask tall(a). or tall(b).
I'd appreciated it if instead of giving straight answers someone could actually explain how I could go about fixing it myself because maybe my way of thinking in Prolog is all bassackwards.
PS: No offense intended to tall, skinny, fat, burger eating, video game playing, vegetarian folks.
Just to supplement Daniel's well-explained answer (+1) for your specific case, consider:
tall(a).
Prolog will look at the first match, which is through:
tall(X) :- skinny(X) ; eatless(X).
This will succeed because skinny(a) will succeed. However, there's a disjunction ; leaving a choice point for Prolog that it hasn't explored yet. Because skinny(a) succeeds and the choice point is pending, you get true but prompted to seek more. Prolog then backtracks to the choice point and tries to satisfy eatless(a) but fails. Thus, you get:
?- tall(a).
true ; % because `skinny(a)` succeeded
false. % because `eatless(a)` failed
Taking another example:
tall(d).
Again, this matches the tall/1 predicate, but this time, skinny(d) fails and prolog moves right on (due to the disjunction) to eatless(d) which succeeds. However, there are no more choice points after that success, so you get:
?- tall(d).
true. % There were no choice points available after success
The best thing to do is not worry about it, because you're not always going to be able to prevent it.
Prolog doesn't ever know that there will be another answer. It just knows that there may be another answer. This is called a choice point. Whenever Prolog reaches an alternative, it creates a choice point and then follows the first option. If that option doesn't work out, it backs up to the most recent choice point and tries the next alternative. If it runs out of alternatives without finding an answer, you get no or false.
You can try to write your code so that you don't get a choice point if you know there are no more items. member/2, for instance, in some Prologs you get false after the last item and in others you do not. But it isn't a composition problem to have a dud choice point after all your solutions. Your user interface probably won't show users Prolog's prompts directly. You can use setof/3 and the other extralogical predicates to get all the solutions. The false won't "leak" out into the world. It's a little unnerving at first, but just trust it and don't worry too much about it.
It is possible to run the same predicate, tall/1 in this case, in different modes based on different instantiation patterns.
When you run ?- tall(a). you instantiate the argument (i.e., X=a) and you want to receive either true or false (and no choicepoints, indicated by ;).
In Prolog this mode is called semi-deterministic.
You can force your predicate to be semi-deterministic for this specific instantiation pattern in the following way:
tall(X):- (ground(X) -> once(tall0(X)) ; tall0(X)).
Here ground(X) succeeds just in case X is fully instantiated.
Fully instantiated means that it is not a variable nor is it a compound term containing a variable.
tall0(X) is your original predicate.
The second mode you want to use is ?- tall(X).
Here you expect all results to be given subsequently, using ;.
This mode is called non-deterministic in Prolog.
The complete code for your example is:
tall(X):- (ground(X) -> once(tall0(X)) ; tall0(X)).
tall0(X):- skinny(X) ; eatless(X).
eatless(X):- playsmore(X).
playsmore(X):- hasxbox(X) ; hasplaystation(X).
skinny(a).
hasxbox(b).
hasplaystation(d).
Now the single predicate tall/1 can be called in the two modes, producing the behavior you want. Semi-deterministic usage:
?- tall(a).
true.
Non-deterministic usage:
?- tall(X).
X = a ;
X = b ;
X = d.
Hope this helps!

Resources