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.
Related
I like the idea of lazy_findall as it helps me with keeping predicates separated and hence program decomposition.
What are the cons of using lazy_findall and are there alternatives?
Below is my "coroutine" version of the branch and bound problem.
It starts with the problem setup:
domain([[a1, a2, a3],
[b1, b2, b3, b4],
[c1, c2]]).
price(a1, 1900).
price(a2, 750).
price(a3, 900).
price(b1, 300).
price(b2, 500).
price(b3, 450).
price(b4, 600).
price(c1, 700).
price(c2, 850).
incompatible(a2, c1).
incompatible(b2, c2).
incompatible(b3, c2).
incompatible(a2, b4).
incompatible(a1, b3).
incompatible(a3, b3).
Derived predicates:
all_compatible(_, []).
all_compatible(X, [Y|_]) :- incompatible(X, Y), !, fail.
all_compatible(X, [_|T]) :- all_compatible(X, T).
list_price(A, Threshold, P) :- list_price(A, Threshold, 0, P).
list_price([], _, P, P).
list_price([H|T], Threshold, P0, P) :-
price(H, P1),
P2 is P0 + P1,
P2 =< Threshold,
list_price(T, Threshold, P2, P).
path([], []).
path([H|T], [I|Q]) :-
member(I, H),
path(T, Q),
all_compatible(I, Q).
The actual logic:
solution([], Paths, Paths, Value, Value).
solution([C|D], Paths0, Paths, Value0, Value) :-
( list_price(C, Value0, V)
-> ( V < Value0
-> solution(D, [C], Paths, V, Value)
; solution(D, [C|Paths0], Paths, Value0, Value)
)
; solution(D, Paths0, Paths, Value0, Value)
).
The glue
solution(Paths, Value) :-
domain(D),
lazy_findall(P, path(D, P), Paths0),
solution(Paths0, [], Paths, 5000, Value).
Here is an alternative no-lazy-findall solution by #gusbro: https://stackoverflow.com/a/68415760/1646086
I am not familiar with lazy_findall but I observe two "drawbacks" with the presented approach:
The code is not as decoupled as one might want, because there is still a mix of "declarative" and "procedural" code in the same predicate. I am putting quotes around the terms because they can mean a lot of things but here I see that path/2 is concerned with both generating paths AND ensuring that they are valid. Similarly solution/5 (or rather list_price/3-4) is concerned with both computing the cost of paths and eliminating too costly ones with respect to some operational bound.
The "bounding" test only happens on complete paths. This means that in practice all paths are generated and verified in order to find the shortest one. It does not matter for such a small problem but might be important for larger instances. Ideally, one might want to detect for instance that the partial path [a1,?,?] will never bring a solution less than 2900 without trying all values for b and c.
My suggestion is to instead use clpfd (or clpz, depending on your system) to solve both issues. With clpfd, one can first state the problem without concern for how to solve it, then call a predefined predicate (like labeling/2) to solve the problem in a (hopefully) clever way.
Here is an example of code that starts from the same "setup" predicates as in the question.
state(Xs,Total):-
domain(Ds),
init_vars(Ds,Xs,Total),
post_comp(Ds,Xs).
init_vars([],[],0).
init_vars([D|Ds],[X|Xs],Total):-
prices(D,P),
length(D,N),
X in 1..N,
element(X, P, C),
Total #= C + Total0,
init_vars(Ds,Xs,Total0).
prices([],[]).
prices([V|Vs],[P|Ps]):-
price(V,P),
prices(Vs,Ps).
post_comp([],[]).
post_comp([D|Ds],[X|Xs]):-
post_comp0(Ds,D,Xs,X),
post_comp(Ds,Xs).
post_comp0([],_,[],_).
post_comp0([D2|Ds],D1,[X2|Xs],X1):-
post_comp1(D1,1,D2,X1,X2),
post_comp0(Ds,D1,Xs,X1).
post_comp1([],_,_,_,_).
post_comp1([V1|Vs1],N,Vs2,X1,X2):-
post_comp2(Vs2,1,V1,N,X2,X1),
N1 is N+1,
post_comp1(Vs1,N1,Vs2,X1,X2).
post_comp2([],_,_,_,_,_).
post_comp2([V2|Vs2],N2,V1,N1,X2,X1):-
post_comp3(V2,N2,X2,V1,N1,X1),
N3 is N2 + 1,
post_comp2(Vs2,N3,V1,N1,X2,X1).
post_comp3(V2,N2,X2,V1,N1,X1) :-
( ( incompatible(V2,V1)
; incompatible(V1,V2)
)
-> X2 #\= N2 #\/ X1 #\= N1
; true
).
Note that the code is relatively straightforward, except for the (quadruple) loop to post the incompatibility constraints. This is due to the way I wanted to reuse the predicates in the question. In practice, one might want to change the way the data is presented.
The problem can then be solved with the following query (in SWI-prolog):
?- state(Xs, T), labeling([min(T)], Xs).
T = 1900, Xs = [2, 1, 2] ?
In SICStus prolog, one can write instead:
?- state(Xs, T), minimize(labeling([], Xs), T).
Xs = [2,1,2], T = 1900 ?
Another short predicate could then transform back the [2,1,2] list into [a2,b1,c2] if that format was expected.
I'm new to prolog, and as I understand it, the purpose of 'distinct' is to weed out duplicates. However, this code block:
allpartsincity(City):-
distinct((proj(Project, _, City), sppj(_, Part, Project, _), part(Part, _, _, _, _))),
part(Part, Name, Color, Num, X),
format('~w ~w ~w ~w ~w ~n', [Part, Name, Color, Num, X]),
fail
;
true.
yields the following:
?- allpartsincity(london).
p2 bolt green 17 paris
p5 cam blue 12 paris
p2 bolt green 17 paris
p6 cog red 19 london
p5 cam blue 12 paris
true.
I'm not sure what I'm missing, but I'd appreciate if someone could point me in the right direction.
distinct/1 is a quite new predicate. It is only of relevance, if the incremental evaluation is of importance either because of infinite data or because (for some obscure reason) the exact order of answers is of relevance. OK, and maybe also because there are many redundant answers and the space to store them would be forbidding, but then a good setof/3 implementation might use a similar technique as well. In your case, you have just a data base of finitely many facts.
Since 19821, the classic predicate for your purpose is setof/3.
You did not give a minimal reproducible example. So I need to do some guessing. In any case, do trust the prolog-toplevel for printing.
city_part(City, CPart) :-
setof(t, city_part0(City, CPart), _).
city_part0(City, part(Part, Name, Color, Num, X)) :-
proj(Project, _A1, City),
sppj(_A2, Part, Project, _A3),
part(Part, Name, Color, Num, X).
You can avoid the intermediary predicate, but then the variable quantification will become cumbersome. I have given these variables already the names A1, A2, A3. These plus Project are only internal variables.
city_part(City, CPart) :-
setof(t, A1^A2^A3^Project^
( CPart = part(Part, Name, Color, Num, X),
proj(Project, A1, City),
sppj(A2, Part, Project, A3),
part(Part, Name, Color, Num, X)
), _).
As you wrote it, the goal part/5 that provides displayed values is unrelated to the conjunction you asked for in distinct/1. If I understand your problem correctly, most likely you should use distinct/2 instead. Try for instance
allpartsincity(City):-
distinct(part(Part, Name, Color, Num, X), (proj(Project, _, City), sppj(_, Part, Project, _), part(Part, _, _, _, _))),
format('~w ~w ~w ~w ~w ~n', [Part, Name, Color, Num, X]),
fail
;
true.
I'm somewhat new to prolog, and I'm trying to figure out how to achieve binding the elements of a list when order does not matter. I demonstrated that in the two last lines of my code. It seemed trivial to do this, but it fails to bind.
fh(Hand) :-
is_card(R, _, C1),
is_card(R, _, C2),
is_card(R, _, C3),
C1 \== C2,
C2 \== C3,
C1 \== C3,
is_card(RR, _, D1),
is_card(RR, _, D2),
R \= RR,
D1 \== D2,
is_set(Hand),
list_to_set([C1,C2,C3,D1,D2], Hand).
I would appreciate some hints. (predicate name and functionality concealed to prevent plagiarism)
You could construct your hand with more strict conditions to establish unique elements by placing an order on them rather than just checking that they are different:
fh(Hand) :-
is_card(R, _, C1),
is_card(R, _, C2),
is_card(R, _, C3),
C1 #< C2,
C2 #< C3,
is_card(RR, _, D1),
is_card(RR, _, D2),
dif(R, RR),
D1 #< D2.
This way, you're using ordering to construct your card hands such that you don't have any repeats. Since you don't care what the order is, having a particular order does not interfere with requirements and provides a means to establish uniqueness.
Reading the documentation available in library(lists), it seems that for your use case it might be best to represent a set as a sorted list without duplicates, made with sort/2.
Here is what I get:
?- A = [2,3,1],
B = [1,1,2,3,2,3],
sort(A, S),
sort(B, S).
A = [2, 3, 1],
B = [1, 1, 2, 3, 2, 3],
S = [1, 2, 3].
sort(A, S) will make sure that S is an ordered list with the same elements as A, and sort(B, S) will make sure that if you make a set from B it is the same as A.
I see that the implementation of is_set/1 is probable the same as length(Set, Lenght), sort(Set, Sorted), length(Sorted, Len). but I can't find the definition of '$skip_list'. But I see:
?- '$skip_list'(Len, [2,3,1], Tail).
Len = 3,
Tail = [].
Like the other commenters, I'll assume you are using is_set/1 from SWI-Prolog's library. In your code you have a call
is_set(Hand)
where Hand is a free variable. Your goal seems to be to use this as a sort of generator or "type declaration", but that doesn't work:
?- is_set(X).
false.
The documentation of is_set/1 reads in part:
is_set(#Set) [det]
True if Set is a proper list without duplicates.
There are a few things going on here. According to SWI's mode documentation, det means "Succeeds exactly once without a choice point", and # means "Argument will not be further instantiated than it is at call-time.". Putting both of these together, is_set/1 cannot be a generator since it doesn't enumerate solutions: It does not instantiate its argument to even a single solution (#), and even if it did, it would not succeed more than once (det). (In fact, a det predicate should never fail, but is_set/1 does; this seems to be a documentation bug, the correct annotation would be semidet.)
So: You cannot use is_set/1 as a generator, only as a type check. But even as a type check it doesn't help you here since a free variable is certainly not "a proper list". However, you don't need either a generator or a type check here. Remove the is_set/1 call, list_to_set/2 alone should do what you want.
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.
I have two dynamics of /2.
One of the lists, lets call it D2 has set values inside of it. For example: 2 and 3, 4 and 5.
How can I check if my dynamic 1 aka. D1 has all the values inside of it that D2 has and then return true if it does?
I tried to use
member(E, D1(_,_)), member(E, D2(_, _)). So far but without much luck.
This is pretty icky as far as data models go and whatever it is you're trying to do with this is going to at least be inefficient, if it can even be made to work. You'd be far better off defining an arity 3 fact with the first arg being an atom that identifies the type.
That said, you can probably do enough introspection to handle it.
dif(Q, P),
predicate_property(QR, dynamic),
predicate_property(PR, dynamic),
QR =.. [Q, _, _],
PR =.. [P, _, _].
This says, find me two predicates with arity 2, whose heads are different. Ideally, you want just the user-defined predicates. SWI-Prolog cannot do this, but GNU Prolog can, you could add some extra constraints:
predicate_property(QR, user),
predicate_property(PR, user),
This is my solution:
matching(Q, P) :-
dif(Q, P), % different predicates, please
predicate_property(QR, dynamic), % both dynamic
predicate_property(PR, dynamic),
QR =.. [Q, Q1, Q2], % arity-2 predicates, please
PR =.. [P, P1, P2],
findall([Q1, Q2], clause(QR, true), Qs), % find all facts (:- true)
findall([P1, P2], clause(PR, true), Ps),
forall(member(PV, Ps), member(PV, Qs)), % ensure the fact sets are equal
forall(member(QV, Qs), member(QV, Ps)).
Please, please, please DO NOT DO THIS!