How to represent uniqueness? - prolog

I am trying to write an agent for determining what card a player has, and I am struggling to represent that each card can only have been dealt to a single player.
So have I have something^ like this:
player(p1).
player(p2).
card(ace).
card(king).
card(queen).
card(jack).
but I am struggling on writing the rule for uniqueness. Borrowing something from logic class, I though about defining uniqueness as "if X has Y, then if something has Y, that something must be X".
has_card(P, C) :-
player(P),
card(C),
not(player(P2),
P2 =\= P,
has_card(P2, C)
).
but this fails because not/3 is not defined.
I tried rewriting it as:
has_card(P, C) :-
player(P),
card(C),
player(P2),
P2 =\= P,
not(has_card(P2,C)).
but then the query (which I expect to return false)
has_card(P, ace).
errors out with
ERROR: =\=/2: Arithmetic: `player1/0' is not a function
So, how can I represent uniqueness?
^ complicating --likely-- unnecessary details omitted
EDIT: in first attempt at has_card I have P2==P which should have been P2=\=P, fixed now

First off, as the error is telling you, you are using =\=, which is arithmetic inequality. For comparing on standard order of terms, you should use \==.
Then, the "not" as in "true when negated goal not true", you should probably use \+, like this:
\+ a == b
which is the same as
a \== b
The not/1 predicate is still available in most Prolog implementations but not recommended.
Fixing these two things will sort out the problem you are having at the moment.
But, as it is at the moment, your has_card/2 at no point maps a card to a player, really (it uses circular logic as far as I can see). Since you are not showing how exactly you know which cards belong to which player at any given moment, it is difficult to comment on that. Either way, some sort of term that you pass around would be probably better than changing the database with assert and retract.

Related

misconception about how prolog works

So I am currently learning prolog and I can't get my head around how this language works.
"It tries all the possible solutions until it finds one, if it doesn't it returns false" is what I've read that this language does. You just Describe the solution and it finds it for you
With that in mind, I am trying to solve the 8 queens problem ( how to place 8 queens on a chess board without anyone threatening the others).
I have this predicate, 'safe' that gets a list of pairs, the positions of all the queens and succeeds when they are not threatening each other.
When I enter in the terminal
?- safe([(1,2),(3,5)]).
true ?
| ?- safe([(1,3),(1,7)]).
no
| ?- safe([(2,2),(3,3)]).
no
| ?- safe([(2,2),(3,4),(8,7)]).
true ?
it recognizes the correct from the wrong answers, so it knows if something is a possible solution
BUT
when I enter
| ?- safe(L).
L = [] ? ;
L = [_] ? ;
it gives me the default answers, even though it recognizes a solution for 2 queens when I enter them.
here is my code
threatens((_,Row),(_,Row)).
threatens((Column,_),(Column,_)).
threatens((Column1,Row1),(Column2,Row2)) :-
Diff1 is Column1 - Row1,
Diff2 is Column2 - Row2,
abs(Diff1) =:= abs(Diff2).
safe([]).
safe([_]).
safe([A,B|T]) :-
\+ threatens(A,B),
safe([A|T]),
safe(T).
One solution I found to the problem is to create predicates 'position' and modify the 'safe' one
possition((0,0)).
possition((1,0)).
...
...
possition((6,7)).
possition((7,7)).
safe([A,B|T]) :-
possition(A),
possition(B),
\+ threatens(A,B),
safe([A|T]),
safe(T).
safe(L,X):-
length(L,X),
safe(L).
but this is just stupid, as you have to type everything explicitly and really really slow,
even for 6 queens.
My real problem here, is not with the code itself but with prolog, I am trying to think in prolog, But all I read is
Describe how the solution would look like and let it work out what is would be
Well that's what I have been doing but it does not seem to work,
Could somebody point me to some resources that don't teach you the semantics but how to think in prolog
Thank you
but this is just stupid, as you have to type everything explicitly and really really slow, even for 6 queens.
Regarding listing the positions, the two coordinates are independent, so you could write something like:
position((X, Y)) :-
coordinate(X),
coordinate(Y).
coordinate(1).
coordinate(2).
...
coordinate(8).
This is already much less typing. It's even simpler if your Prolog has a between/3 predicate:
coordinate(X) :-
between(1, 8, X).
Regarding the predicate being very slow, this is because you are asking it to do too much duplicate work:
safe([A,B|T]) :-
...
safe([A|T]),
safe(T).
Once you know that [A|T] is safe, T must be safe as well. You can remove the last goal and will get an exponential speedup.
Describe how the solution would look like and let it work out what is
would be
demands that the AI be very strong in general. We are not there yet.
You are on the right track though. Prolog essentially works by enumerating possible solutions and testing them, rejecting those that don't fit the conditions encoded in the program. The skill resides in performing a "good enumeration" (traversing the domain in certain ways, exploiting domain symmetries and overlaps etc) and subsequent "fast rejection" (quickly throwing away whole sectors of the search space as not promising). The basic pattern:
findstuff(X) :- generate(X),test(X).
And evidently the program must first generate X before it can test X, which may not be always evident to beginners.
Logic-wise,
findstuff(X) :- x_fulfills_test_conditions(X),x_fullfills_domain_conditions(X).
which is really another way of writing
findstuff(X) :- test(X),generate(X).
would be the same, but for Prolog, as a concrete implementation, there would be nothing to work with.
That X in the program always stands for a particular value (which may be uninstantiated at a given moment, but becomes more and more instantiated going "to the right"). Unlike in logic, where the X really stands for an unknown object onto which we pile constraints until -ideally- we can resolve X to a set of concrete values by applying a lot of thinking to reformulate constraints.
Which brings us the the approach of "Constraint Logic Programming (over finite domains)", aka CLP(FD) which is far more elegant and nearer what's going on when thinking mathematically or actually doing theorem proving, see here:
https://en.wikipedia.org/wiki/Constraint_logic_programming
and the ECLiPSe logic programming system
http://eclipseclp.org/
and
https://www.metalevel.at/prolog/clpz
https://github.com/triska/clpfd/blob/master/n_queens.pl
N-Queens in Prolog on YouTube. as a must-watch
This is still technically Prolog (in fact, implemented on top of Prolog) but allows you to work on a more abstract level than raw generate-and-test.
Prolog is radically different in its approach to computing.
Arithmetic often is not required at all. But the complexity inherent in a solution to a problem show up in some place, where we control how relevant information are related.
place_queen(I,[I|_],[I|_],[I|_]).
place_queen(I,[_|Cs],[_|Us],[_|Ds]):-place_queen(I,Cs,Us,Ds).
place_queens([],_,_,_).
place_queens([I|Is],Cs,Us,[_|Ds]):-
place_queens(Is,Cs,[_|Us],Ds),
place_queen(I,Cs,Us,Ds).
gen_places([],[]).
gen_places([_|Qs],[_|Ps]):-gen_places(Qs,Ps).
qs(Qs,Ps):-gen_places(Qs,Ps),place_queens(Qs,Ps,_,_).
goal(Ps):-qs([0,1,2,3,4,5,6,7,8,9,10,11],Ps).
No arithmetic at all, columns/rows are encoded in a clever choice of symbols (the numbers indeed are just that, identifiers), diagonals in two additional arguments.
The whole program just requires a (very) small subset of Prolog, namely a pure 2-clauses interpreter.
If you take the time to understand what place_queens/4 does (operationally, maybe, if you have above average attention capabilities), you'll gain a deeper understanding of what (pure) Prolog actually computes.

Binary logic in prolog

I want to solve following problem using inference making power of prolog.
One day, 3 persons, a, b, c were caught by police at the crime spot. When police settled interrogating them:
i) a says I am innocent
ii) b says a is criminal
iii) c says I am innocent.
Its known that
i) Exactly one person speaks true.
ii) Exactly one criminal is there.
Who is criminal?
To model above problem in First Order logic:
Consider c/1 is a predicate returns true when argument is Criminal
we can write:
(not(c(a)),c(c)) ; (c(c),c(a)).
c(a); c(b); c(c).
(not(c(a)),not(c(b))) ; (not(c(a)),not(c(c))) ; (not(c(b)),not(c(c))).
After modelling above statements in prolog, I will query:
?-c(X).
it should return:
X=c.
But error I got:
"No permission to modify static procedure `(;)/2'"
Since PROLOG does indeed work with Horn clauses, you'll need things of form head :- tail, reading :- as "if."
solve(Solution) :- ...
%With a Solution looking something like:
% solve(a(truth,innocent),b(false,criminal),c(false,innocent)).
To use the generate and test method, which is a common and reasonable way to solve this, you'd do something like this:
solve(Solution) :-
Solution = [a(_,_),b(_,_),c(_,_)],
generate(Solution),
validate(Solution).
generate should give you a well-formed Solution, that is, one having all the variables filled in with values that make some kind of sense (that is, false, true, criminal, innocent).
validate should ensure that the Solution matches the constraints you gave.
solve only completes when one of generate's solutions makes it past validate's constraints.
For an introduction to the generate and test method, see this tutorial.
But if you're writing code that isn't Horn clauses, you might need a tutorial on writing PROLOG functions (OK, relations), like this one.

Prolog: Can you make a predicate behave differently depending on whether a value is ground or not?

I have a somewhat complex predicate with four arguments that need to work when both the first and last arguments are ground/not ground, not ground/ground or ground/ground, and the second and third arguments are ground.
i.e. predicate(A,B,C,D).
I can't provide my actual code since it is part of an assignment.
I have it mostly working, but am receiving instantiation errors when A is not ground, but D is. However, I have singled out a line of code that is causing issues. When I change the goal order of the predicate, it works when D is ground and A is not, but in doing so, it no longer works for when A is ground and D is not. I'm not sure there is a way around this.
Is there a way to use both lines of code so that if the A is ground for instance it will use the first line, but if A is not ground, it will use the second, and ignore the first? And vice versa.
You can do that, but, almost invariably, you will break the declarative semantics of your programs if you do that.
Consider a simple example to see how such a non-monotonic and extra-logical predicate already breaks basic assumptions and typical declarative properties of well-known predicates, like commutativity of conjunction:
?- ground(X), X = a.
false.
But, if we simply exchange the goals by commutativity of conjunction, we get a different answer:
?- X = a, ground(X).
X = a.
For this reason, such meta-logical predicates are best avoided, especially if you are just beginning to learn the language.
Instead, better stay in the pure and monotonic subset of Prolog. Use constraints like dif/2 and CLP(FD) to make your programs usable in all directions, increasing generality and ease of understanding.
See logical-purity, prolog-dif and clpfd for more information.

Recursive reference in prolog

I meet some problem when I try to implement
friends(mia, ellen).
friends(mia, lucy).
friends(X,Y) :-
friends(X,Z),
friends(Y,Z).
and when i ask ?- friends(mia, X)., it run out of local stack.
Then I add
friends(ellen, mia) friends(lucy, mia)
I ask ?- friends(mia, X). ,it keeps replying X = mia.
I can't understand, why it is recursive?
First, two assumptions:
the actual code you wanted to write is the following one, with appropriate dots:
friends(mia,ellen).
friends(mia,lucy).
friends(X,Y) :-
friends(X,Z),
friends(Z,Y).
transivity holds: friends of friends are my friends too (I would rather model friendship as a distance: "A is near B" and "B is near C" does not necessarly imply "A is near C"). repeat's answer is right about figuring out first what you want to model.
Now, let's see why we go into infinite recursion.
Step-by-step
So, what happens when we ask: friends(mia,X) ?
First clause gives Y=ellen (you ask for more solutions)
Second clause gives Y=lucy (you ask again for more solutions)
Stack overflow !
Let's detail the third clause:
I want to know if friends(mia,Y) holds for some variable Y.
Is there a variable Z such that friends(mia,Z) holds ?
Notice that apart from a renaming from Y to Z, we are asking the same question as step 1 above? This smells like infinite recursion, but let's see...
We try the first two clauses of friends, but then we fail because there is no friends(ellen,Y) nor friends(lucy,Y), so...
We call the third clause in order to find if there is a transitive friendship, and we are back to step 1 without having progressed any further => infinite recursion.
This problem is analogous to infinite Left recursion in context-free grammars.
A fix
Have two predicates:
known_friends/2, which gives direct relationships.
friends/2, which also encodes transitivity
known_friends(mia,ellen).
known_friends(mia,lucy).
friends(X,Y) :- known_friends(X,Y).
friends(X,Y) :- known_friends(X,Z), friends(Z,Y).
Now, when we ask friends(mia,X), friends/2 gives the same answer as the two clauses of known_friends/2, but does not find any answer for the transitive clause: the difference here is that known_friends will make a little progress, namely find a known friend of mia (without recursion), and try to find (recursively) if that friend is a friend of some other people.
Friends' friends
If we add known_friends(ellen, bishop) :-) then friends will also find Y=bishop, because:
known_friends(mia,ellen) holds, and
friends(ellen,bishop) is found recursively.
Circularity
If you add cyclic dependencies in the friendship graph (in known_friends), then you will have an infinite traversal of this graph with friends. Before you can fix that, you have to consider the following questions:
Does friends(X,Y) <=> friends(Y,X) hold for all (X,Y) ?
What about friends(X,X), for all X ?
Then, you should keep a set of all seen people when evaluating friends in order to detect when you are looping through known_friends, while taking into account the above properties. This should not be too difficult too implement, if you want to try.
This clause of friends/2 is flawed:
friends(X,Y) :- friends(X,Z),friends(Y,Z).
Translate that into English: "If X and Y have a mutual friend Z, then X and Y are friends."
Or, let's specialize, let X be "me", let Y be my neighbour "FooBert", and let Z be "you": So if I am your friend and FooBert is your friend... does that make me and FooBert friends? I don't think so, I hate that guy---he always slams the door when he gets home. :)
I suggest you consider the algebraic properties that the relation friends/2 should have, the ones it may have, and ones it should not have. What about reflexivity, symmetry, anti-symmetry, transitivity?

Prolog dot product with variables (constraint satisfaction)

I'm working on a prolog assignment and I'm currently very very close to the solution. So, the problem is a constraint satisfaction problem where I have to find values for a set of variables such that certain conditions are true. Specifically, given 3 words (W1,W2,W3), assign their variables such that W1+W2=W3. An example of this would be SEND+MORE=MONEY, or IT+IS=ME.
The constraints are: (1) they have to add up correctly, (2) the starting letter cannot be 0 (3) and all variables must be distinct. And it has to work for a general word problem. My issue is happening when I try to ensure that they add up correctly (I've met the other conditions and I understand the problem). In terms of the second word problem we should have:
10*I + 1*T
+10*I + 1*S
___________
10*M + 1*E
So, I have made a function that makes lists of powers of 10 in a certain length, like so:
powlist(1,L) :-
append([1.0],[],L).
powlist(N,L) :-
N1 is N-1,
X is 10**N1,
powlist(N1,L1),
append([X],L1,L),
!.
I also have the actual list of letters, say, [I,T,I,S,M,E]. I then constructed a list of coefficients (I'll explain that part later) out of powlist so we have something like the following: [10,1,10,1,-10,-1]. I did this so if we took the dot product between this list of coefficients and the list of letters, and it was zero, the constraint would be satisfied. But, I can't get this dot product theory to work. I currently have a line that says:
scalar_product(Coefficients, Letters, #=, 0)
But this is giving me the following error:
! Instantiation error in argument 2 of is/2
! goal: _102 is 0+10.0*_109
I'm not sure how to define dot product so it can work on variables (instead of just atoms). All of the rest of the code works perfectly (and I don't want to put it on here because this is a very common question for introductory prolog courses, and I don't want to give lazy people answers). What do you guys suggest?
Your strategy is indeed sound and does work, at least with SWI-Prolog CLP(FD) using the built-in scalar_product/4. I am unfamiliar with the definition of this predicate in SICStus, but it's interface appears to be the same as in SWI-Prolog.
I can make a couple of suggestions. Firstly, perhaps some aspect of the code you've written is generating choicepoints which, when executed in backtracking (e.g., to seek alternate solutions, such as via label/1), the interpreter executes the subgoal _102 is 0+10.0*_109 where _109 is unintentionally unbound. Have you written a predicate which contains such a line? Even if not, I recommend double checking your code to ensure that they do not generate unnecessary choicepoints, such as your definition of powlist/2. I recommend that you try the following instead:
powlist(1, [1]) :- !.
powlist(N, [F|Fs]) :-
N > 1,
N1 is N - 1,
F is 10 ** N1,
powlist(N1, Fs).
This version leaves no choicepoints for the Prolog interpreter to backtrack to, which might resolve the problem (though, without seeing more code, I simply can't tell).
Otherwise, if you are correct and the error is indeed emanating from within the definition of scalar_product/4 (though I'd be surprised), then perhaps you could generate the scalar product constraint term and add it to the store yourself, manually. For example, consider:
my_scalar_product([V|Vs], [C|Cs], Op, Value) :-
construct_constraint(Vs, Cs, (V * C), Constr),
Constraint =.. [Op, Constr, Value],
Constraint.
construct_constraint([], [], Acc, Acc).
construct_constraint([V|Vs], [F|Fs], Acc, Res) :-
construct_constraint(Vs, Fs, '+'(Acc, (V * F)), Res).
This version (my_scalar_product/4) assumes the same interface as the built-in scalar_product/4, but it adds the constraint to the store instead of attempting to execute it using is/2.

Resources