Age Constraint Finding - prolog

So I have a simpler variation of the Einstein/Zebra puzzle in Prolog.
And I came up with this possible solution:
b_setval(T_age, Var).
friends(L) :-
L = [person(A1, B1, T_age), person(A2, B2, C2), person(A3, B3, T_age+3)],
:
:
member(person(_,yang,T_age+3),L),
member(person(_,_,18),L).
But my query friends(L). - false. only returns false as stated.
What am I doing wrong?

After following the answer of #luker, you can check your answer
friends(L) :-
% 1
L = [person(ada, _, Ta), person(ama, _, _), person(ana, _, _)],
% 2
member(person(_,_,15), L),
member(person(_,_,17), L),
member(person(_,_,18), L),
% 3
member(person(_, chang, _), L),
% 4
member(person(_, yang, Ty), L), Ty is Ta + 3,
% 5
member(person(_, thatcher, 17), L).
Interesting, this produces 2 results, which is weird for this kind of problem.

One potential problem that stands out is the T_age+3 term in the list L. In Prolog, this will not be arithmetically evaluated in-line. It will simply be the term, '+'(T_age,3). So the only element that would match this member of the list would be a term that looks like, person(X, Y, <something>+3). It's unclear whether this is your intention.
You can do a trace to see how variables are being instantiated with each member call, but let's try doing this manually for illustrative purposes:
L = [person(A1, B1, T_age), person(A2, B2, C2), person(A3, B3, T_age+3)],
member(person(ada, _,T_age),L),
...
This member call should succeed because Prolog can match it to person(A1, B1, T_age) in the list by unifying A1 = ada. The list L now looks like:
[person(ada, B1, T_age), person(A2, B2, C2), person(A3, B3, T_age+3)]
Moving on to the next member call:
member(person(ama, _, _),L),
...
This can't match the first member, but can match the second by unifying A2 = ama. L is now:
[person(ada, B1, T_age), person(ama, B2, C2), person(A3, B3, T_age+3)]
Then you have:
member(person(ana, _, _),L),
This can't match the first or second members, but can match the third by unifying A3 = ana. L is now:
[person(ada, B1, T_age), person(ama, B2, C2), person(ana, B3, T_age+3)]
The next member call is:
member(person(_,chang, _),L),
Which can match the first member again by unifying B1 = chang, so L becomes:
[person(ada, chang, T_age), person(ama, B2, C2), person(ana, B3, T_age+3)]
Then
member(person(_,yang,T_age+3),L),
This will match the second element of the list by unifying, B2 = yang and C2 = T_age+3. L then becomes:
[person(ada, chang, T_age), person(ama, yang, T_age+3), person(ana, B3, T_age+3)]
Then
member(person(_,thatcher,17),L),
This is where you have some trouble. It cannot match the first two elements of L because of the second argument. The third argument, 17 cannot match the term, T_age+3 in the third element of L. Remember: Prolog does not solve this as an equation T_age+3 = 17. It is just going to see 17 as an atomic integer, and see T_age+3 as a term with two arguments and find that they don't match. So this member call fails, and the whole predicate fails.

Related

Finding shortest path in water jugs problem

Here's my solution for the water jugs problem
:- use_module(library(clpfd)).
initial(state(8, 0, 0)).
final(state(4, 4, 0)).
constraint(A0, A1, B0, B1, C0, C1, V) :-
A0 #< A1,
( B0 #> 0, T #= min(V - A0, B0), A1 #= A0 + T, B1 #= B0 - T, C1 #= C0
; C0 #> 0, T #= min(V - A0, C0), A1 #= A0 + T, C1 #= C0 - T, B1 #= B0
).
transition(state(A0, B0, C0), state(A1, B1, C1)) :-
( constraint(A0, A1, B0, B1, C0, C1, 8)
; constraint(B0, B1, A0, A1, C0, C1, 5)
; constraint(C0, C1, A0, A1, B0, B1, 3)
).
solve(A, A, _, [A]).
solve(A, B, P, [A|Q]) :-
transition(A, A1),
\+ member(A1, P),
solve(A1, B, [A|P], Q).
path(P) :-
initial(S0),
final(S),
solve(S0, S, [], P).
Is there a way to find the P of minimal length without traversing all options?
Here is a solution that makes more use of the power of clpfd: First state the problem, then try to solve it (using labeling/2 or similar). Given that we do not know the length of the (shortest) path, this will generate larger and larger problems until a solution is found. In my code, I do not prevent visiting the same state twice (but this could be added in the same way as in the MiniZinc model written by #DavidTonhofer, or as some post-processing). However, in order to ensure a finite search space, I've added code to stop the problem generation if the length of the path is longer than (5+1)*(3+1), as this is an upper bound on the number of different states (assuming we have do not add or remove water outside of the 3 jugs).
:- use_module(library(clpfd)).
initial(state(8, 0, 0)).
final(state(4, 4, 0)).
constraint(A0,A1,B0,B1,C0,C1,R,Max):-
T#=min(Max-B0,A0),
R in 0..1,
R#==>T#>0,
R#==>A1#=A0-T,
R#==>B1#=B0+T,
R#==>C1#=C0.
transition(state(A0, B0, C0), state(A1, B1, C1)) :-
A0+B0+C0#=A1+B1+C1,
A0 in 0..8,
B0 in 0..5,
C0 in 0..3,
A1 in 0..8,
B1 in 0..5,
C1 in 0..3,
constraint(A0,A1,B0,B1,C0,C1,RAB,5),
constraint(B0,B1,A0,A1,C0,C1,RBA,8),
constraint(A0,A1,C0,C1,B0,B1,RAC,3),
constraint(C0,C1,A0,A1,B0,B1,RCA,8),
constraint(C0,C1,B0,B1,A0,A1,RCB,5),
constraint(B0,B1,C0,C1,A0,A1,RBC,3),
RAB+RBA+RAC+RCA+RCB+RBC#=1.
solve(A, A, Xs, [A]):-
labeling([],Xs).
solve(A, B, Xs, [A|Q]) :-
length(Xs, L),
L < 24*3,
transition(A, A1),
A=state(X1,X2,X3),
solve(A1, B, [X1,X2,X3|Xs], Q).
path(P) :-
initial(S0),
final(S),
solve(S0, S, [], P).
I tried to keep the code relatively close to the one in the question. The main difference is that all the prolog-level disjunctions in transition/2 and constraint/7 have been removed and replaced by reification. In particular, I added the parameter R to constraint/8 which is equal to 1 if that specific transition is taken. Then I state in transition/2 that exactly one of the transitions must take place.
I must add that this formulation is not particularly efficient and I would not be surprised to find out that one can solve the problem more efficiently with either a different clpfd formulation or without using clpfd at all.

Prolog equality check fails on simple task

Im trying to achieve this:
sum(bookmark(num)),
bookmark(bookmark(bum)),
bookmark(bookmark(bookmark(num))).
sum(bookmark(num), bookmark(bookmark(num)), A).
to return А = bookmark(bookmark(bookmark(num)))
Im not close yet, since i fail at the bookmark function as stated below.
bookmark(e1, e2) :-
e1 \= 0,
e2 \= 0.
/*#e1 = element-one, e2 = element-two*/
sum(e1, e2, result) :-
bookmark(e1, e2),
e1 is (1+1),
e2 is (1+1),
result = (e1 + e2).
Im checking if the bookmark function returns true, then the simple addition will follow. Problem is: if the input for the bookmark function is any two numbers, it returns false.
Example with bookmark(1, 2) - false.
Example with bookmark(0, 0) - false.
Any help why this doesn't work will be greatly appreciated.
In Prolog variables start with an uppercase. So e1 is not a variable, but a costant, E1 is a variable.
bookmark(E1, E2) :-
E1 \= 0,
E2 \= 0.
sum(E1, E2, Result) :-
bookmark(E1, E2),
E1 is (1+1),
E2 is (1+1),
Result = E1 + E2.
The above will however only succeed if E1 and E2 are both 2, and then Result will be 2 + 2 (not 4, just 2 + 2, or in canonical form +(2, 2)).

Sum values of variables?

I'm new with Prolog, and I'm trying to create a predicate that find a specific fact and sum the values.
This is my predicate:
position(X,T,P1,P2,P3,P4) :- object(X,C1,C2,C3,C4,T1),
T>T1,
move(X,S1,S2,S3,S4,T2),
T2>T1,
T2=<T,
P1 is C1+S1,P2 is C2+S2,P3 is C3+S3,P4 is C4+S4.
And what i get is this:
?- position(car0,31,P1,P2,P3,P4).
P1 = 930,
P2 = 278,
P3 = 1057,
P4 = 365 ;
P1 = 943,
P2 = 288,
P3 = 1058,
P4 = 370 ;
false.
I want the sum of all(S1,S2,S3,S4) values of move with the (C1,C2,C3,C4) of object but I don't know how to do this.
You try to do too much in a single predicate. It usually is better to split the logic in several predicates that each perform a certain task.
Cummulative coordinates
We can first create a list of list of values with findall/3 [swi-doc]:
all_moves(X, T, Diffs) :-
findall([T1, C1, C2, C3, C4], (move(X,C1,C2,C3,C4,T1), T1 <= T), Cs),
sort(Cs, Diffs).
We here thus sort the move/6s on timestamp, and create a list of 5-tuples with [T1, C1, C2, C3, C4] with T1 the time stamp, and C1, C2, C3, and C4 the timestamps.
Next we can define a cummulative sum function by making use of plus/3 [swi-doc] and maplist/3 [swi-doc]:
cumsum(P, _, P).
cumsum(P, [[_|D]|R], P2) :-
maplist(plus, P, D, P1),
cumsum(P1, R, P2).
then we can thus obtain the positions of an object with:
position(X,T,P1,P2,P3,P4) :-
object(X, C1, C2, C3, C4, T1),
T > T1,
all_moves(X, T, Mvs),
cumsum([C1, C2, C3, C4], Mvs, [P1, P2, P3, P4]).
Final coordinates
If we only need the final coordinates, we can improve the above. In that case, the order of the moves is irrelevant, as long as these are properly filtered. So we can define allmoves_unsorted/3, like:
all_moves_unsorted(X, T, Diffs) :-
findall([C1, C2, C3, C4], (move(X, C1, C2, C3, C4, T1), T1 <= T), Diffs).
Then we can sum these up with the object, with foldl/4 [swi-doc]:
final_position(X, T, S1, S2, S3, S4) :-
object(X, P1, P2, P3, P4, T1),
T1 < T,
all_moves_unsorted(X, T, Diffs),
foldl(maplist(plus), Diffs, [P1, P2, P3, P4], [S1, S2, S3, S4]).

crossword puzzle with prolog

Currently I am doing a Prolog tutorial.
There is an exercise to solve a crossword puzzle with 5 words.
My Problem is that Prolog stops unification for my solution at a very early point.
It looks like that:
And there is a small knowledge base given:
word(astante, a,s,t,a,n,t,e).
word(astoria, a,s,t,o,r,i,a).
word(baratto, b,a,r,a,t,t,o).
word(cobalto, c,o,b,a,l,t,o).
word(pistola, p,i,s,t,o,l,a).
word(statale, s,t,a,t,a,l,e).
To solve that task I have a predicate crossword/6.
So I have thought that the predicate crossword must contain 6 words that are made of variables and at every field where two words cross I have set there the same variable.
crossword(word(H1, A1, B1, C1, D1, E1, F1, G1),
word(H2, A2, B2, C2, D2, E2, F2, G2),
word(H3, A3, B3, C3, D3, E3, F3, G3),
word(V1, _1, B1, _2, B2, _3, B3, _4),
word(V2, _5, D1, _6, D2, _7, D3, _8),
word(V3, _9, F1, _10, F2, _11, F3 _12)).
In SWI-Prolog I have typed the following request:
?- crossword(H1, H2, H3, V1, V2, V3).
So I have asked for the solution of a crossword puzzle.
The result I get is like that:
H1 = word(_720, _722, _724, _726, _728, _730, _732, _734),
H2 = word(_738, _740, _742, _744, _746, _748, _750, _752),
H3 = word(_756, _758, _760, _762, _764, _766, _768, _770),
V1 = word(_774, _776, _724, _780, _742, _784, _760, _788),
V2 = word(_792, _794, _728, _798, _746, _802, _764, _806),
V3 = word(_810, _812, _732, _816, _750, _820, _768).
Question: Why does Prolog stop unification at such a early point ? And why doesn't it return any solution?
Your code declares a simple fact: there is a crossword/6 predicate whose arguments are word/8 predicates, and some of the arguments of the word/8 predicates are the same. In particular, since crossword/6 is declared as a simple fact, there's no relationship between the word/8 predicates in the crossword/6 declaration and the knowledge base (just like the fact for "astoria" doesn't constrain the fact for "astante").
Instead, only the words themselves are simple facts:
word(astante, a,s,t,a,n,t,e).
word(astoria, a,s,t,o,r,i,a).
word(baratto, b,a,r,a,t,t,o).
word(cobalto, c,o,b,a,l,t,o).
word(pistola, p,i,s,t,o,l,a).
word(statale, s,t,a,t,a,l,e).
Because these are simple facts with no conditions, we can always prove that there is a word/8 predicate whose first argument is astante/0, second argument is a/0, third argument is s/0, and so on.
What you want to say is that the six words form a valid solution if other things are true of those words:
crossword( H1, H2, H3, V1, V2, V3 ) :-
<conditions for a successful crossword solution>.
Next, define the conditions for crossword/6 so that a valid solution is one in which the variables unify with the first argument of word/8 predicates if the third, fifth, and seventh arguments of those word/8 arguments unify with each other in the right way.
For an (incomplete) example, I can say I have a valid cross-word solution if the second letter of H1 is the second letter of V1, and the sixth letter of H1 is the second letter of V3:
crossword( H1, H2, H3, V1, V2, V3 ) :-
word( H1, _, TL, _, _, _, TR, _ ),
word( V1, _, TL, _, _, _, _, _ ),
word( V3, _, TR, _, _, _, _, _ ).
Here I'm using underscore _ to avoid giving names to variables whose names don't matter. I'm also using TL and TR for "top left" and "top right" to make the reasoning easier on myself. Prolog sees that we can prove crossword/6 if we can prove that there are word/8 predicates whose arguments unify in a particular way, and searches for combinations of word/8 predicates that do so. The "knowledge base" provides axioms for each possible proof.
Do you see how to complete the crossword/6 definition now? Notice that you'll need to give some of the underscore variables (called "anonymous variables") names to complete the solution, and introduce additional word/8 terms on the right-hand side of the turnstyle.

Compare lists of functors and unify variables

How can I verify if two lists represent the same relationship between their variables in any given order and then unify the corresponding variables?
For example the list:
[#=(_G13544,_G13547+1),#=(_G13553,_G13554),#=(_G13559,2),#>(_G13559, _G13544)]
would be equivalent to:
[#>(_G13453,_G13430),#=(_G13409,_G13355),#=(_G13453,2),#=(_G13430,1+_G13370)]
because both could be written as:
[A#>B,C#=D,A#=2,B#=E+1]
and the variable would be bound in the following way:
_G13453 = _G13559 # Equivalent to A
_G13430 = _G13544 # Equivalent to B
_G13409 = _G13553 # Equivalent to C
_G13355 = _G13554 # Equivalent to D
_G13370 = _G13547 # Equivalent to E
The functors are the following CLPFD operators:
Symmetrical: #=/2,+/2,-/2, and #\//2;
Not symmetrical: #>/2, and #</2;
Unary: abs/1
something to get started
'same relationship between their variables'(L1, L2, Vs) :-
copy_term(L1, T1),
copy_term(L2, T2),
numbervars(T1, 0, N),
numbervars(T2, 0, N),
rel_pairs(T1, T2, [], Vs).
rel_pairs([], [], B, B).
rel_pairs(Xs, Ys, B0, B2) :-
select(X, Xs, Xr),
select(Y, Ys, Yr),
assign(X, Y, B0, B1),
rel_pairs(Xr, Yr, B1, B2).
assign(A#=B, C#=D, B0, B2) :-
assign(A, C, B0, B1),
assign(B, D, B1, B2)
;
assign(A, D, B0, B1),
assign(B, C, B1, B2).
assign(A#>B, C#>D, B0, B2) :-
assign(A, C, B0, B1),
assign(B, D, B1, B2).
assign(A+B, C+D, B0, B2) :-
assign(A, C, B0, B1),
assign(B, D, B1, B2)
;
assign(A, D, B0, B1),
assign(B, C, B1, B2).
assign('$VAR'(A), '$VAR'(B), B0, B0) :-
memberchk(A-B, B0), !.
assign('$VAR'(A), '$VAR'(B), B0, [A-B|B0]) :-
\+memberchk(A-_, B0),
\+memberchk(_-B, B0), !.
assign(X, X, B, B).
surely there are a number of improvements that could be done...

Resources