Related
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.
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.
I am very inexperienced with Prolog. I have a data set that contains elements and relations in graph that has circularity (quite a lot). There are rules to calculate the summary relation of a path. One of these is: one takes the path, then takes the weakest relation, and that is the one that holds between both ends.
With
Elements E1, E2, E3 and
Relations R1/R1c, R2, R3 (strength low to high) and
structure E1-R3-E2, E1-R1-E2, E2-R2-E3, E3-R1-E2
I can make the following minimal example:
% weaker table
isWeaker( r1, r2).
isWeaker( r2, r3).
weaker( X, Y) :- isWeaker( X, Y).
weaker( X, Y) :-
isWeaker( X, Z),
weaker( Z, Y).
% 'weakest' is <= not <
weakest( X, X, Y) :- =(X,Y).
weakest( X, X, Y) :- weaker( X, Y).
% All direct relations
isADirectRelation( e1, r1, e2).
isADirectRelation( e1, r3, e2).
isADirectRelation( e2, r2, e3).
isADirectRelation( e3, r1, e2).
isADirectRelation( e1, r3, e4).
isADirectRelation( e4, r2, e3).
isADirectRelation( e1, r1, e4).
isADirectRelation( e3, r1, e4).
% derived relations calculations
% Structural Chains
isADerivedRelation( Source, Relation, Target, Visited) :-
\+ member( [Source,Relation,Target], Visited),
weakest( Relation, RelationOne, RelationTwo),
isARelation( Source, RelationOne, Intermediate, [[Source,Relation,Target]|Visited]),
isARelation( Intermediate, RelationTwo, Target, [[Source,Relation,Target]|Visited]).
% major workhorse with anti-circularity
isARelation( Source, Relation, Target, Visited) :-
(isADirectRelation( Source, Relation, Target);
isADerivedRelation( Source, Relation, Target, Visited)).
The result of isARelation( Source, Relation, Target, []). is
e1,r1,e2
e1,r3,e2
e2,r2,e3
e3,r1,e2
e1,r3,e4
e4,r2,e3
e1,r1,e4
e3,r1,e4
e1,r1,e3
e3,r1,e3
e1,r1,e3 duplicate
e3,r1,e3 duplicate
Missing are
e4,r1,e4
e2,r2,e2
Is it at all possible to solve this in Prolog? Formally, yes, of course, but also with a decent performance?
There are many things to be said about this question, so this will be a long and rambling and ultimately unsatisfactory answer. So I might as well start with a pet peeve: Please don't use camelCaseIdentifiers for predicates, we usually use underscore_separated_words instead. I'm not sure why this bugs me in Prolog in particular, but I suspect partly because uppercase letters are syntactically significant.
Moving on, your weakest/3 predicate is broken:
?- weakest(Weakest, r2, r1).
false.
I think you had this right in the first version of your question, but then you removed the third clause of weakest/3 because you thought it caused redundant answers. Needless to say, "efficiency" is useless without correctness. (Also, we usually put the "output" arguments last, not first.)
Part of the reason you get redundant answers is your use of two (indirectly) recursive calls to isARelation/4 in isADerivedRelation/4. What you are computing is something like the transitive closure of the union of "direct" relations. The usual way to express the transitive closure in Prolog is like this:
transitive_closure(A, B) :-
base_relation(A, B).
transitive_closure(A, C) :-
base_relation(A, B),
transitive_closure(B, C).
That is, we first "take a base step", then recurse. If our base relation has pairs a-b, b-c, c-d, then this will find the solution a-d exactly once, as the composition of the base pair a-b and the derived transitive pair b-d. In contrast, if we were to structure the second clause as you did, with two recursive calls to transitive_closure/2, we would get the solution a-d twice: Once as above, but also once because we would derive the transitive pair a-c and compose it with c-d to give a-d.
You can fix this by changing your first isARelation/4 call in isADerivedRelation/4 into a call to isADirectRelation/3.
Another problem is that you are using Visited wrong: You are marking the pair Source-Target as visited before you have proved that such a solution even exists! You should probably mark Source-Intermediate as visited instead.
Even so, you will still get redundant solutions for a pair of elements if there are several different paths between those elements in the graph. This is just how Prolog's logic works: Prolog finds individual answers to your query but does not allow you to talk directly about relationships between those answers. If we want to force it to enumerate everything exactly once, we must leave pure logic.
Some Prolog systems offer a feature called "tabling" which essentially caches all the solutions for a "tabled" predicate and avoids re-computations. This should avoid redundant answers and even simplify your definition: If your closure relation is tabled, you no longer need to track a Visited list because cyclic recomputations will be avoided by the tabling. I can't give you tested code because I have no Prolog with tabling lying around. Even without tabling offered by your system, there is the theoretical possibility of "memoizing" solutions yourself, using Prolog's impure database. It's difficult to get it exactly right with no redundant solutions whatsoever.
As an alternative to impure Prolog, your problem seems a better fit to datalog or answer-set-programming. These are programming models that use Prolog-like syntax but with set semantics that seems to be exactly what you want: A proposition is either a solution or not, there is no concept of redundant solutions. The entire set of solutions is computed in one go. Cycle elimination is also automatic, so you don't need (in fact, because of the restricted input language, cannot use) a Visited list. If I were you, I would try to do this in Datalog.
As a further Prolog extension, there might be a spiffy solution based on Constraint Handling Rules (CHR). But really, do try Datalog.
Finally, I don't see why you think that e2,r2,e2 is a missing solution. The only path from e2 to e2 that I see goes through e3 and back to e2 via an r1 relation, which is the weakest one, so the solution should be e2,r1,e2.
What I ended up with, thanks to also the comments by Lurker and answer by Isabelle is this:
% weaker table
isWeaker( r1, r2).
isWeaker( r2, r3).
weaker( X, Y) :- isWeaker( X, Y).
weaker( X, Y) :-
isWeaker( X, Z),
weaker( Z, Y).
% 'weakest' is <= not <
weakest( X, X, Y) :- =(X,Y).
weakest( X, X, Y) :- weaker( X, Y).
% All direct relations
isADirectRelation( e1, r1, e2).
isADirectRelation( e1, r3, e2).
isADirectRelation( e2, r2, e3).
isADirectRelation( e3, r1, e2).
isADirectRelation( e1, r3, e4).
isADirectRelation( e4, r2, e3).
isADirectRelation( e1, r1, e4).
isADirectRelation( e3, r1, e4).
% derived relations calculations
isARelation( Source, Relation, Target, _) :-
isADirectRelation( Source, Relation, Target).
% Structural Chains
isARelation( Source, Relation, Target, Visited) :-
\+ member( [Source,Relation,Target], Visited),
weakest( Relation, RelationOne, RelationTwo),
isADirectRelation( Source, RelationOne, Intermediate),
isARelation( Intermediate, RelationTwo, Target, [[Source,RelationOne,Intermediate]|Visited]).
isARelation( Source, Relation, Target, Visited) :-
\+ member( [Source,Relation,Target], Visited),
weakest( Relation, RelationOne, RelationTwo),
isADirectRelation( Source, RelationTwo, Intermediate),
isARelation( Intermediate, RelationOne, Target, [[Source,RelationTwo,Intermediate]|Visited]).
write_relation( Result) :-
write( Result), nl.
writeAllRelations :-
setof( (Relation, Source, Target), Relation^isARelation( Source, Relation, Target, []), ListOfAllRelations),
% maplist( write_relation, ListOfAllRelations). % For SWIProlog
write( ListOfAllRelations). % for JIProlog
This works and produces he right outcome:
r1,e1,e2
r1,e1,e3
r1,e1,e4
r1,e2,e2
r1,e2,e3
r1,e2,e4
r1,e3,e2
r1,e3,e3
r1,e3,e4
r1,e4,e2
r1,e4,e3
r1,e4,e4
r2,e1,e3
r2,e2,e3
r2,e4,e3
r3,e1,e2
r3,e1,e4
However, in the real world, with 60 or so entities and 800 or so direct relations, I've not found a Prolog that can handle it. I'll look into Datalog.
I have the following generic Breadth-first search code for Prolog and I would like to take the simple node representation s(a,b,go(a,b)) and change it to a predicate so that go(a,b) will represent a STRIPS operator say stack(A,B) so that I might have two predicates: s(S0,S,stack(A,B)) and s(S0,S,unstack(B,A)) (classic blocks world problem) which can be used by the breadth-first search below. I'm not sure if this is possible or how I would go about doing it. My first idea was to have a predicate as follows:
% S0 is the given state and S is the successor for the 'stack(A,B)' predicate if S0
% A and B are not the same blocks, and we want the next state S to contain the
% same state/preconditions information except we want to add 'on(A,B)'
% to S and we want to remove 'clear(B)' from S0
s(S0,S,stack(A,B)) :-
A \== B,
% other predicates etc
The breadth-first search is given below.
:- use_module(library(lists)).
% bfs(?initial_state, ?goal_state, ?solution)
% does not include cycle detection
bfs(X,Y,P) :-
bfs_a(Y,[n(X,[])],R),
reverse(R,P).
bfs_a(Y,[n(Y,P)|_],P).
bfs_a(Y,[n(S,P1)|Ns],P) :-
findall(n(S1,[A|P1]),s(S,S1,A),Es),
append(Ns,Es,O),
bfs_a(Y,O,P).
% s(?state, ?next_state, ?operator).
s(a,b,go(a,b)).
s(a,c,go(a,c)).
bfs(S0,Goal,Plan) :-
bfs_a(Goal1,[n(S0,[])],R),
subset(Goal,Goal1),
reverse(R,Plan).
bfs_a(Y,[n(Y,P)|_],P).
bfs_a(Y,[n(S,P1)|Ns],P) :-
findall(n(S1,[A|P1]), s(S,S1,A), Es),
append(Ns,Es,O),
bfs_a(Y,O,P).
s(State,NextState,Operation) :-
opn(Operation, PreList), subset(PreList, State),
deletes(Operation, DelList), subtract(State, DelList, TmpState),
adds(Operation, AddList), union(AddList, TmpState, NextState).
subset([ ],_).
subset([H|T],List) :-
member(H,List),
subset(T,List).
opn(move(Block,X1,X2),[clear(Block),clear(X2),on(Block,X1)]) :-
block(Block),object(X1),object(X2),
Block \== X1, X2 \== Block, X1 \== X2.
adds(move(Block,X1,X2),[on(Block,X2),clear(X1)]).
deletes(move(Block,X1,X2),[on(Block,X1),clear(X2)]).
object(X) :- place(X) ; block(X).
block(a).
block(b).
block(c).
block(d).
place(x1).
place(x2).
place(x3).
I'm writing a predicate to add two vectors. This is what I came up with:
add( [], [], 0 ).
add( [A], 0, A ).
add( [A], [B], C ) :- C is A + B.
add( A, B, C ) :- add( B, A, C ).
add( [H1|T1], [H2|T2], WYNIK ) :- X is H1 + H2, add( T1, T2, Y ), append( [X], Y, WYNIK ).
First four lines work just fine, but I can't get the last one to work - what do I do wrong?
In this order, the last line will never be run. The line:
add( A, B, C ) :- add( B, A, C ).
will unify with anything that hasn't already been handled by a rule above it.
As Jeff observed, the problem is the rule:
add( A, B, C ) :- add( B, A, C ).
In general, this is a rule that expresses something that you want to be true, but that doesn't help you solve a goal. The query add(1,2,X) by this rule leads to the subquery add(2,1,X), which leads to the subquery add(1,2,X): SLD resoltuion can spend forever on this branch (if it has no other rules of higher priority, and it doesn't spot that the rule doesn't make progress) without getting anywhere. You should only use such rule with conditions (e.g. strictlylessthan (B,A)) that ensure the rule is only applicable when it can do useful work. The issue of these kinds of rules is a reason why Prolog is not really a declarative language.
To regain commutativity, you'll need to add the rul:
add (0, [A], A).
Your add predicate is kind of odd: add([1],0,1) is true, as is add([1],[0],1), but add([0],1,1) is not, nor is add([1],[0],[1]). add is perfectly meaningful from a computational point of view, but is it really what you want?