Prolog "," operator not working as intended? - prolog

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".

Related

What is Prolog saying about an uninstantiated variable?

Say we were to execute the following, and SWI Prolog responds:
?- write(X).
_13074
true.
What is _13074? Is this an address? Is this an ID of some sort? I notice that we'll get a different value each time. Furthermore, why does Prolog report true.? Is this Prolog saying that anything can be unified with X? Why do these appear in the order they do?
If we were to unify X with, say, 1, then the result is different.
?- X = 1, write(X).
1
X = 1.
What is _13074?
The simplest answer is that it represents an uninstantiated variable.
To be more precise from the Prolog Standard
anonymous variable: A variable (represented in a term or Prolog text by _) which differs from every other variable (and anonymous
variable) (see 6.1.2, 6.4.3)
instantiated: A variable is instantiated with respect to substitution if application of the substitution yields an atomic term or a compound term.
A term is instantiated if any of its variables are instantiated.
uninstantiated: A variable is uninstantiated when it is not instantiated.
variable: An object which may be instantiated to a term during execution.
named variable: A variable which is not an anonymous variable (see 6.1.2, 6.4.3)
So obviously all of that is self referential but in short by the standard there are anonymous variables _ and named variables, E.g. X, Y, Ls.
Note that the standard does not say what is the difference between _ and variables with numbers in the suffix, E.g. _13074. Those are implementation specific.
The standard does note for writing a term,
7.10.5 Writing a term
When a term Term is output using write-term/3 (8.14.2) the action which is taken is defined by the rules below:
a) If Term is a variable, a character sequence representing that variable is output. The sequence begins with _ (underscore) and the remaining characters are implementation dependent. The same character sequence is used for each occurrence of a particular variable in Term. A different character sequence is used for each distinct variable in Term.
Since you specifically mention SWI-Prolog there are other variable caveats to be aware of:
named singleton variables AKA auxiliary variables
Named singletons start with a double underscore (__) or a single underscore followed by an uppercase letter, E.g., __var or _Var.
Attribute variables - provide a technique for extending the Prolog unification algorithm Holzbaur, 1992 by hooking the binding of attributed variables. There is no consensus in the Prolog community on the exact definition and interface to attributed variables. The SWI-Prolog interface is identical to the one realised by Bart Demoen for hProlog Demoen, 2002. This interface is simple and available on all Prolog systems that can run the Leuven CHR system (see chapter 9 and the Leuven CHR page).
Global variables - are associations between names (atoms) and terms.
I don't plan to dive deeper into variables as one has to start looking at SWI-Prolog C level source code to really get a more accurate understanding, (ref). I also don't plan to add more from the standard as one would eventually have to reproduce the entire standard here just to cover all of the references.
For more definitions from the Prolog standard see: Is this Prolog terminology correct? (fact, rule, procedure, predicate, ...) The answer is a community wiki so most users can add to it and the OP does not get the points, so upvote all you want.
Is this an address?
No
Sometimes you will also see logic variable used but I don't plan to expand on that here, however for the record SWI-Prolog is NOT based on WAM it is based on A Portable Prolog Compiler.
See above 7.10.5 Writing a term
Is this an ID of some sort?
I would not argue with that in a causal conversation about SWI-Prolog but there is enough problems with that simple analogy to split hairs and start a discussion/debate, E.g. can a blob be assigned to a variable? What is numbervars?
See above 7.10.5 Writing a term
I notice that we'll get a different value each time.
The Prolog standard uses the word occurrence.
See above 7.10.5 Writing a term
why does Prolog report true.?
Prolog is a logic language which executes queries (goal) that result in either true or false or the instantiated values of variables, however there can be side effects such as writing to a file, throwing exceptions, etc.
The Prolog standard states
A.2.1.1 The General Resolution Algorithm
The general resolution of a goal G of a database P is defined by the following non-deterministic algorithm:
a) Start with the initial goal G which is an ordered conjunction of
predications.
b) If G is the singleton true then stop (success).
c) Choose a predication A in G (predication-choice)
d) If A is true, delete it, and proceed to step (b).
e) If no renamed clause in P has a head which unifies with A then stop (failure).
f) Choose a freshly renamed clause in P whose head H unifies with A (clause-choice) where σ = MGU(H, A) and B is the body of the clause,
g) Replace in G the predication A by the body B, flatten and apply the substitution σ.
h) Proceed to step (b).
Also see:
Resolution
MGU
Is this Prolog saying that anything can be unified with X?
For very simple Prolog implementations (ref) then the question would make sense. In the real world and even more so with SWI-Prolog were the rubber meets the road I would have to say not in all cases.
For most Prolog code syntactic unification is what is driving what is happening. See: A.2.1.1 The General Resolution Algorithm above. However if you start to think about things like blobs, attributes, threads, exceptions, and so on then you really have to look at what is a variable, even the kind of variable and what that variable can do , E.g.
?- X is true.
ERROR: Arithmetic: `true/0' is not a function
ERROR: In:
ERROR: [10] _4608 is true
ERROR: [9] toplevel_call(user:user: ...) at c:/program files/swipl/boot/toplevel.pl:1117
?- trie_new(A_trie).
A_trie = <trie>(0000000006E71DB0).
?- write(X).
_13074
true.
Why do these appear in the order they do?
write(X). is the goal entered by the user.
The goal is executed which in this case has the side effect of writing to the current output stream stream, E.g. current_output/1, the variable X which for SWI-Prolog for this occurrence of X is uninstantiated and is displayed as _13074.
The logic query ends and the result of the query being logical is either true or false. Since the query executed successfully the result is true.
If we were to unify X with, say, 1, then the result is different.
?- X = 1, write(X).
1
X = 1.
I will presume you are asking why there is no true at the end.
IIRC with SWI-Prolog, if the query starts with a variable and then the query succeeds with the variable being instantiated that will be reported and no true or false will then appear, E.g.
?- X = 1.
X = 1.
?- current_prolog_flag(double_quotes,V).
V = string.
?- X = 1, Y = 1, X = Y.
X = Y, Y = 1.
If however the query succeeds and no variable was instantiated then the query will report true E.g.
?- 1 = 1.
true.
?- current_prolog_flag(double_quotes,string).
true.
If the the query fails the query will report false E.g.
?- X = 1, Y = 2, X = Y.
false.
?- current_prolog_flag(quotes,String).
false.
I suspect this much information will now have you asking for more details but I won't go much deeper than this as SO is not a place to give a lecture condensed into an answer. I will try to clarify what is written but if it needs a lot more detail expect to be requested to post a new separate question.
I know the info from the standard presented here leaves lots of lose ends. If you really want the details from the standard then purchase the standard as those of us who have have done. I know it is not cheap but for questions like this it is the source of the answers.

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 Cryptarithmetic program

I'm taking a programming course and the professor just lightly skimmed over Prolog due to lack of time. Anyways, he suggested we research it on our own. I came across a Cryptarithmetic program that is supposed to calculate? AM+PM = DAY. I do not know what is supposed to be added as input in the SWI interpreter and what should be received as the correct output...If this makes any sense?
I tried...
solve([AM],[PM],[DAY]).
That does nothing. Any help on what the correct input would be for AM+PM = DAY or something similar would be great! Here is the program I was playing with...
solve([A,M,P,D,Y]):-
select(A,[0,1,2,3,4,5,6,7,8,9],WA), % W means Without
not(A=0),
select(M,WA,WMA),
select(P,WMA,WMAP),
not(P=0),
select(D,WMAP,WMAPD),
not(D=0),
select(Y,WMAPD,WMAPDY),
DAY is 100*D+10*A+Y,
AM is 10*A+M,
PM is 10*P+M,
DAY is AM+PM.
Please keep in mind that we only had two classes on Prolog so I know next to nothing!
Scott
Okay, this program will give a variable assignment for the formula
DAY = AM + PM
Where each of the characters is a digit from 0 to 9 no, digit may be used twice and A,P and D mustn't be 0 (no leading zeroes allowed).
For beginners trying to understand prolog programs it might be more feasible to ask the question: "How is it (the program) true", instead of "what input generates which output". Prolog gladly supplies you with the variable settings it needed to make, to give you a true. (It also tracks on the way where it could have gone another way, so can ask again).
The program uses pattern matching, but basically wants a list of five elements which represent the variables A, M,P,D and Y (in that order).
select's arguments are:
something (you can put in a list)
a list that contains the first argument
the list without the first argument.
not tries to solve what it is given and fails if it succeeds.
, is a short cirquiting and, like && in many C-like languages.
The last four lines are simple arithmetics, while the lines above just ensure that you aren't selecting doubles and leading 0s.
so after you have loaded your program, you can query for answers:
?- solve([A, M,P,D,Y]).
A = 2,
M = 5,
P = 9,
D = 1,
Y = 0
If you want another solution, you can hit space or ;. If you want to know, if there is a solution with A = 5, you can query like this:
?- A = 5, solve([A, M,P,D,Y]).
A = 5,
M = 6,
P = 9,
D = 1,
Y = 2
If you want to "reassemble" it, this line might help:
?- solve([A, M,P,D,Y]), format('~w~w~w= ~w~w + ~w~w~n', [D,A,Y,A,M,P,M]).
format is something like printf in many other languages.

prolog dcg restriction

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.

Simple Prolog program

I'm trying to teach myself some Prolog so that I can determine its fitness for solving a problem I have. Essentially the problem is, given a bunch of rules about the interaction between items, determine what items are available, unavailable, selected, and not selected.
But I'm failing at even the most simple parts! I drastically reduced the problem size just to see what I could do. Below is my knowledge base:
selected(A) :- implied(A).
implied(B) :- implies(A,B),selected(A).
implied(option_one).
implies(option_one,option_two).
And when I query:
selected(X).
I only get back option_two.
It seems like there is something very basic that I'm not understanding here, but it seems to me that if option_one should also come back in that list (especially since one of the facts is 'implied(option_one)'.
If it matters, I've tried this using P# as well as SWI-Prolog, which give the same result.
When u have your first answer X = option_two press ; to get next answer
?- selected(X).
X = option_two ;
X = option_one.
Or u may use smth like that for showing all matching things:
?- selected(X), writeln(X), fail.
option_two
option_one
false.

Resources