Related
In SWI-Prolog I want to establish the list L from two lists L1 and L2 with the smallest count of elements under the condition, that 1 ∈ L1 and 1 ∈ L2.
If 1 ∉ L1 and 1 ∈ L2, then L = L1. If 1 ∈ L1 and 1 ∉ L2, then L = L2. If 1 ∉ L1 and 1 ∉ L2, then the predicate returns false.
I could evaluate this in Prolog with the following conditions:
minset_one(D1, D2, T) :- ((member(1, D1), not(member(1, D2))) -> T=D1).
minset_one(D1, D2, T) :- ((not(member(1, D1)), member(1, D2)) -> T=D2).
minset_one(D1, D2, T) :- (member(1, D1), member(1, D2), length(D1,L1), length(D2,L2), L1 >= L2) -> T=D2.
minset_one(D1, D2, T) :- (member(1, D1), member(1, D2), length(D1,L1), length(D2,L2), L2 > L1) -> T=D1.
My problem with that function is, the member function is called very often. Is their a way to reduce the complexity of that predicate in that way, the functions
member(1, D1)
member(1, D2)
length(D1, L1)
length(D2, L2)
are called only one time?
Both your code and the code in the answer of #TessellatingHacker lose logical-purity when the arguments of minset_one/3 are not sufficiently instantiated:
?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T).
D1 = [1,Y,Z], D2 = [1,V], T = D2
; false. % no more solutions!
This is clearly incomplete. There are other solutions. We lost logical-purity.
So, what can we do about this?
Basically, we have two options:
check D1, D2 and T upfront and throw an instantiation_error when the instantiation is not sufficient.
use building blocks that are better suited for code that preserves logical-purity.
In this answer I want to show how to realise option number two.
The code is based on if_/3 which is the core of library(reif).
In short, we reify the truth values of relations and use Prolog indexing on these values.
Using SWI-Prolog 8.4.2:
?- use_module(library(reif)).
First, shorter_than_t(Xs,Ys,T)
reifies "list Xs is shorter than Ys" into T:
shorter_than_t([],Ys,T) :-
aux_nil_shorter_than(Ys,T).
shorter_than_t([_|Xs],Ys,T) :-
aux_cons_shorter_than_t(Ys,Xs,T).
aux_nil_shorter_than_t([],false).
aux_nil_shorter_than_t([_|_],true).
aux_cons_shorter_than_t([],_,false).
aux_cons_shorter_than_t([_|Ys],Xs,T) :-
shorter_than_t(Xs,Ys,T).
Based on shorter_than_t/3 we define minset_one/3:
minset_one(D1,D2,T) :-
if_(shorter_than_t(D1,D2),
if_(memberd_t(1,D1), D1=T, (memberd_t(1,D2,true),D2=T)),
if_(memberd_t(1,D2), D2=T, (memberd_t(1,D1,true),D1=T))).
Now let's run above query again:
?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T).
D1 = [X,Y,Z], D2 = [1,V], T = D2
; D1 = [X,Y,Z], D2 = [U,1], T = D2, dif(U,1)
; D1 = [1,Y,Z], D2 = [U,V], T = D1, dif(U,1), dif(V,1)
; D1 = [X,1,Z], D2 = [U,V], T = D1, dif(U,1), dif(V,1), dif(X,1)
; D1 = [X,Y,1], D2 = [U,V], T = D1, dif(U,1), dif(V,1), dif(X,1), dif(Y,1)
; false.
At last, minset_one/3 has become complete!
I think you could do it with a wrapper / helper predicate which does those checks once, and then does a lookup of some fixed answers:
% minset_one(1 in D1, 1 in D2, D1, D2, D1Len, D2Len, T).
minset_one_(true, false, D1, _, _, _, D1).
minset_one_(false, true, _, D2, _, _, D2).
minset_one_(true, true, _, D2, D1Len, D2Len, D2) :- D1Len >= D2Len.
minset_one_(true, true, D1, _, D1Len, D2Len, D1) :- D1Len < D2Len.
minset_one(D1, D2, T) :-
(member(1, D1) -> D1check = true ; D1check = false),
(member(1, D2) -> D2check = true ; D2check = false),
length(D1, D1Len),
length(D2, D2Len),
minset_one_(D1check, D2check, D1, D2, D1Len, D2Len, T).
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.
given the query ?- p == 'p'., prolog outputs true.
But given the query p =\= 'p'., prolog outputs
ERROR: Arithmetic: `p' is not a function
ERROR: In:
Why is that? whats the difference between == and =/= so p is somehow evaluated differently?
The negation of == is not =\=, but \==.
From comparision operators:
T1 == T2 succeeds if terms T1 and T2 are identical
T1 \== T2 succeeds if terms T1 and T2 are not identical
E1 =:= E2 succeeds if values of expressions E1 and E2 are equal
E1 =\= E2 succeeds if values of expressions E1 and E2 are not equal
=\= expects p and 'p' to be expressions and attempts to evaluate, but they are not.
So I am working on a practice problem, where I need to find the number of cells with opening right, up , down and left. I have a working solution but I want to change it into clauses. I don't want to to use -> to define if and else. how can I fix the code below without the affecting the solution.
Here is the code:
stats(U,D,L,R) :- maze(Size,_,_,_,_),
findall(R, genXY(Size,R), Out),
statsHelp(Out,U, L, R, D).
statsHelp([],0,0,0,0).
statsHelp([[X|[Y]]|Tl],U, L, R, D) :- cell(X,Y,Dirs,Wt),
(contains1(u,Dirs) -> U1 is 1; U1 is 0), % how do i remove -> and separate them into clauses?
(contains1(d,Dirs) -> D1 is 1; D1 is 0),
(contains1(l,Dirs) -> L1 is 1; L1 is 0),
(contains1(r,Dirs) -> R1 is 1; R1 is 0),
statsHelp(Tl,U2, L2, R2, D2),
U is U1 + U2,
D is D1 + D2,
R is R1 + R2,
L is L1 + L2.
contains1(V,[V|Tl]).
contains1(V,[Hd|Tl]):-
contains1(V,Tl).
Note that your contains1/2 predicate is just the standard member/2, which you could use instead.
As for a version that always succeeds and produces an extra 0 or 1, I think you have to use a cut or negation to achieve that. Here is one with negation:
member_reified(X, Xs, Result) :-
member(X, Xs),
Result = 1.
member_reified(X, Xs, Result) :-
\+ member(X, Xs),
Result = 0.
You could of course also just move your use of -> into your definition of member_reified/3. Different implementations can give different trade-offs on backtracking; this one may succeed several times:
?- member_reified(x, [a, b, c], Result).
Result = 0.
?- member_reified(a, [a, b, c], Result).
Result = 1 ;
false.
?- member_reified(a, [a, b, c, a], Result).
Result = 1 ;
Result = 1 ;
false.
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.