In Prolog, can solutions be chosen in random order? - prolog

If I have the following:
a(X) :- X = 1; X = 2; X = 3; X = 4.
I can produce solutions in deterministic order:
?- a(X).
X = 1 ;
X = 2 ;
X = 3 ;
X = 4.
Is there any method of asking the system to produce solutions in non-deterministic, random order? For example:
?- a(X).
X = 4 ;
X = 1 ;
X = 3 ;
X = 2.
I'm aware that I can find all solutions then select one at random (findall(X, a(X), Y), random_member(Z, Y).) but this is too expensive in my case.
Possibly clearer example:
p(X,Y,Z) :-
(X = a; X = b; X = c; X = d), % (D1)
(Y = a; Y = b; Y = c), % (D2)
(Z = a; Z = b; Z = c; Z = d). % (D3)
When deterministic, generating the solution X = d, Y = c, Z = d using ?- p(X,Y,Z). will always go through the 47 previous solutions (4 * 3 * 4 = 48). However, if disjunctions are selected in non-deterministic order, the system might choose X = d at D1, Y = c at D2, Z = d at D3, generating it as the first solution.
This is being used for constrained AI-generated content, so there are many more variables in the real-world use-case.

From what you say in the comments, my impression is that a more important question for your use case is:
Can solutions be created in random order?
(This is because you say that you cannot create them all, and then choose a random one.)
To create them in a different order, Boris has hinted at a good way: Simply reorder the disjunctions!
For example, in the case you show:
p(X, Y, Z) :-
(X = a; X = b; X = c; X = d), % (D1)
(Y = a; Y = b; Y = c), % (D2)
(Z = a; Z = b; Z = c; Z = d). % (D3)
You could (automatically) create such declaratively equivalent versions of this snippet by exchanging the order if the disjunctions, such as: (X = c ; X = b ; etc.), and each of these snippets may yield the solutions in a different order.
However, it may be easier to first rewrite this to the equivalent version:
p(X, Y, Z) :-
member(X, [a,b,c,d]),
member(Y, [a,b,c]),
member(Z, [a,b,c,d]).
This way, it is easier to shuffle the lists and use the randomized lists to generate solutions.
For example, you can change this to:
p(X, Y, Z) :-
random_member(X, [a,b,c,d]),
random_member(Y, [a,b,c]),
random_member(Z, [a,b,c,d]).
random_member(X, Ls0) :-
random_permutation(Ls0, Ls),
member(X, Ls).
Now, you will get answers like:
?- p(X, Y, Z).
X = d,
Y = Z, Z = b ;
X = Z, Z = d,
Y = b ;
X = d,
Y = b,
Z = c ;
etc.
Note that this way to incorporate randomness to your code is impure: There is now implicit global state in your program, and you can no longer easily reproduce results that you need when describing test cases etc. for such programs. A solution preserving logical-purity has to make this state explicit, for example by carrying the random seed as one of the arguments, so that each run is completely reproducible.
Note that reordering conjunctions and/or goals like this works only for the pure and monotonic subset of Prolog, so make sure that you use declarative features like constraints to safely exchange goals, and to increase the generality of your code!

Related

Rule to test whether two lists contain same two elements fails due to uniqueness constraint

I'm trying to create a rule called redundancy that examines lists to see if two elements appear together in more than one list.
Here is my code:
columns([a,b,c]).
columns([b,c,d]).
in(X, [H|_]) :-
X = H.
in(X, [_|T]) :-
in(X, T).
redundancy(X, Y) :-
columns(A),
columns(B),
A \= B,
X \= Y,
in(X, A),
in(X, B),
in(Y, A),
in(Y, B).
The problem is the constraint X \= Y. I want it in there to exclude instances where X and Y are identical elements, which would be true for all single elements that appear in more than one list. But it only returns false for the given columns even though it should return permutations of b and c.
?- redundancy(U, T).
false.
If I comment out the constraint I get the expected elements along with the unwanted ones mentioned above.
?- redundancy(X, Y).
X = Y, Y = b ;
X = b,
Y = c ;
X = c,
Y = b ;
X = Y, Y = c ;
X = Y, Y = b ;
X = b,
Y = c ;
X = c,
Y = b ;
X = Y, Y = c ;
false.
Is there a way to enforce this constraint? I'm also interested in ideas to restrict results to a given combination of elements rather than permutations.
Simply move X \= Y to the last line of your predicate. also, see prolog-dif and instantiation-error.
The thing to avoid is using non-pure predicates with not-yet-instantiated logical variables (unless this is exactly what you intended, and you know what you're doing).
Another thing to notice is that X \= Y is not a constraint (that's dif), but a check.

Prolog: calculate index for a list of list

I'm new to prolog. I want to implement a predict called high/3. This predict supports two scenarios: One is to return the index of one character in a list of list, for example:
high([[a,b,c],[d,e,f], [g,h]], b, X) returns X = 1
high([[a,b,c],[d,e,f], [g,h]], f, X) returns X = 2
The second scenario is if you provide an index, it should also return all characters at that index position.
e.g.
high([[a,b,c],[d,e,f], [g,h]], X, 1) returns X = b; X= e; X= h
high([[a,b,c],[d,e,f], [g,h]], X, 2) returns X = c; X= f.
I wrote the following predirect:
high([[X|_]|L], X, 0).
high([[Head|Tail]|L], X, H):- member(X, Tail), high([Tail|L], X, H1),H is H1 + 1.
high([[Head|Tail]|L], X, H):- not(member(X, Tail)), high(L, X, H).
This predict only works for the first scenario, but it doesn't work properly for the second scenario.
If I run high([[a,b,c],[d,e,f], [g,h]], X, 1), it only returns X = b, but I expect it returns b, e, h there one by one.
Why does it return only b and fail?
It's a bit unclear what it should do in cases where there are identical elements in different lists. Nevertheless, here's my solution using library(clpfd):
:- use_module(library(clpfd)).
high([H|_], X, I) :-
high_(H, X, I, 0).
high([_|T], X, I) :-
high(T, X, I).
high_([X|_], X, I, I).
high_([_|T], X, I, J) :-
J #>= 0,
J #=< I,
J1 #= J + 1,
high_(T, X, I, J1).
This has the following behaviour:
?- high([[a,b,c],[d,e,f],[g,h]], b, I).
I = 1 ;
false.
?- high([[a,b,c],[d,e,f],[g,h]], f, I).
I = 2 ;
false.
?- high([[a,b,c],[d,e,f],[g,h]], X, 1).
X = b ;
X = e ;
X = h ;
false.
?- high([[a,b,c],[d,e,f],[g,h]], X, 2).
X = c ;
X = f ;
false.
But also works when there are duplicates:
?- high([[a,a],[b,a]], a, X).
X = 0 ;
X = 1 ;
X = 1 ;
false.
With unknown sublists:
?- high([A,B], X, 2).
A = [_4552, _4558, X|_4566] ;
B = [_4552, _4558, X|_4566] ;
false.
With an unknown list of lists:
?- high(L, X, 2).
L = [[_4518, _4524, X|_4532]|_4514] ;
L = [_4512, [_4524, _4530, X|_4538]|_4520] ;
L = [_4512, _4518, [_4530, _4536, X|_4544]|_4526] ;
…
It only returns one result because not(member(X, Tail)) will never be true as long as X hasn't been unified with anything (and Tail is not empty). In other words, since the second clause succeeds, the third one cannot and the recursion does not continue to handle the following lists.
However, I'd say you're going about this the wrong way. Your current code will also give wrong output if an element is present in multiple sublists.
You can break down your problem into smaller parts: you need to be able to relate and element to its index inside a single, simple list; and you need to be able to evaluate this for all sublists in your total list.
First things first: relate and element to its index:
index([X|_], X, 0).
index([_|T], X, I) :- index(T, X, I2), I is I2 + 1.
Very simple and easy to understand, right?
Now to recurse through all lists and match all elements/indices in them:
high([H|_], X, I) :- index(H, X, I).
high([_|T], X, I) :- high(T, X, I).
This will give all the expected outputs:
?- high([[a,b,c],[d,e,f], [g,h]], b, X)
X = 1;
false.
?- high([[a,b,c],[d,e,f], [g,h]], f, X)
X = 2;
false.
high([[a,b,c],[d,e,f], [g,h]], X, 1).
X = b;
X = e;
X = h;
false.
high([[a,b,c],[d,e,f], [g,h]], X, 2).
X = c;
X = f;
false.

Prolog: How do I list the different values that X and Y could be using prolog's clpfd library?

test(X, Y) :-
X ins 1..3,
Y ins 1..3,
X #\= Y.
Here is my attempt at doing it. The goal would be to type this into SWI-Prolog so that this output comes out.
?- test(X, Y).
X = 1
Y = 2 ;
X = 2,
Y = 1;
X = 3,
Y = 1 ;
... etc.
I'm actually trying to solve the 8-queens problem using prolog and have this so far.
eight_queens(Qs, L) :-
Qs = [ [X1,Y1], [X2, Y2], [X3, Y3], [X4, Y4], [X5, Y5], [X6, Y6], [X7, Y7], [X8, Y8], [X9, Y9] ],
Qs ins 1..9,
X1 #\= X2,
X1 #\= X3,
...
etc.
But I keep getting this error: "Arguments are not sufficiently instantiated" for both the test function and the eight_queens problem.
Besides the observation about in/2 and ins/2 posted by #coder, that solve your imminent problem, I would add the following points that are good to keep in mind when using CLP(FD):
1. Always make labeling the last goal
First let's observe the answers for the variant marked as 2nd way using ins in #coder's post but without the goal label/1:
test(X, Y) :-
[X,Y] ins 1..3,
X #\= Y.
?- test(X,Y).
X in 1..3, % residual goal
X#\=Y, % residual goal
Y in 1..3. % residual goal
Since there is no unique answer to the query, Prolog answers with residual goals (see section A.8.8 of the CLP(FD) manual) for more information). These residual goals are constraints that are being propagated and with every additional (non-redundant) constraint the domain is narrowed. If this does not lead to a unique solution like in the example above you can get concrete values by labeling the constrained variables (e.g. with label/1). This observation suggests to use labeling as the last goal:
?- test(X,Y), label([X,Y]).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2.
This is obviously the same result as with #coders version but the three pairs (X,Y) = (1,1) ∨ (2,2) ∨ (3,3) are not considered when labeling due to the constraint X#\=Y being posted before the goal label([X,Y]). In #coder's version it is the other way around: label([X,Y]) is delivering all three pairs as possible solutions and the last goal X#\=Y is eliminating them subsequently. To see this just leave the last goal as a comment and query the predicate:
test(X,Y):- [X,Y] ins 1..3, label([X,Y]). %, X#\=Y.
?- test(X,Y).
X = Y, Y = 1 ; % <- (1,1)
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = Y, Y = 2 ; % <- (2,2)
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
X = Y, Y = 3. % <- (3,3)
The difference is minuscule in this example, so there's nothing wrong with #coder's version. But in general this might lead to a big difference if the constraints posted after labeling exclude a lot of candidates. So it's good practice to always put labeling as the last goal.
2. Separate labeling from the actual relation
Coming from the previous observations it is opportune to divide the predicate into a core relation that is posting all the constraints and labeling. Consider the restructured predicate test/2 as a template:
test(X,Y) :-
test_(X,Y,L), % the core relation
label(L). % labeling
test_(X,Y,L) :-
L=[X,Y], % variables to be labeled in a flat list
L ins 1..3,
X#\=Y.
The predicate test_/3 is describing the actual relation by posting all the necessary constraints and has a list as an additional argument that contains all the variables to be labeled. Obtaining the latter might not be trivial, depending on the data structures your arguments come with (consider for example a list of lists as an argument that you want to turn into a flat list for labeling). So the predicate test/2 is only calling test_/3 and subsequently the labeling goal. This way you have a clean and easily readable separation.
3. Try different labeling strategies
The goal label(L) is the simplest way to do labeling. It is equivalent to labeling([],L). The first argument of labeling/2 is a list of options that gives you some control over the search process, e.g. labeling([ff],L) labels the leftmost variable with the smallest domain next, in order to detect infeasibility early. Depending on the problem you are trying to solve different labeling strategies can lead to results faster or slower. See the documentation of labeling/2 for available labeling strategies and further examples.
ins is used for lists, in is used for single variable so in your example:
test(X, Y) :-
X ins 1..3,
Y ins 1..3,
X #\= Y.
X,Y are assumed to be lists. This does not produces a syntax error, but produces error when trying to run it with X,Y not being lists.
Also when using in Low..High doesn't mean that the variable is int just X=<High and X>=Low. In order to put the constraint to be integers use label/1:
:- use_module(library(clpfd)).
%using in/
test(X,Y):- X in 1..3,Y in 1..3,label([X,Y]), X#\=Y.
%2nd way using ins
test(X,Y):- [X,Y] ins 1..3, label([X,Y]), X#\=Y.
Example:
?- test(X,Y).
X = 1,
Y = 2 ;
X = 1,
Y = 3 ;
X = 2,
Y = 1 ;
X = 2,
Y = 3 ;
X = 3,
Y = 1 ;
X = 3,
Y = 2 ;
false.

ERROR: Out of local stack in my Prolog code

I cannot figure out why the following query from the given Prolog code generates the error Out of local stack.
Prolog code:
likes(g,c).
likes(c,a).
likes(c,b).
likes(b,a).
likes(b,d).
likes(X,Z) :- likes(X,Y), likes(Y,Z).
the query
?- likes(g,X).
results in
X = c ;
X = a ;
X = b ;
ERROR: Out of local stack
Edit 1 This is the way I think that Prolog should deal with this query,
likes(g,c) is a fact, so X={c}
likes(g,b) <= likes(g,c) and likes(c,b), so X={c,b}
likes(g,a) <= likes(g,b) and likes(b,a), so X={c,b,a}
likes(g,d) <= likes(g,b) and likes(b,d), so X={c,b,a,d}
likes(g,a) and false, so nothing to add to X
likes(g,d) and false, so nothing to add to X
end of backtracking search.
Edit 2 I managed to get what I was looking for by the following modification of the code:
likes(g,c).
likes(c,a).
likes(c,b).
likes(b,a).
likes(b,d).
indirect_likes(A,B):- likes(A,B).
indirect_likes(A,C):- likes(B,C), indirect_likes(A,B).
the query
?- indirect_likes(g,Which).
results in
Which = c ;
Which = a ;
Which = b ;
Which = a ;
Which = d ;
false.
However, there is still something which I cannot figure out the rationale behind. If I change the last rule to be
indirect_likes(A,C):- indirect_likes(A,B), likes(B,C).
Then I get ERROR: Out of local stack! As far as I know, logical conjunction is commutative.
To get the transitive-closure of binary relation R_2, use meta-predicate closure/3 like so:
?- closure(R_2,From,To).
Let's run a sample query of closure/3 together with likes/2!
?- closure(likes,X,Y).
X = g, Y = c
; X = g, Y = a
; X = g, Y = b
; X = g, Y = a % redundant
; X = g, Y = d
; X = c, Y = a
; X = c, Y = b
; X = c, Y = a % redundant
; X = c, Y = d
; X = b, Y = a
; X = b, Y = d
; false. % query terminates universally
We get the same answers when we use indirect_likes/2, but in a different order:
?- indirect_likes(X,Y).
X = g, Y = c
; X = c, Y = a
; X = c, Y = b
; X = b, Y = a
; X = b, Y = d
; X = g, Y = a
; X = g, Y = b
; X = c, Y = a % redundant
; X = g, Y = a % redundant
; X = c, Y = d
; X = g, Y = d
; false. % query terminates universally
As you stated in your comments to #C.B.'s answer, binary relations are not necessarily reflexive and/or symmetric. With the definition you gave, likes/2 is neither:
?- likes(X,X).
false. % not reflexive (not even close)
?- likes(X,Y), likes(Y,X).
false. % not symmetric (not even close)
So far, so good!
Let's tentatively add the following additional fact to your database:
likes(b,b).
With this expanded definition, indirect_likes/2 behaves erratically:
?- indirect_likes(b,b).
true
; true
; true
... % does not terminate universally
?- indirect_likes(X,Y), false. % do we get finite failure?
... % no! query does not terminate universally
What can we do? Let's use meta-predicate closure/3 with the expanded version of likes/2!
?- closure(likes,b,b).
true % succeeds non-deterministically
; false. % query terminates universally
?- closure(likes,X,Y), false. % do we get finite failure?
false. % yes! query terminates universally
The bottom line?
In pure Prolog, conjunction is commutative, as far as the logical meaning is concerned.
Due to Prolog's SLD resolution, the goal false,repeat finitely fails, but repeat,false does not.
The programmer needs to take care of termination, but usually this is a small price to pay for the raw performance and control that Prolog offers.
In this answer I passed "termination worries" on to the implementor of meta-predicate closure/3 :)
You spin off into infinite recursion.
Once you get to likes(b,a), you call likes(a,_G4382), which has no fact defined so it switches to the rule likes(X,Z) :- likes(X,Y), likes(Y,Z). So it calls likes(a,_G4383) which calls likes(X,Z) :- likes(X,Y), likes(Y,Z). over and over and over.
You might want to define and auxillary predicate aux_likes/2 that hosts all your facts. This will only work if there are no circular relationships, i.e. aux_likes(b,c) and aux_likes(c,b). You would also need to define likes(X,X). This is essentially a graph problem and in graph theory a node has to be connected to itself. If you use it as a generator, it will go off into into infinite recursion (if you have cycles) unless you add cuts which are not ideal.
If using swi-prolog, feel free to enter the debug or spy query to see what's going on.
Code:
aux_likes(g,c).
aux_likes(c,a).
aux_likes(c,b).
aux_likes(b,a).
aux_likes(b,d).
likes(X,Z) :- aux_likes(X,Y), likes(Y,Z).
likes(X,X).
Output with new predicate:
?- likes(g,X),print(X),nl,fail.
a
a
d
b
c
g
false.
This says g can like a through c or through b. It likes d through b, it likes b through c and it likes c directly. It also must like itself inorder to query like this. If you would rather have the usage mode (+,+) meaning you supply it with both terms and no variables (as a checker) you can do
?- likes(g,c).
true .

Learning prolog, some list functions

I am working on an assignment that deals with lists in prolog. The basic idea is that given a list, prolog should be able to determine if a value is repeated at all, repeated only once, or repeated only twice, etc. I thought the simplest solution would be to count the number of times a value occurs and then use that count to determine how many times it is repeated.
list_count([],X,0).
list_count([X|T],X,Y) :- list_count(T,X,Z), Y is 1 + Z.
list_count([X1|T],X,Z) :- X1 \= X, list_count(T,X,Z).
repeated_in(+E,+List) :- list_count(List,E,Num), Num >= 2.
No matter what I do though my first predicate always fails. Help?
list_count/3 does work. I think the only issue is the improper usage of prefix '+': try
% repeated_in(+E,+List)
repeated_in(E,List):- list_count(List,E,Num), Num >= 2.
note: prefixing arguments is used for documentation purpose, as a recap about mode usage
Here a logically pure implementation, based on
if_/3 and (=)/3 by #false.
atLeastOnceMember_of(E,[X|Xs]) :-
if_(E = X, true, atLeastOnceMember_of(E,Xs)).
atLeastTwiceMember_of(E,[X|Xs]) :-
if_(E = X, atLeastOnceMember_of(E,Xs), atLeastTwiceMember_of(E,Xs)).
First, let's look at the queries you suggested in your question:
?- atLeastTwiceMember_of(a,[a,b,a,b,a,c]).
true. % succeeds deterministically
?- atLeastTwiceMember_of(b,[a,b,a,b,a,c]).
true. % succeeds deterministically
?- atLeastTwiceMember_of(c,[a,b,a,b,a,c]).
false.
?- atLeastTwiceMember_of(x,[a,b,a,b,a,c]).
false.
The code is monotone, so we get logically sound answers for more general uses, too!
?- atLeastTwiceMember_of(X,[a,b,a,b,a,c]).
X = a ;
X = b ;
false.
At last, let us consider a generalization of above query:
?- atLeastTwiceMember_of(X,[A,B,C,D,E,F]).
X = A, A = B ;
X = A, A = C, dif(C,B) ;
X = A, A = D, dif(D,C), dif(D,B) ;
X = A, A = E, dif(E,D), dif(E,C), dif(E,B) ;
X = A, A = F, dif(F,E), dif(F,D), dif(F,C), dif(F,B) ;
X = B, B = C, dif(C,A) ;
X = B, B = D, dif(D,C), dif(D,A) ;
X = B, B = E, dif(E,D), dif(E,C), dif(E,A) ;
X = B, B = F, dif(F,E), dif(F,D), dif(F,C), dif(F,A) ;
X = C, C = D, dif(D,B), dif(D,A) ;
X = C, C = E, dif(E,D), dif(E,B), dif(E,A) ;
X = C, C = F, dif(F,E), dif(F,D), dif(F,B), dif(F,A) ;
X = D, D = E, dif(E,C), dif(E,B), dif(E,A) ;
X = D, D = F, dif(F,E), dif(F,C), dif(F,B), dif(F,A) ;
X = E, E = F, dif(F,D), dif(F,C), dif(F,B), dif(F,A) ;
false.

Resources