Shortest way to define multiple rules in prolog - prolog

I'm trying to solve an exercise in order to become more familiar with prolog.
The task is following:
% Sten wants to send Lisa 100 flowers. He can choose from lilies, roses and tulips.
% One lily costs $50, rose $10 and tulip $1. Find how many flowers of each type he
% must buy, so that he spends exactly $500.
I have solved that exercise, but in somewhat bulky way I guess. My code is:
% numbers 1..100
digit(1). digit(2). digit(3). digit(4). digit(5). digit(6). digit(7). digit(8).
digit(9). digit(10). digit(11). digit(12). digit(13). digit(14). digit(15). digit(16).
digit(17). digit(18). digit(19). digit(20). digit(21). digit(22). digit(23). digit(24).
digit(25). digit(26). digit(27). digit(28). digit(29). digit(30). digit(31). digit(32).
digit(33). digit(34). digit(35). digit(36). digit(37). digit(38). digit(39). digit(40).
digit(41). digit(42). digit(43). digit(44). digit(45). digit(46). digit(47). digit(48).
digit(49). digit(50). digit(51). digit(52). digit(53). digit(54). digit(55). digit(56).
digit(57). digit(58). digit(59). digit(60). digit(61). digit(62). digit(63). digit(64).
digit(65). digit(66). digit(67). digit(68). digit(69). digit(70). digit(71). digit(72).
digit(73). digit(74). digit(75). digit(76). digit(77). digit(78). digit(79). digit(80).
digit(81). digit(82). digit(83). digit(84). digit(85). digit(86). digit(87). digit(88).
digit(89). digit(90). digit(91). digit(92). digit(93). digit(94). digit(95). digit(96).
digit(97). digit(98). digit(99). digit(100).
quantity(A1,A2,A3):-
var(A1), var(A2), var(A3),
digit(A1), digit(A2), digit(A3),
X is A1+A2+A3, X is 100,
Y is (A1*50)+(A2*10)+(A3*1), Y is 500.
Can somebody suggest a better method for initializing those rules? For example in Haskell I could do something like this:
let numbers = [1..100]
Thanks in advance.

Using SWI-Prolog:
:- use_module(library(clpfd)).
flowers(L, R, T) :-
[L,R,T] ins 0..sup,
L+R+T #= 100,
L*50 + R*10 + T*1 #= 500.
Example query:
?- flowers(Lilies, Roses, Tulips), label([Lilies,Roses,Tulips]).
Lilies = 1,
Roses = 39,
Tulips = 60 ;
false.

Some versions of Prolog have the between/3 predicate. You could say
digit(X):-between(1,100,X).
If between is not available, you could say
digit(X):-member(X,[1,2,3,4,5 and so on]).
If you do not want to use member/2, use recursion.
Edit: you can also implement between/3 like this:
my_between(X,Y,Z):-X<Y,(Z=X;X2 is X+1,my_between(X2,Y,Z)).
A robust and efficient implementation of between/3 may be more complicated, but for your purposes, this should be enough.

quantity(lilies(L),roses(R),tulips(T)) :-
between(0,100,L),
between(0,100,R),
between(0,100,T),
L + R + T =:= 100,
L*50 + R*10 + T =:= 500 .

Related

Why doesn't this clpfd query terminate until I add a redundant constraint?

I've written some predicates which take the length of a list and attaches some constraints to it (is this the right vocabulary to be using?):
clp_length([], 0).
clp_length([_Head|Rest], Length) :-
Length #>= 0, Length #= Length1 + 1,
clp_length(Rest, Length1).
clp_length2([], 0).
clp_length2([_Head|Rest], Length) :-
Length #= Length1 + 1,
clp_length2(Rest, Length1).
The first terminates on this simple query, but the second doesn't:
?- Small in 1..2, clp_length(Little, Small).
Small = 1,
Little = [_1348] ;
Small = 2,
Little = [_1348, _2174] ;
false.
?- Small in 1..2, clp_length2(Little, Small).
Small = 1,
Little = [_1346] ;
Small = 2,
Little = [_1346, _2046] ;
% OOPS %
This is strange to me, because Length is pretty clearly greater than 0. To figure that out you could either search, find the zero, and deduce that adding from zero can only increase the number, or you could propagate the in 1..2 constraint down. It feels like the extra clause is redundant! That it isn't means my mental model of clpfd is pretty wrong.
So I think I have two questions (would appreciate answers to the second as comments)
Specifically, why does this additional constraint cause the query to work correctly?
Generally, is there a resource I can use to learn about how clpfd is implemented, instead of just seeing some examples of how it can be used? I'd prefer not to have to read Markus Triska's thesis but that's the only source I can find. Is that my only option if I want to be able to answer questions like this one?
1mo, there is the issue with naming. Please refer to previous answers by
mat
and me recommending relational names. You won't go far using inappropriate names. So list_length/2 or list_fdlength/2 would be an appropriate name. Thus we have list_fdlength/2 and list_fdlength2/2.
2do, consider the rule of list_fdlength2/2. Nothing suggests that 0 is of relevance to you. So that rule will be exactly the same if you are using 0 or 1 or -1 or whatever as base case. So how should this poor rule ever realize that 0 is the end to you? Better, consider a generalization:
list_fdlength2(fake(N), N) :- % Extension to permit fake lists
N #< 0.
list_fdlength2([], 0).
list_fdlength2([_Head|Rest], Length) :-
Length #= Length1 + 1,
list_fdlength2(Rest, Length1).
This generalization shows all real answers plus fake answers. Note that I have not changed the rule, I added this alternative fact only. Thus the fake solutions are actually caused by the rule:
?- list_fdlength2(L, 1).
L = [_A]
; L = [_A, _B|fake(-1)]
; L = [_A, _B, _C|fake(-2)]
; ... .
?- list_fdlength2(L, 0).
L = []
; L = [_A|fake(-1)]
; L = [_A, _B|fake(-2)]
; ... .
Each clause tries to contribute to the solutions just in the scope of the clause. But there is no way to derive (by the built-in Prolog execution mechanism) that some rules are no longer of relevance. You have to state that explicitly with redundant constraints as you did.
Now, back to your original solution containing the redundant constraint Length #>= 0. There should not be any such fake solution at all.
list_fdlength(fake(N), N) :-
N #< 0.
list_fdlength([], 0).
list_fdlength([_Head|Rest], Length) :-
Length #>= 0,
Length #= Length1 + 1,
list_fdlength(Rest, Length1).
?- list_fdlength(L, 1).
L = [_A]
; L = [_A, _B|fake(-1)] % totally unexpected
; false.
?- list_fdlength(L, 0).
L = []
; L = [_A|fake(-1)] % eek
; false.
There are fake answers, too! How ugly! At least, they are finite in number. But, you could have done it better by using
Length #>= 1 in place of Length #>=0. With this little change, there are no longer any fake solutions when N is non-negative and thus also your original program will be better.

sudoku solver in prolog without clpfd

I have an AI project in which I should make a sudoku solver in Prolog but without using the clpfd package. How should I write the code and is there any way to print whenever a variable gets a value?
Without clpfd, you have to write a classic generate-and-test search. The nice thing about clpfd is that the constraints limit the search space. You can emulate this without using clpfd though. Let's simplify the task a bit so I can illustrate. How can you find X and Y values that solve both of these equations: X + Y = 10, 2*X + Y - 1 = 15?
First, encode your two equations:
eq1(X,Y) :- 10 is X + Y.
eq2(X,Y) :- 15 is 2*X + Y - 1.
Now create your search space in a solver predicate:
solution(X, Y) :-
between(1, 10, X), between(1, 10, Y),
eq1(X,Y),
eq2(X,Y).
Now you can run it and see:
?- solution(X,Y).
X = 6,
Y = 4 ;
false.
This is less efficient than it would be with clpfd because clpfd would notice things about the equations that would help it constrain the search space:
?- [library(clpfd)].
true.
?- X + Y #= 10, 2*X + Y - 1 #= 15.
2*X+Y#=16,
X+Y#=10.
?- X + Y #= 10, 2*X + Y - 1 #= 15, X in 1..10.
X = 6,
Y = 4.
See, it has already figured out that there is only one solution and I didn't have to constrain Y at all. That's very impressive! But you can still use Prolog without clpfd, it's just worse. :) In this example, we probably tried all 10x10=100 possible combinations to find this one solution. Less efficient. But not impossible.
So, what are the constraints you have to figure out for sudoku? Probably something like this:
unique(Row) :- sort(Row, Sorted), length(Row, Length), length(Sorted, Length).
all_digits(Row) :- forall(between(1,9,X), memberchk(X, Row)).
And so forth.
Edit: Combinatorial complexity estimation
Suppose we do a totally unprincipled search: that is, we even try obviously wrong cases like a grid of all 9s. We have 9x9=81 cells and 9 possible values (1-9). This yields 9^81 = a very large number, unlikely to be checked in your lifetime. And most of those grids are going to be fruitless permutations.
Suppose you constrain your search so that each row is a permutation of 1-9. There are 9! permutations of 1-9; with nine of those, you can multiply that outcome by nine, so there should be 9!^9. This is still awful! clpfd is probably able to further winnow this down by combining the 3x3 grid constraints; I'm not sure how I would go about doing that manually, other than in a semi-procedural way, choosing a 3x3 grid permutation and then passing each 3x1 row of that on to the next 3x3 grid selection.
It's worth noting that most of the optimization in classic Prolog programs comes down to either making the generate step generate better-qualified candidates or making the test step less expensive. A more obvious implementation can still be useful for checking a more complex implementation.
Thanks to #mat for checking my math and recommending this excellent article on the combinatorial problem of Sudoku.

Combining two numbers in prolog

Kindly, could you help me in the following:
I am writing a Prolog program that takes two numbers digits then combine them as one number, for example:
Num1: 5
Num2: 1
Then the new number is 51.
Assume V1 is the first number digit and V2 is the second number digit. I want to combine V1 and V2 then multiply the new number with V3, so my question is how I can do it?
calculateR(R, E, V1, V2, V3, V4):-
R is V1 V2 * V3,
E is R * V4.
Your help is appreciated.
Here is another solution that is based on the idea of #aBathologist and that relies on ISO predicates only, and does not dependent on SWI's idiosyncratic modifications and extensions. Nor does it have most probably unwanted solutions like calculateR('0x1',1,1,17). nor calculateR(1.0e+30,0,1,1.0e+300). Nor does it create unnecessary temporary atoms.
So the idea is to restrict the definition to decimal numbers:
digit_digit_number(D1, D2, N) :-
number_chars(D1, [Ch1]),
number_chars(D2, [Ch2]),
number_chars(N, [Ch1,Ch2]).
Here is a version which better clarifies the relational nature of Prolog - using library(clpfd) which is available in many Prolog systems (SICStus, SWI, B, GNU, YAP). It is essentially the same program as the one with (is)/2 except that I added further redundant constraints that permit the system to ensure termination in more general cases, too:
:- use_module(library(clpfd)).
digits_radix_number(Ds, R, N) :-
digits_radix_numberd(Ds, R, 0,N).
digits_radix_numberd([], _, N,N).
digits_radix_numberd([D|Ds], R, N0,N) :-
D #>= 0, D #< R,
R #> 0,
N0 #=< N,
N1 #= D+N0*R,
digits_radix_numberd(Ds, R, N1,N).
Here are some uses:
?- digits_radix_number([1,4,2],10,N).
N = 142.
?- digits_radix_number([1,4,2],R,142).
R = 10.
?- digits_radix_number([1,4,2],R,N).
R in 5..sup, 4+R#=_A, _A*R#=_B, _A in 9..sup, N#>=_A,
N in 47..sup, 2+_B#=N, _B in 45..sup.
That last query asks for all possible radices that represent [1,4,2] as a number. As you can see, not anything can be represented that way. The radix has to be 5 or larger which is not surprising given the digit 4, and the number itself has to be at least 47.
Let's say we want to get a value between 1450..1500, what radix do we need to do that?
?- digits_radix_number([1,4,2],R,N), N in 1450..1500.
R in 33..40, 4+R#=_A, _A*R#=_B, _A in 37..44,
N in 1450..1500, 2+_B#=N, _B in 1448..1498.
Gnah, again gibberish. This answer contains many extra equations that have to hold. Prolog essentially says: Oh yes, there is a solution, provided all this fine print is true. Do the math yourself!
But let's face it: It is better if Prolog gives such hard-to-swallow answer than if it would say Yes.
Fortunately there are ways to remove such extra conditions. One of the simplest is called "labeling", where Prolog will "try out" value after value:
?- digits_radix_number([1,4,2],R,N), N in 1450..1500, labeling([],[N]).
false.
That is clear response now! There is no solution. All these extra conditions where essentially false, like all that fine print in your insurance policy...
Here's another question: Given the radix and the value, what are the required digits?
?- digits_radix_number(D,10,142).
D = [1,4,2]
; D = [0,1,4,2]
; D = [0,0,1,4,2]
; D = [0,0,0,1,4,2]
; D = [0,0,0,0,1,4,2]
; ... .
So that query can never terminate, because 00142 is the same number as 142. Just as 007 is agent number 7.
Here is a straight-forward solution that should work in any Prolog close to ISO:
digits_radix_to_number(Ds, R, N) :-
digits_radix_to_number(Ds, R, 0,N).
digits_radix_to_number([], _, N,N).
digits_radix_to_number([D|Ds], R, N0,N) :-
N1 is D+N0*R,
digits_radix_to_number(Ds, R, N1,N).
?- digits_radix_to_number([1,4,2],10,R).
R = 142.
Edit: In a comment, #false pointed out that this answer is SWI-Prolog specific.
You can achieve your desired goal by treating the numerals as atoms and concatenating them, and then converting the resultant atom into a number.
I'll use atom_concat/3 to combine the two numerals. In this predicate, the third argument with be the combination of atoms in its first and second arguments. E.g.,
?- atom_concat(blingo, dingo, X).
X = blingodingo.
Note that, when you do this with two numerals, the result is an atom not a number. This is indicated by the single quotes enclosing the the result:
?- atom_concat(5, 1, X).
X = '51'.
But 51 \= '51' and we cannot multiply an atom by number. We can use atom_number/2 to convert this atom into a number:
?- atom_number('51', X).
X = 51.
That's all there is to it! Your predicate might look like this:
calculateR(No1, No2, Multiplier, Result) :-
atom_concat(No1, No2, NewNoAtom),
atom_number(NewNoAtom, NewNo),
Result is NewNo * Multiplier.
Usage example:
?- calculateR(5, 1, 3, X).
X = 153.
Of course, you'll need more if you want to prompt the user for input.
I expect #Wouter Beek's answer is more efficient, since it doesn't rely on converting the numbers to and from atoms, but just uses the assumption that each numeral is a single digit to determine the resulting number based on their position. E.g., if 5 is in the 10s place and 1 is in the 1s place, then the combination of 5 and 1 will be 5 * 10 + 1 * 1. The answer I suggest here will work with multiple digit numerals, e.g., in calculateR(12, 345, 3, Result), Result is 1234 * 3. Depending on what you're after this may or may not be a desired result.
If you know the radix of the numbers involved (and the radix is the same for all the numbers involved), then you can use the reverse index of the individual numbers in order to calculate their positional summation.
:- use_module(library(aggregate)).
:- use_module(library(lists)).
digits_to_number(Numbers1, Radix, PositionalSummation):-
reverse(Numbers1, Numbers2),
aggregate_all(
sum(PartOfNumber),
(
nth0(Position, Numbers2, Number),
PartOfNumber is Number * Radix ^ Position
),
PositionalSummation
).
Examples of use:
?- digits_to_number([5,1], 10, N).
N = 51.
?- digits_to_number([5,1], 16, N).
N = 81.
(The code sample is mainly intended to bring the idea across. Notice that I use aggregate_all/3 from SWI-Prolog here. The same could be achieved by using ISO predicates exclusively.)

how to solve when the number of variables in the goal/query varies - Prolog Constrain Solver

Here's a snippet on the classic SENDMORY crypt-arithmetic problem solutiong using prolog constraint solving mechanism-
:- lib(ic).
sendmore(Digits) :-
Digits = [S,E,N,D,M,O,R,Y],
Digits :: [0..9],
alldifferent(Digits),
S #\= 0,
M #\= 0,
1000*S + 100*E + 10*N + D
+ 1000*M + 100*O + 10*R + E
#= 10000*M + 1000*O + 100*N + 10*E + Y,
labeling(Digits).
Now, to execute this, I would send a goal/query like this:
?- sendmore(Digits).
And that would return me the possible solutions for the digits.
Now, my question is, I do not want to sort of "hard-code" the variables (like S,E,N,...) this way, but the goal/query would give the number of variables. For example, if the query I pass is something like:
?- sendmore(S,E,N,D,M).
then, it should compute only the values of SENDM and assume that the other variables are not applicable, and hence assign 0 to those variables and then proceed with the computation. And the next time I query, I may pass a different number of variables in the query.. like example:
?- sendmore(S,N,D,M,O,Y).
and the program should compute likewise.
What I am trying to achieve is a more generalised problem solver for the above scenario. Any directions on this is really appreciated. I am quite new to prolog,and am using ECLIPSE constraint solver.
Thank You.
Here are 2 ideas:
You can define sendmore() with different numbers of parameters, which would call the "real" version with the missing ones filled in. But you couldn't have different versions with the same NUMBER of parameters but DIFFERENT ones (since Prolog matches args to parameters by position).
You could expand/complicate your list format to allow the specification of which parameters you are passing; something line [(s,S),(e,E),(n,N),(d,D),(m,M)] for your middle example. A little tedious, but gives you the flexibility you seem to want.
Normally, variables in a goal and variables in a clause head are matched by their positions, not their names. So a call ?- sendmore0([S,E,N,D,M]). should be implemented as:
sendmore0([S,E,N,D,M]) :- sendmore([S,E,N,D,M,_,_,_]).
However, this would mean that you would need to implement this for every possible combination.
If you really want to implement what you describe, then you need to give the variable stable names. In ECLiPSe, you can do this with the library var_name. It's quite a hack, though...
:- lib(var_name).
sendmore0(L) :-
build_arg(["S","E","N","D","M',"O","R","Y"], L, A),
sendmore(A).
build_arg([], _, []) :- !.
build_arg([H|T], L, [HA|HT]) :-
match_arg(L, H, HA),
build_arg(T, L, HT).
match_arg([], _, _). % or use 0 as last argument if you want
match_arg([H|T], Base, A) :-
(
get_var_name(H, S),
split_string(S,"#","",[Base,_])
->
A = H
;
match_arg(T, Base, A)
).
Then you can call sendmore0/1 with a shorter list of variables. Don't forget to set the variable names!
?- set_var_name(S, "S"), set_var_name(E, "E"), sendmore0([S, E]).
S = 9
E = 5
Yes (0.00s cpu, solution 1, maybe more)
Disclaimer: this is not what stable names are for. They are meant for debugging purposes. If Joachim ever sees this, he'll give me a sharp clip round the ears...

Cryptogram solver in Prolog

I have to solve a cryptogram which looks like this:
ABC / DEF = 0.GHGHGH...
where length of variables may be different. Also repetition may be different (like 0.XYZXYZ...). I've written a piece of code which I thought will work but it doesn't:
cryp(A,B,C) :-
mn(A, X),
mn(B, Y),
mn(C, Z),
Z = X/Y.
mn([], 0).
mn([H|T], W) :- D is 10, mn(T, W1), length(T, D), P is 10^D, W is W1 + H*P.
I execute it as crypt([A,B,C], [D,E,F], [G,H]). I thought it will at least solve ABC / DEF = GH just to have any part of working solution but it doesn't work.
I don't have any clue how to do it even for one example input. I don't know how to represent 0.GHGHGH....
EDIT:
mn/2 is for converting list of digits to number ([1,2,3] -> 123).
Here is clp(fd)-way
?- use_module(library(clpfd)).
true.
?- Vars=[A,B,C,D,E,F,G,H], Vars ins 0..9, A#\=0, D#\=0, ((10*A + B)*10+C)*99 #= ((10*D+E)*10+F)*(10*G+H),all_distinct(Vars),forall(label(Vars),format('~w~n',[Vars])).
[1,0,8,2,9,7,3,6]
[1,0,8,3,9,6,2,7]
[1,3,0,2,8,6,4,5]
[1,3,0,4,9,5,2,6]
[1,3,8,2,9,7,4,6]
[1,3,8,5,0,6,2,7]
[1,6,0,4,9,5,3,2]
[1,8,0,3,9,6,4,5]
[1,8,0,4,9,5,3,6]
[2,0,4,3,9,6,5,1]
[2,5,9,4,0,7,6,3]
[2,8,4,3,9,6,7,1]
[2,8,7,4,5,1,6,3]
[2,8,7,6,9,3,4,1]
[2,9,0,6,3,8,4,5]
[3,1,0,4,9,5,6,2]
[3,1,0,6,8,2,4,5]
[3,6,0,4,9,5,7,2]
[3,6,0,7,9,2,4,5]
[3,8,0,4,9,5,7,6]
[4,0,8,5,6,1,7,2]
[4,0,8,7,9,2,5,1]
[4,9,3,5,6,1,8,7]
[5,0,4,6,9,3,7,2]
[5,0,4,7,9,2,6,3]
[5,1,8,6,9,3,7,4]
[5,7,4,6,9,3,8,2]
[5,7,4,9,0,2,6,3]
[5,9,4,7,2,6,8,1]
[6,8,0,9,3,5,7,2]
[7,5,6,9,2,4,8,1]
You might do the following identifications: let a=0.GHGHGHGH.... Then
a = 0.GHGH....
GH + a = GH.GHGHGHGH.... = 100 a
GH = 99 a
=> a = GH/99
So you can replace it by GH/99 in your formulas.
Similarly 0.XYZXYZXYZ = XYZ/999.

Resources