Prolog parsing malfunctioning - prolog

My code takes an expression like or(lit(true),lit(X)),X) and outputs it as a list of lists.
tocnf(Tree, Expr) :-
trans(Tree ,Expr, []).
trans(lit(X)) -->bbool(X).
trans(or(lit(X1),lit(X2))) --> bconj(X1), bdisj(X2).
trans(and(lit(X1),lit(X2))) --> bbool(X1), bconj(X2).
bdisj(Conj) --> bconj(Conj).
bconj(Bool) --> bbool(Bool).
bbool(X) --> [[X]].
this code should take something like
tocnf(lit(X),X)
output it as
[[X]]
or
tocnf(or(lit(true),lit(X)),X)
and output it as
[[true],[X]].
Question is why when I do
tocnf(or(lit(true), and(lit(X),lit(true))),X)
it outputs
false.

Preliminaries
First, a note on style: You should always use the phrase/2 interface to access DCGs, so write tocnf/2 as:
tocnf(Tree, Expr) :-
phrase(trans(Tree), Expr).
Further, tocnf/2 is a rather imperative name, since it implies a direction of use ("to" CNF). However, the relation also makes sense in other directions, for example to generate answers. Therefore, try to find a better name, that does justice to this general nature of Prolog. I leave this as an exercise.
Declarative debugging
Now, on to your actual question. Apply declarative debugging to find the reason for the failure.
We start with the query you posted:
?- tocnf(or(lit(true), and(lit(X),lit(true))), X).
false.
This means that the program is unexpectedly too specific: It fails in a case we expect to succeed.
Now, we generalize the query, to find simpler cases that still fail. This is completely admissible because your program is written using the monotonic subset of Prolog, as is highly recommended to make declarative debugging applicable.
To generalize the query, I use variables instead of some subterms. For example:
?- tocnf(or(lit(_), and(lit(X),lit(true))), X).
false.
Aha! This still fails, and therefore every more specific query will also fail.
So, we proceed like this, using variables instead of some subterms:
?- tocnf(or(lit(_), and(lit(X),lit(_))), X).
false.
?- tocnf(or(_, and(lit(X),lit(_))), X).
false.
?- tocnf(or(_, and(_,lit(_))), X).
false.
?- tocnf(or(_, and(_,_)), X).
false.
All of these queries also fail.
Now, we take it just one step further:
?- tocnf(or(_, _), X).
X = [[_G793], [_G795]].
Aha! So we have found a case that succeeds, and one slightly more specific though still very simple case that fails:
?- tocnf(or(_, and(_,_)), X).
false.
This is the case I would start with: Think about why your relation does not work for terms of the form or(_, and(_,_)).
Automated solution
A major attraction of pure monotonic Prolog is that the reasoning above can be automated:
The machine should find the reason for the failure, so that we can focus on more important tasks.
One way to do this was generously made available by Ulrich Neumerkel.
To try it out, you need to install:
library(diadem) and
library(lambda).
Now, to recapitulate: We have found a query that unexpectedly fails. It was:
?- tocnf(or(lit(true), and(lit(X),lit(true))), X).
false.
To find a reason for this, we first load library(diadem):
?- use_module(library(diadem)).
true.
Then, we repost the query with a slight twist:
?- tocnf(or(lit(true), and(lit(X),lit(true))), X).?Generalization.
That is, I have simply appended ?Generalization. to the previous query.
In response, we get:
Generalization = tocnf(or(_, and(_, _)), _) .
Thus, Generalization is a more general goal that still fails. Since the Prolog program we are considering is completely pure and monotonic, we know that every more specific query will also fail. Therefore, I suggest you focus on this simpler and more general case, which was found automatically in this case, and is the same goal we also found manually after several steps.
Unexpected failure is a common issue when learning Prolog, and automated declarative debugging lets you quickly find the reasons.

Related

What am I missing about equality and unification in Prolog?

I'm working through Clocksin and Mellish to try and finally go beyond just dabbling in Prolog. FWIW, I'm running SWI-Prolog:
SWI-Prolog version 7.2.3 for x86_64-linux
Anyway, I implemented a diff/2 predicate as part of exercise 1.4. The predicate is very simple:
diff(X,Y) :- X \== Y.
And it works when used in the sister_of predicate, like this:
sister_of(X,Y) :-
female(X),
diff(X,Y),
parents(X, Mum, Dad ),
parents(Y, Mum, Dad ).
in that, assuming the necessary additional facts, doing this:
?- sister_of(alice,alice).
returns false as expected. But here's the rub. If I do this instead:
?- sister_of(alice, Who).
(again, given the additional facts necessary)
I get
Who = edward ;
Who = alice;
false
Even though, as already shown, the sister_of predicate does not treat alice as her own sister.
On the other hand, if I use the SWI provided dif/2 predicate, then everything works the way I would naively expect.
Can anyone explain why this is happening this way, and why my diff implementation doesn't work the way I'm expecting, in the case where I ask for additional unifications from that query?
The entire source file I'm working with can be found here
Any help is much appreciated.
As you note, the problem stems from the interplay between equality (or rather, inequality) and unification. Observe that in your definition of sister_of, you first find a candidate value for X, then try to constrain Y to be different, but Y is still an uninstantiated logic variable and the check is always going to succeed, like diff(alice, Y) will. The following constraints, including the last one that gives a concrete value to Y, come too late.
In general, what you need to do is ensure that by the time you get to the inequality check all variables are instantiated. Negation is a non-logical feature of Prolog and therefore potentially dangerous, but checking whether two ground terms are not equal is safe.

Custom subset not backtracking

I'm new to prolog and I've been having trouble with some homework.
On some part of my code I have to generate subsets of a given set on backtracking. Meaning, the code should try for a subset, and when it fails the next condition, try the next subset. I have done some research and the default function subset won't backtrack because as explained in this question both arguments are input arguments. So I built a custom one, which still isn't backtracking. Can you give me a hint on what I'm failing on? Here's my code:
numNutrients(8).
product(milk,[2,4,6]).
product(porkChops,[1,8]).
product(yoghurt,[3,1]).
product(honey,[5,7]).
product(plastic,[3,5,2]).
product(magic,[5,7,8]).
nutrientlist(N,L):-findall(I,between(1,N,I),L).
subset2([],[]):-!.
subset2([X|T],[X|T2]):-
subset2(T,T2).
subset2([_|T],[T2]):-
subset2(T,T2).
shopping(K,L):-
numNutrients(J),
nutrientlist(J,N),
findall(P,product(P,_),Z),
subset2(X,Z),
length(X,T),
T =< K,
covers(X,N),
L = X.
covers(_,[]):-!.
covers([X|L],N):-
product(X,M),
subset2(M,N),
subtract(N,M,T),
covers(L,T).
main:-
shopping(5,L),
write(L).
The problem is on predicate shopping(K,L). When it gets to predicate subset2, it gives the whole set, which has length 6 (not 5), then fails and doesn't backtrack. Since all previous predicates can't backtrack it just fails.
So, why doesn't subset2 backtrack?
Thank you for your time.
Primary focus: subset2/2
First, let us focus only on the predicate that shows different properties from those you expect.
In your case, this is only subset2/2, defined by you as:
subset2([], []) :- !.
subset2([X|T], [X|T2]) :-
subset2(T, T2).
subset2([_|T], [T2]) :-
subset2(T, T2).
I will now use declarative debugging to locate the cause of the problem.
For this method to apply, I remove the !/0, because declarative debugging works best on pure and monotonic logic programs. See logical-purity for more information. Thus, we shall work on:
subset2([], []).
subset2([X|T], [X|T2]) :-
subset2(T, T2).
subset2([_|T], [T2]) :-
subset2(T, T2).
Test cases
Let us first construct a test case that yields unintended answers. For example:
?- subset2([a], [a,b]).
false.
That obviously not intended. Can we generalize the test case? Yes:
?- subset2([a], [a,b|_]).
false.
So, we have now an infinite family of examples that yield wrong results.
Exercise: Are there also cases where the program is too general, i.e., test cases that succeed although they should fail?
Locating mistakes
Why have we seen unintended failure in the cases above? To locate these mistakes, let us generalize the program.
For example:
subset2(_, []).
subset2([_|T], [_|T2]) :-
subset2(T, T2).
subset2(_, [T2]) :-
subset2(T, T2).
Even with this massive generalization, we still have:
?- subset2([a], [a,b|_]).
false.
That is, we have many cases where we expect the query to succeed, but it fails. This means that the remaining program, even though it is a massive generalization of the original program, is still too specific.
Correcting the program
To make the shown cases succeed, we have to either:
add clauses that describe the cases we need
or change the existing clauses to cover these cases too.
For example, a way out would be to add the following clause to the database:
subset2([a], [a,b|_]).
We could even generalize it to:
subset2([a], [a|_]).
Adding either or both of these clauses to the program would make the query succeed:
?- subset2([a], [a,b|_]).
true.
However, that is of course not the general definition of subset2/2 we are looking for, since it would for example still fail in cases like:
?- subset2([x], [x,y|_]).
false.
Therefore, let us go with the other option, and correct the existing definition. In particular, let us consider the last clause of the generalized program:
subset2(_, [T2]) :-
subset2(T, T2).
Note that this only holds if the second argument is a list with exactly one element which is subject to further constraints. This seems way too specific!
Therefore, I recommend you start by changing this clause so that it at least makes the test cases collected so far all succeed. Then, add the necessary specializations to make it succeed precisely for the intended cases.

Simple Prolog program: "Arguments are not sufficiently instantiated" error

I am writing a Prolog predicate that cuts first three elements off a numbered list and prints the result. An example of a numbered list:
[e(f,1),e(o,2),e(o,3),e(b,4),e(a,5),e(r,6)].
The original predicate for normal list looks like this:
strim([H|T],R) :-
append(P,R,[H|T]),
length(P,3).
So, since length predicate works perfectly for numbered lists as well, I only had to write predicate that appends one numbered list to another:
compose([],L,[L]).
compose([e(F,C)|T],e(A,_),[e(F,C)|L]) :-
N is C+1,
compose(T,e(A,N),L).
napp(X,[],X).
napp(L,[e(X,Y)|T],M):-
compose(L,e(X,Y),L1),
napp(L1,T,M).
I expected the predicate for numbered list to be a slightly modified version of predicate for normal list, so I wrote this:
numstrim([e(X,Y)|T],R) :-
napp(P,R,[e(X,Y)|T]),
length(P,3).
However, I'm getting this error:
ERROR: compose/3: Arguments are not sufficiently instantiated
Could somebody please explain what's causing the error and how to avoid it? I'm new to Prolog.
Instantiation errors are a common phenomenon in Prolog programs that use moded predicates: These are predicates that can only be used in special circumstances, requiring for example that some arguments are fully instantiated etc.
As a beginner, you are in my view well advised to use more general predicates instead, so that you can freely exchange the order of goals and do not have to take procedural limitations into account, at least not so early, and without the ability to freely experiment with your code.
For example, in your case, the following trivial change to compose/3 gives you a predicate that works in all directions:
compose([], L, [L]).
compose([e(F,C)|T], e(A,_), [e(F,C)|L]) :-
N #= C+1,
compose(T, e(A,N), L).
Here, I have simply replaced the moded predicate (is)/2 with the CLP(FD) constraint (#=)/2, which completeley subsumes the more low-level predicate over integers.
After this small change (depending on your Prolog system, you may have to import a library to use the more general arithmetic predicates), we get:
?- numstrim([e(f,1),e(o,2),e(o,3),e(b,4),e(a,5),e(r,6)], Es).
nontermination
So, we find out that the instantiation error has actually overshadowed a different problem that can only be understood procedurally, and which has now come to light.
To improve this, I now turn around the two goals of numstrim/2:
numstrim([e(X,Y)|T], R) :-
length(P, 3),
napp(P, R, [e(X,Y)|T]).
This is because length(P, 3) always terminates, and placing a goal that always terminates first can at most improve, never worsen, the termination properties of a pure and monotonic logic program.
So now we get:
?- numstrim([e(f,1),e(o,2),e(o,3),e(b,4),e(a,5),e(r,6)], Es).
Es = [e(b, _1442), e(a, _2678), e(r, _4286)] .
That is, at least we get an answer now!
However, the predicate still does not terminate universally, because we get:
?- numstrim([e(f,1),e(o,2),e(o,3),e(b,4),e(a,5),e(r,6)], Es), false.
nontermination
I leave fixing this as an exercise.

Making "deterministic success" of Prolog goals explicit

The matter of deterministic success of some Prolog goal has turned up time and again in—at least—the following questions:
Reification of term equality/inequality
Intersection and union of 2 lists
Remove duplicates in list (Prolog)
Prolog: How can I implement the sum of squares of two largest numbers out of three?
Ordering lists with constraint logic programming)
Different methods were used (e.g., provoking certain resource errors, or looking closely at the exact answers given by the Prolog toplevel), but they all appear somewhat ad-hack to me.
I'm looking for a generic, portable, and ISO-conformant way to find out if the execution of some Prolog goal (which succeeded) left some choice-point(s) behind. Some meta predicate, maybe?
Could you please hint me in the right direction? Thank you in advance!
Good news everyone: setup_call_cleanup/3 (currently a draft proposal for ISO) lets you do that in a quite portable and beautiful way.
See the example:
setup_call_cleanup(true, (X=1;X=2), Det=yes)
succeeds with Det == yes when there are no more choice points left.
EDIT: Let me illustrate the awesomeness of this construct, or rather of the very closely related predicate call_cleanup/2, with a simple example:
In the excellent CLP(B) documentation of SICStus Prolog, we find in the description of labeling/1 a very strong guarantee:
Enumerates all solutions by backtracking, but creates choicepoints only if necessary.
This is really a strong guarantee, and at first it may be hard to believe that it always holds. Luckily for us, it is extremely easy to formulate and generate systematic test cases in Prolog to verify such properties, in essence using the Prolog system to test itself.
We start with systematically describing what a Boolean expression looks like in CLP(B):
:- use_module(library(clpb)).
:- use_module(library(lists)).
sat(_) --> [].
sat(a) --> [].
sat(~_) --> [].
sat(X+Y) --> [_], sat(X), sat(Y).
sat(X#Y) --> [_], sat(X), sat(Y).
There are in fact many more cases, but let us restrict ourselves to the above subset of CLP(B) expressions for now.
Why am I using a DCG for this? Because it lets me conveniently describe (a subset of) all Boolean expressions of specific depth, and thus fairly enumerate them all. For example:
?- length(Ls, _), phrase(sat(Sat), Ls).
Ls = [] ;
Ls = [],
Sat = a ;
Ls = [],
Sat = ~_G475 ;
Ls = [_G475],
Sat = _G478+_G479 .
Thus, I am using the DCG only to denote how many available "tokens" have already been consumed when generating expressions, limiting the total depth of the resulting expressions.
Next, we need a small auxiliary predicate labeling_nondet/1, which acts exactly as labeling/1, but is only true if a choice-point still remains. This is where call_cleanup/2 comes in:
labeling_nondet(Vs) :-
dif(Det, true),
call_cleanup(labeling(Vs), Det=true).
Our test case (and by this, we actually mean an infinite sequence of small test cases, which we can very conveniently describe with Prolog) now aims to verify the above property, i.e.:
If there is a choice-point, then there is a further solution.
In other words:
The set of solutions of labeling_nondet/1 is a proper subset of that of labeling/1.
Let us thus describe what a counterexample of the above property looks like:
counterexample(Sat) :-
length(Ls, _),
phrase(sat(Sat), Ls),
term_variables(Sat, Vs),
sat(Sat),
setof(Vs, labeling_nondet(Vs), Sols),
setof(Vs, labeling(Vs), Sols).
And now we use this executable specification in order to find such a counterexample. If the solver works as documented, then we will never find a counterexample. But in this case, we immediately get:
| ?- counterexample(Sat).
Sat = a+ ~_A,
sat(_A=:=_B*a) ? ;
So in fact the property does not hold. Broken down to the essence, although no more solutions remain in the following query, Det is not unified with true:
| ?- sat(a + ~X), call_cleanup(labeling([X]), Det=true).
X = 0 ? ;
no
In SWI-Prolog, the superfluous choice-point is obvious:
?- sat(a + ~X), labeling([X]).
X = 0 ;
false.
I am not giving this example to criticize the behaviour of either SICStus Prolog or SWI: Nobody really cares whether or not a superfluous choice-point is left in labeling/1, least of all in an artificial example that involves universally quantified variables (which is atypical for tasks in which one uses labeling/1).
I am giving this example to show how nicely and conveniently guarantees that are documented and intended can be tested with such powerful inspection predicates...
... assuming that implementors are interested to standardize their efforts, so that these predicates actually work the same way across different implementations! The attentive reader will have noticed that the search for counterexamples produces quite different results when used in SWI-Prolog.
In an unexpected turn of events, the above test case has found a discrepancy in the call_cleanup/2 implementations of SWI-Prolog and SICStus. In SWI-Prolog (7.3.11):
?- dif(Det, true), call_cleanup(true, Det=true).
dif(Det, true).
?- call_cleanup(true, Det=true), dif(Det, true).
false.
whereas both queries fail in SICStus Prolog (4.3.2).
This is the quite typical case: Once you are interested in testing a specific property, you find many obstacles that are in the way of testing the actual property.
In the ISO draft proposal, we see:
Failure of [the cleanup goal] is ignored.
In the SICStus documentation of call_cleanup/2, we see:
Cleanup succeeds determinately after performing some side-effect; otherwise, unexpected behavior may result.
And in the SWI variant, we see:
Success or failure of Cleanup is ignored
Thus, for portability, we should actually write labeling_nondet/1 as:
labeling_nondet(Vs) :-
call_cleanup(labeling(Vs), Det=true),
dif(Det, true).
There is no guarantee in setup_call_cleanup/3 that it detects determinism, i.e. missing choice points in the success of a goal. The 7.8.11.1 Description draft proposal only says:
c) The cleanup handler is called exactly once; no later than
upon failure of G. Earlier moments are:
If G is true or false, C is called at an implementation
dependent moment after the last solution and after the last
observable effect of G.
So there is currently no requirement that:
setup_call_cleanup(true, true, Det=true)
Returns Det=true in the first place. This is also reflected in the test cases 7.8.11.4 Examples that the draf proposal gives, we find one test case which says:
setup_call_cleanup(true, true, X = 2).
Either: Succeeds, unifying X = 2.
Or: Succeeds.
So its both a valid implementation, to detect determinism and not to detect determinism.

Prolog list membership, multiple results returned

I have a standard procedure for determining membership of a list:
member(X, [X|_]).
member(X, [_|T]) :- member(X, T).
What I don't understand is why when I pose the following query:
?- member(a,[a,b]).
The result is
True;
False.
I would have thought that on satisfying the goal using the first rule (as a is the head of the list) True would be returned and that would be the end of if. It seems as if it is then attempting to satisfy the goal using the second rule and failing?
Prolog interpreter is SWI-Prolog.
Let's consider a similar query first: [Edit: Do this without adding your own definition ; member/2 is already defined]
?- member(a,[b,a]).
true.
In this case you get the optimal answer: There is exactly one solution. But when exchanging the elements in the list we get:
?- member(a,[a,b]).
true
; false.
Logically, both are just the affirmation that the query is true.
The reason for the difference is that in the second query the answer true is given immediately upon finding a as element of the list. The remaining list [b] does not contain a fitting element, but this is not yet examined. Only upon request (hitting SPACE or ;) the rest of the list is tried with the result that there is no further solution.
Essentially, this little difference gives you a hint when a computation is completely finished and when there is still some work to do. For simple queries this does not make a difference, but in more complex queries these open alternatives (choicepoints) may accumulate and use up memory.
Older toplevels always asked if you want to see a further solution, even if there was none.
Edit:
The ability to avoid asking for the next answer, if there is none, is extremely dependent on the very implementation details. Even within the same system, and the same program loaded you might get different results. In this case, however, I was using SWI's built-in definition for member/2 whereas you used your own definition, which overwrites the built-in definition.
SWI uses the following definition as built-in which is logically equivalent to yours but makes avoiding unnecessary choice points easier to SWI — but many other systems cannot profit from this:
member(B, [C|A]) :-
member_(A, B, C).
member_(_, A, A).
member_([C|A], B, _) :-
member_(A, B, C).
To make things even more complex: Many Prologs have a different toplevel that does never ask for further answers when the query does not contain a variable. So in those systems (like YAP) you get a wrong impression.
Try the following query to see this:
?- member(X,[1]).
X = 1.
SWI is again able to determine that this is the only answer. But YAP, e.g., is not.
Are you using the ";" operator after the first result then pushing return? I believe this is asking the query to look for more results and as there are none it is coming up as false.
Do you know about Prolog's cut - !?
If you change member(X, [X|_]). to member(X, [X|_]) :- !. Prolog will not try to find another solution after the first one.

Resources