Related
So I've been learning Prolog, and to test myself I wanted to solve a certain puzzle. I think I'm pretty close to solving it, but I seem to be doing something wrong. It seems my predicate block/3 is not called.
maybe it's a syntax error, or I'm doing something that doesn't work in Prolog. I can't see it.
block/3 is supposed to give all possible combinations of sets in block/2.
I'm not sure if it's entirely relevant but I'll include the goal of the puzzle:
There's four cubes, with different combination of four images on their faces. (Kim,Lab,Hail and Com)
The goal is to align the cubes in such a way that if you put them together, all the sides next to each other should be the same. So it's four of the same rows going around each cube, and then two sides that should also be matching.
I wrote the program to just solve the rows, and disregarding the orientation of the images and the two sides. Should that give more than one answer it shouldn't give too many to manually try.
anyway, somehow solve(X) completely ignores my predicate block/3. I've been staring at it for a long time and I can't find the issue.
member( block(1, _, Row), X )
is equivalent to
E = block(1, _, Row), member( E, X )
so it does not in fact call block/3 as a predicate, it just uses it as a compound term, symbolically.
I am learning prolog, what I am doing is writing a predicate to join two list. For example, if I query:
joinL([22,33,44],[1,2,3],L)
It will show L = [22,33,44,1,2,3].
To do it, I have tried to write predicate as followings:
joinL([],L2,L2).
joinL([H|T],L2,L):-joinL(T,L2,L),L = [H|L].
But when I query
joinL([22,33,44],[1,2,3],L)
It does not show desired result as i have just described above. Actually, it returns false.
What I want to ask is: "How did my code become wrong?", I do NOT ask "How to write predicate that join two list in prolog?" cause I can google it easily, and when compare with my code, I curiously want to know why i am wrong with my code. Can any one help me! Thank you all for reading and answering my question!
The problem is that you are using the = in the same way as one would use assignment:
L = [H|L]
In a state-changing language this means that whatever is stored in L (which is supposed to be a list) becomes a new list, made by tacking H to the front: [H|L]
In Prolog this states that what we know about L is that it is equal to [H|L]- equal to itself with H tacked to the front. This is not possible for any L though (actually, it is, if L is an infinite list containing only H but the proof engine of Prolog is not good enough to deal with that). Prolog's proof search fails at that hurdle and will return "false" - there are no solutions to the logic program you have entered.
(More after a coffee)
Here is how to think about this:
Ok, so I would like to state some logic facts about the problem of "list concatenation" so that, based on those logic facts, and given two completely-specified lists L1, L2, Prolog's proof search can determine enough about what the concatenated list LJ should look like to actually output it completely!
We decide to specify a predicate joinL(list1,list2,joinedlist) to express this.
First, we cover a special edge case:
joinL([],L2,LJ) :- LJ = L2.
So, it is stated that the 'joinL' relationship between the empty list '[]' and the joined list 'LJ' is such that 'LJ' is necessarily equal to 'L2'.
The logical reading is:
(LJ = L2) → joinL([],L2,LJ)
The operational reading is:
In order to prove joinL([],L2,LJ) you must prove LJ = L2 (which can either be verified if LJ and L2 are already known or can be added to the solution's known constraints if not.
There is also the reading of the SLD resolution, where you add the negation of joinL([],L2,LJ) to your set of logic facts, then try to prove ⊥ (the contradiction also know as the empty statement) using resolution, but I have not found that view to be particularly helpful.
Anyway, let's state more things about the edge cases:
joinL([],L2,LJ) :- LJ = L2.
joinL(L1,[],LJ) :- LJ = L1.
joinL([],[],LJ) :- LJ = [].
This will already enable the Prolog proof engine to determine LJ completely whenever any of the L1 and L2 is the empty list.
One commonly abbreviates to:
joinL([],L,L).
joinL(L,[],L).
joinL([],[],[]).
(The above abbreviation would not be possible in Picat for example)
And the third statement can be dropped because the other two "subsume it" - they cover that case more generally. Thus:
joinL([],L,L).
joinL(L,[],L).
Now for the case of non-empty lists. A fat part of logic programming is about inductive (or recursive) definitions of predicates (see this), so let's go:
joinL([H|T],L2,LJ) :- LJ = [H|LX], joinL(T,L2,LX).
Again, this is just a specification, where we say that the concatenation of a nonempty list [H|T] and any list L2 is a list LJ such that LJ is composed of H and a list LX and LX is the concatenation of T and L2.
This is useful to the Prolog proof engine because it gives more information about LJ (in fact, it specifies what the first element of LJ is) and reduces the problem to finding out more using the same predicate but a problem that is a little nearer to the base case with the empty list: joinL(T,L2,LX). If the proof goes down that route it will eventually hit joinL([],L2,LX), find out that L2 = LX and be able to successfully return from its descent.
joinL([H|T],L2,LJ) :- LJ = [H|LX], joinL(T,L2,LX).
is commonly abbreviated to
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
Looks like we have covered everything with:
joinL([],L,L).
joinL(L,[],L).
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
We can even drop the second statement, as it is covered by the recursive descent with L2 always equal to '[]'. It gives us a shorter program which burns cycles needlessly when L2 is '[]':
joinL([],L,L).
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
Let's test this. One should use unit tests but I can't be bothered now and will just run these in SWISH. Let's see what Prolog can find out about X:
joinL([],[],X). % X = []
joinL([1,2],[],X). % X = [1,2]
joinL([],[1,2],X). % X = [1,2]
joinL([3,4],[1,2],X). % X = [3,4,1,2]
joinL([1,2],[3,4],X). % X = [1,2,3,4]
One can constrain the result completely, transforming Prolog into a checker:
joinL([3,4],[1,2],[3,4,1,2]). % true
joinL([3,4],[1,2],[1,1,1,1]). % false
Sometimes the predicate works backwards too, but often more careful design is needed. Not here:
joinL([3,4],L2,[3,4,1,2]). % L2 = [1, 2]
For this one, Prolog suggests a second solution might exist but there is none of course:
joinL(L1,[3,4],[1,2,3,4]). % L1 = [1, 2]
Find me something impossible:
joinL(L1,[3,4],[1,2,100,100]). % false
The goal is to select shapes that don't touch each other using constraints (clpfd). Calling start(Pairs,4) would return Pairs = [1,3,5,7].
One problem I noticed is that if I print Final before labeling, it prints [1,3,5,7]. Which means labeling isn't doing anything.
What could I change/add to this code in order to fix that and also remove possible backtracking?
:-use_module(library(clpfd)).
:-use_module(library(lists)).
% init initialises Pairs and Max
% Pairs - The elements inside the Nth list in Pairs,
% represent the index of the shapes that shape N can touch
init([[3,5,6,7],[4,5,7],[1,4,5,7],[2,3,7],[1,2,3,7],[1],[1,2,3,4,5]],7).
start(Final, N):-
init(Pairs, Max),
length(Final, N),
domain(Final, 1, Max),
ascending(Final),
all_different(Final),
rules(Pairs,Final),
labeling([],Final).
rules(_,[]).
rules(Pairs,[H|T]):-
nth1(H,Pairs,PairH),
secondrule(PairH,T),
rules(Pairs,T).
secondrule(_, []).
secondrule(PairH, [H|T]):-
element(_,PairH,H),
secondrule(PairH, T).
ascending([_|[]]).
ascending([H|[T1|T2]]):-
H #< T1,
ascending([T1|T2]).
This is an Independent Set problem, which is an NP-hard problem. Therefore, it is unlikely that anybody will ever find a way to do it without search (backtracking) for general instances.
Regarding your code, labeling/2 does nothing, because your rules/2 is in fact a search procedure that returns the solution it it can find it. all_different/1 is useless too, because it is implied by ascending/1.
Presumably, your goal is a program that sets up constraints (without any search) and then searches for a solution with labeling/2. For that, you need to rethink your constraint model. Read up a bit on independent sets.
I'm trying to re-familiarize myself with Prolog and I thought this could be the type of problem with an elegant solution in Prolog.
I'm following along this example:
http://home.deib.polimi.it/matteucc/Clustering/tutorial_html/hierarchical.html
I've tried a variety of data formats:
dist('BA','FI',662).
dist(0,'BA','FI',662).
dist(['BA'],['FI'],662).
but I haven't found any particular one most suitable.
Here's all the data in the first format:
%% Graph distances
dist('BA','FI',662).
dist('BA','MI',877).
dist('BA','NA',255).
dist('BA','RM',412).
dist('BA','TO',996).
dist('FI','MI',295).
dist('FI','NA',468).
dist('FI','RM',268).
dist('FI','TO',400).
dist('MI','NA',754).
dist('MI','RM',564).
dist('MI','TO',138).
dist('NA','RM',219).
dist('NA','TO',869).
dist('RM','TO',669).
Now, there seems to be some awesome structure to this problem to exploit, but I'm really struggling to get a grasp of it. I think I've got the first cluster here (thought it may not be the most elegant way of doing it ;)
minDist(A,B,D) :- dist(A,B,D), dist(X,Y,Z), A \= X, A \= Y, B \= X, B \= Y, D < Z.
min(A,B,B) :- B < A
min(A,B,A) :- A < B
dist([A,B],C, D) :- minDist(A,B,D), dist(A,C,Q), dist(B,C,W), min(Q,W,D)
The problem I have here is the concept of "replacing" the dist statements involving A and B with the cluster.
This just quickly become a brainteaser for me and I'm stuck. Any ideas on how to formulate this? Or is this perhaps just not the kind of problem elegantly solved with Prolog?
Your table is actually perfect! The problem is that you don't have an intermediate data structure. I'm guessing you'll find the following code pretty surprising. In Prolog, you can simply use whatever structures you want, and it will actually work. First let's get the preliminary we need for calculating distance without regard for argument order:
distance(X, Y, Dist) :- dist(X, Y, Dist) ; dist(Y, X, Dist).
This just swaps the order if it doesn't get a distance on the first try.
Another utility we'll need: the list of cities:
all_cities(['BA','FI','MI','NA','RM','TO']).
This is just helpful; we could compute it, but it would be tedious and weird looking.
OK, so the end of the linked article makes it clear that what is actually being created is a tree structure. The article doesn't show you the tree at all until you get to the end, so it isn't obvious that's what's going on in the merges. In Prolog, we can simply use the structure we want and there it is, and it will work. To demonstrate, let's enumerate the items in a tree with something like member/2 for lists:
% Our clustering forms a tree. So we need to be able to do some basic
% operations on the tree, like get all of the cities in the tree. This
% predicate shows how that is done, and shows what the structure of
% the cluster is going to look like.
cluster_member(X, leaf(X)).
cluster_member(X, cluster(Left, Right)) :-
cluster_member(X, Left) ; cluster_member(X, Right).
So you can see we're going to be making use of trees using leaf('FI') for instance, to represent a leaf-node, a cluster of N=1, and cluster(X,Y) to represent a cluster tree with two branches. The code above lets you enumerate all the cities within a cluster, which we'll need to compute the minimum distance between them.
% To calculate the minimum distance between two cluster positions we
% need to basically pair up each city from each side of the cluster
% and find the minimum.
cluster_distance(X, Y, Distance) :-
setof(D,
XCity^YCity^(
cluster_member(XCity, X),
cluster_member(YCity, Y),
distance(XCity, YCity, D)),
[Distance|_]).
This probably looks pretty weird. I'm cheating here. The setof/3 metapredicate finds solutions for a particular goal. The calling pattern is something like setof(Template, Goal, Result) where the Result will become a list of Template for each Goal success. This is just like bagof/3 except that setof/3 gives you unique results. How does it do that? By sorting! My third argument is [Distance|_], saying just give me the first item in the result list. Because the result is sorted, the first item in the list will be the smallest. It's a big cheat!
The XCity^YCity^ notation says to setof/3: I don't care what these variables actually are. It marks them as "existential variables." This means Prolog will not provide multiple solutions for each city combination; they will all be thrown together and sorted once.
This is all we need to perform the clustering!
From the article, the base case is when you have two clusters left: just combine them:
% OK, the base case for clustering is that we have two items left, so
% we cluster them together.
cluster([Left,Right], cluster(Left,Right)).
The inductive case takes the list of results and finds the two which are nearest and combines them. Hold on!
% The inductive case is: pair up each cluster and find the minimum distance.
cluster(CityClusters, FinalCityClusters) :-
CityClusters = [_,_,_|_], % ensure we have >2 clusters
setof(result(D, cluster(N1,N2), CC2),
CC1^(select(N1, CityClusters, CC1),
select(N2, CC1, CC2),
cluster_distance(N1, N2, D)),
[result(_, NewCluster, Remainder)|_]),
cluster([NewCluster|Remainder], FinalCityClusters).
Prolog's built-in sorting is to sort a structure on the first argument. We cheat again here by creating a new structure, result/3, which will contain the distance, the cluster with that distance, and the remaining items to be considered. select/3 is extremely handy. It works by pulling an item out of the list and then giving you back the list without that item. We use it twice here to select two items from the list (I don't have to worry about comparing a place to itself as a result!). CC1 is marked as a free variable. The result structures will be created for considering each possible cluster with the items we were given. Again, setof/3 will sort the list to make it unique, so the first item in the list will happen to be the one with the shortest distance. It's a lot of work for one setof/3 call, but I like to cheat!
The last line says, take the new cluster and append it to the remaining items, and forward it on recursively to ourself. The result of that invocation will eventually be the base case.
Now does it work? Let's make a quick-n-dirty main procedure to test it:
main :-
setof(leaf(X), (all_cities(Cities), member(X, Cities)), Basis),
cluster(Basis, Result),
write(Result), nl.
Line one is a cheesy way to construct the initial conditions (all cities in their own cluster of one). Line two calls our predicate to cluster things. Then we write it out. What do we get? (Output manually indented for readability.)
cluster(
cluster(
leaf(FI),
cluster(
leaf(BA),
cluster(
leaf(NA),
leaf(RM)))),
cluster(
leaf(MI),
leaf(TO)))
The order is slightly different, but the result is the same!
If you're perplexed by my use of setof/3 (I would be!) then consider rewriting those predicates using the aggregate library or with simple recursive procedures that aggregate and find the minimum by hand.
(Pardon if my terminology is wrong... I'm new to Prolog.)
Suppose you have a series of symbols appearing in some unknown number of predicates.
f1(a, b, c, d).
f2(b, b, c).
...
fn(b, d, e).
Later--at runtime--you realize that terms a and b are the same, and you wish to merge them or replace one of them with the other. In other words, I would like to either:
Make a = b
Replace all instances of a with b
Replace a and b with a new symbol (made through gensym/2)
...or anything else that accomplishes this
... where I do not know which predicates use these terms.
Atoms that start with upper case letters are variables. The first step then is to use A and B. If at some point you decide two variables are actually equal, you just say it A = B. The process of stating logically that one thing = another is "unification".
e.g.
veryDifferentOrTheSame(A,B) :- veryDifferent(A,B).
veryDifferentOrTheSame(A,B) :- A = B.
Of course, unification won't always work. a(X) = b(X) will fail.
This all implies that when the code was written, you knew that you weren't sure A=B.
You can also dynamically assert clauses at runtime. Declaring a clause as dynamic and using assera or assertz.
But if you state:
iOwn(goldfish).
iOwnFish :- iOwn(fish).
and then want to make that work by saying "in my universe fish = goldfish", then you're in strange territory.