constraint programming, repetition of number in a list, prolog - prolog

How can I limit the repetition of a number in a list?
What is a suitable constraint in the following code example?
limit(X) :-
length(X,10),
domain(X,1,4),
% WANTED CONSTRAINT: maximum repetition of each number is 5 times.
labeling([],X).
Some sample queries and expected answers:
?- limit([1,1,1,1,1,1,1,1,1]).
false.
?- limit([1,1,1,1,1,2,2,2,2,2]).
true.

This works, L is the list of the number of repetitions of each number from 1 to 4.
:- use_module(library(clpfd)).
limit(X) :-
length(L, 4),
L ins 0..5,
sum(L, #=, 10),
label(L),
maplist(make_list, [1,2,3,4], L, LX),
flatten([LX],X).
make_list(Val, Nb, L) :-
length(L, Nb),
L ins Val .. Val.
The problem is that the numbers are group by values.
The code may be generalized to
limit(X, Min, Max, Len, Rep) :-
Nb is Max -Min + 1,
length(L, Nb),
L ins 0..Rep,
sum(L, #=, Len),
label(L),
numlist(Min, Max, Lst),
maplist(make_list, Lst, L, LX),
flatten([LX],X).
You try : limit(X, 1, 4, 10, 5).

In this answer we use two different clpfd "flavors": sicstus-prolog and gnu-prolog.
:- use_module(library(clpfd)).
limited_repetitions__SICStus(Zs) :-
length(Zs, 10),
domain(Zs, 1, 4),
domain([C1,C2,C3,C4], 0, 5),
global_cardinality(Zs, [1-C1,2-C2,3-C3,4-C4]),
labeling([], Zs).
limited_repetitions__gprolog(Zs) :-
length(Zs, 10),
fd_domain(Zs, 1, 4),
maplist(fd_atmost(5,Zs), [1,2,3,4]),
fd_labeling(Zs).
Simple sample query run with SICStus Prolog version 4.3.2 and GNU Prolog 1.4.4:
?- limited_repetitions__SICStus(Zs). % ?- limited_repetitions__gprolog(Zs).
Zs = [1,1,1,1,1,2,2,2,2,2] % Zs = [1,1,1,1,1,2,2,2,2,2]
; Zs = [1,1,1,1,1,2,2,2,2,3] % ; Zs = [1,1,1,1,1,2,2,2,2,3]
; Zs = [1,1,1,1,1,2,2,2,2,4] % ; Zs = [1,1,1,1,1,2,2,2,2,4]
; Zs = [1,1,1,1,1,2,2,2,3,2] % ; Zs = [1,1,1,1,1,2,2,2,3,2]
; Zs = [1,1,1,1,1,2,2,2,3,3] % ; Zs = [1,1,1,1,1,2,2,2,3,3]
; Zs = [1,1,1,1,1,2,2,2,3,4] % ; Zs = [1,1,1,1,1,2,2,2,3,4]
; Zs = [1,1,1,1,1,2,2,2,4,2] % ; Zs = [1,1,1,1,1,2,2,2,4,2]
... % ...
Let's measure the time required for counting the number of solutions!
call_succeeds_n_times(G_0, N) :-
findall(t, call(G_0), Ts),
length(Ts, N).
?- call_time(call_succeeds_n_times(limited_repetitions__SICStus(_), N), T_ms).
N = 965832, T_ms = 6550. % w/SICStus Prolog 4.3.2
?- call_time(call_succeeds_n_times(limited_repetitions__gprolog(_), N), T_ms).
N = 965832, T_ms = 276. % w/GNU Prolog 1.4.4

In this previous answer we utilized the SICStus Prolog clpfd predicate global_cardinality/2. As an non-constraint alternative, we could also use selectd/3 like this:
multi_selectd_rest([],Ds,Ds).
multi_selectd_rest([Z|Zs],Ds0,Ds) :-
selectd(Z,Ds0,Ds1),
multi_selectd_rest(Zs,Ds1,Ds).
Putting it to good use in limited_repetitions__selectd/3 we define:
limited_repetitions__selectd(Zs) :-
length(Zs, 10),
multi_selectd_rest(Zs,[1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4],_).
Again, let's measure the time required for counting the number of solutions!
?- call_time(call_succeeds_n_times(limited_repetitions__selectd(_),N), T_ms).
N = 965832, T_ms = 4600.

Here is a way, but not for sequences:
:- [library(clpfd)].
limit_repetition(Xs, Max) :-
maplist(vs_n_num(Xs, Max), Xs).
vs_n_num(Vs, Max, X) :-
maplist(eq_b(X), Vs, Bs),
% sum(Bs, #=, EqC),
% EqC #=< Max.
sum(Bs, #=<, Max).
eq_b(X, Y, B) :- X #= Y #<==> B.
vs_n_num/3 is an adapted version of what you can find in docs.
Here's a way to delimite sequences:
limit_repetition([X|Xs], Max) :-
limit_repetition(X, 1, Xs, Max).
limit_repetition(X, C, [Y|Xs], Max) :-
X #= Y #<==> B,
( B #/\ C + B #=< Max #/\ D #= C + B ) #\/ ( (#\ B) #/\ D #= 1 ),
limit_repetition(Y, D, Xs, Max).
limit_repetition(_X, _C, [], _Max).
yields
?- length(X,4), X ins 1..4, limit_repetition(X, 1) ,label(X).
X = [1, 2, 1, 2] ;
X = [1, 2, 1, 3] ;
...
Seems the former version is more related to your sample.

Related

How to use an fd solver to determine which elements of a list can sum to a given number?

Given a list of possible summands I want to determine which, if any, can form a given sum. For example, with [1,2,3,4,5] I can make the sum of 9 with [4,5], [5,3,1], and [4,3,2].
I am using GNU Prolog and have something like the following which does not work
numbers([1,2,3,4,5]).
all_unique(_, []).
all_unique(L, [V|T]) :-
fd_exactly(1, L, V),
all_unique(L, T).
fd_sum([], Sum).
fd_sum([H|T], Sum):-
S = Sum + H,
fd_sum(T, S).
sum_clp(N, Summands):-
numbers(Numbers),
length(Numbers, F),
between(1, F, X),
length(S, X),
fd_domain(S, Numbers),
fd_domain(Y, [N]),
all_unique(S, Numbers),
fd_sum(S, Sum),
Sum #= Y,
fd_labeling(S).
I think the main problem is that I am not representing the constraint on the sum properly? Or maybe it is something else?
Just in case you're really interested in CLP(FD), here is your corrected program.
numbers([1,2,3,4,5]).
% note: use builtins where available, both for efficiency and correctness
%all_unique(_, []).
%all_unique(L, [V|T]) :-
% fd_exactly(1, L, V),
% all_unique(L, T).
fd_sum([], 0). % sum_fd_SO.pl:8: warning: singleton variables [Sum] for fd_sum/2
fd_sum([H|T], Sum):-
% note: use CLP(FD) operators and the correct operands
Sum #= S + H,
fd_sum(T, S).
sum_clp(N, S):- % sum_fd_SO.pl:13-23: warning: singleton variables [Summands] for sum_clp/2
numbers(Numbers),
length(Numbers, F),
between(1, F, X),
length(S, X),
fd_domain(S, Numbers),
%fd_domain(Y, [N]),
%all_unique(S, Numbers),
fd_all_different(S),
fd_sum(S, N),
%Sum #= Y,
fd_labeling(S).
test
?- sum_clp(3,L).
L = [3] ? ;
L = [1,2] ? ;
L = [2,1] ? ;
no
I think mixing the code for sublist into clp code is causing some confusion. GNU-Prolog has a sublist/2 predicate, you can use that.
You seem to be building the arithmetic expression with fd_sum but it is incorrectly implemented.
sum_exp([], 0).
sum_exp([X|Xs], X+Xse) :-
sum_exp(Xs, Xse).
sum_c(X, N, Xsub) :-
sublist(Xsub, X),
sum_exp(Xsub, Xe),
N #= Xe.
| ?- sum_exp([A, B, C, D], X).
X = A+(B+(C+(D+0)))
yes
| ?- sum_c([1, 2, 3, 4, 5], 9, X).
X = [4,5] ? ;
X = [2,3,4] ? ;
X = [1,3,5] ? ;
(1 ms) no
| ?- length(X, 4), sum_c(X, 4, [A, B]), member(A, [1, 2, 3]).
A = 1
B = 3
X = [_,_,1,3] ? ;
A = 2
B = 2
X = [_,_,2,2] ? ;
A = 3
B = 1
X = [_,_,3,1] ?
yes

Sum elements of list in prolog

I want to sum element of a list like this:
sum([1,[2,3],4],S).
I used that but I have a problem:
sum([],0).
sum([T|R],M) :- sum(R,S), M is T+S.
I get the following error:
ERROR: is/2: Type error: `[]' expected, found `[2,3]' (a list) ("x" must hold one character)
The problem is that you can't add T if T is a list. You could easily solve using is_list/1 that succeds if T is a list:
sum([],0).
sum([T|R],M) :- (is_list(T) -> sum(T,N1),sum(R,S), M is N1+S
; sum(R,S), M is T+S ).
Examples:
?- sum([1,[2,3],4],S).
S = 10.
?- sum([1,2,3,4],S).
S = 10.
?- sum([1,[2],[3],4],S).
S = 10.
?- sum([1,[[2],[3]],4],S).
S = 10.
A better approach would be using CLPFD:
:- use_module(library(clpfd)).
sum([],0).
sum([[]],0).
sum([T|R],M) :- (is_list(T) -> sum(T,N1),sum(R,S), M #= N1+S
; sum(R,S), M #= T+S ).
Now you can query more general questions like:
?- sum(L,N).
L = [],
N = 0 ;
L = [[]],
N = 0 ;
L = [N],
N in inf..sup ;
L = [N, []],
N in inf..sup ;
L = [_1836, _1842],
_1836+_1842#=N ;
L = [_1842, _1848, []],
_1842+_1848#=N ;
L = [_2142, _2148, _2154],
_2142+_2192#=N,
_2148+_2154#=_2192 ;
L = [_2148, _2154, _2160, []],
_2148+_2204#=N,
_2154+_2160#=_2204 ;
L = [_2448, _2454, _2460, _2466],
_2448+_2504#=N,
_2454+_2528#=_2504,
_2460+_2466#=_2528 ;
and goes on...
Another approach:
% Old code, with vars renamed:
%sum([], 0).
%sum([Num|Tail], TotalSum) :- sum(Tail, TailSum), TotalSum is Num + TailSum.
% New code:
sum([], 0).
sum([Elem|Tail], TotalSum) :-
sum(Elem, ElemSum),
sum(Tail, TailSum),
TotalSum is ElemSum + TailSum.
sum(Num, Num).
Demo: http://swish.swi-prolog.org/p/cmzcsXrJ.pl.
You can use flatten/2 plus foldl/4 or sum_list/2 library predicates in Swi-Prolog:
% flatten/2 + foldl/4 based implementation
sum_nested_list(List, Sum) :-
flatten(List, Flat),
foldl(plus, Flat, 0, Sum).
% flatten/2 + sum_list/2 based implementation
sum_nested_list(List, Sum) :-
flatten(List, Flat),
sum_list(Flat, Sum).

Prolog how to find sum of elements matrix

I have matrix size [n,n].
I need to find sum
For example
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
sum = 3+4+7+8
I need to find sum of elements of first quadrant matrix
Using library(clpfd), which provides the useful sum/3 and transpose/2:
:- use_module(library(clpfd)).
sum_first_quadrant(M, S) :-
first_quadrant(M, Q),
maplist(sum_, Q, Ss),
sum_(Ss, S).
sum_(L, S) :-
sum(L, #=, S).
first_quadrant(M, Q) :-
transpose(M, T),
reverse(T, RT),
dichotomize(RT, RD),
reverse(RD, D),
transpose(D, TD),
dichotomize(TD, Q).
dichotomize(M, D) :-
length(M, L),
X #= L//2,
dichotomize_(M, X, D).
dichotomize_(_, 0, []).
dichotomize_([H|T], X, [H|T2]) :-
X #> 0,
Y #= X - 1,
dichotomize_(T, Y, T2).
Example:
?- sum_first_quadrant([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], Z).
Z = 22 ;
false.
Note
You can get rid of the extraneous choice point in dichotomize_ using if_/3 and (=)/3 from library reif:
dichotomize_(L, X, D) :-
X #>= 0,
if_(X = 0,
D = [],
( Y #= X - 1,
L = [H|T],
D = [H|T2],
dichotomize_(T, Y, T2)
)
).
%-matrix
data([[1,2,18,23],
[5,6,10,10],
[9,10,11,12],
[13,14,15,16]]).
%-Sum
main(S):-
data(Ms),
length(Ms,N),
Mdl is N//2,
sum_matr(Ms,1,N,Mdl,0,S).
%+Matrix,+RowCounter,+Length,+Midle,+Acc,-S
sum_matr([R|Rs],I,K,Mdl,Acc,S):-
I=<Mdl, I1 is I+1,
findEnd(R,Mdl,ResList),
sum_row(ResList,Mdl,I,K,0,Srow),
Acc1 is Acc + Srow,
sum_matr(Rs,I1,K,Mdl,Acc1,S).
sum_matr(_,I,_,Mdl,S,S):-
I>Mdl.
%+Row,+Counter,+I,+K,+Acc,-Sum
sum_row([X|Xs],C,I,K,Acc,S):-
C<K,
Acc1 is Acc+X,
C1 is C+1,
sum_row(Xs,C1,I,K,Acc1,S).
sum_row(_,C,_,K,S,S):-
C>=K.
%+List, +Position, -End
findEnd(E, 0, E).
findEnd([_|T], N, E):-
N>0,
N1 is N-1,
findEnd(T, N1, E).

Use reified constraints to make 3 numbers consecutive

Here's an outline of my SWI-Prolog program:
:- use_module(library(clpfd)).
consec1(L) :-
L=[L1,L2,L3,L4,L5,L6,L7,L8,L9],
L ins 1..9,
...,
abs(L5-L4)#=1,
all_different(L),
labeling([],L)
abs(L5-L4)#=1 makes L5 and L4 next to each other. If I wanted to make three numbers next to each other e.g. L3, L4 and L5, how could I use reified constraints to do this?
E.g. L3=4,L5=5,L4=6 or L4=7,L5=8,L3=9
This implements consecutive in the sense you gave in the comments. For a list of N values, we need space enough to make all the values fit in between, and all values need to be different.
consecutive([]). % debatable case
consecutive(Xs) :-
Xs = [_|_],
length(Xs, N),
all_different(Xs),
max_of(Max, Xs),
min_of(Min, Xs),
Max-Min #= N-1.
max_of(Max, [Max]).
max_of(Max0, [E|Es]) :-
Max0 #= max(E,Max1),
max_of(Max1, Es).
min_of(Min, [Min]).
min_of(Min0, [E|Es]) :-
Min0 #= min(E, Min1),
min_of(Min1, Es).
TL;DR: too long for a comment: play-time with specialized sicstus-prolog clpfd constraints
This answer follows up this previous answer; recent versions of SICStus Prolog offer specialized clpfd constraints maximum/2 and minimum/2 as alternatives to min_of/2 and max_of/2.
Using the following code for benchmarking1,2 ...
:- use_module(library(clpfd)).
:- use_module(library(between)).
bench_(How, N, Ub) :-
\+ \+ ( length(Xs, N),
domain(Xs, 1, Ub),
all_different(Xs),
Max-Min #= N-1,
( How = 0
; How = min_of , max_of( Max, Xs), min_of( Min, Xs)
; How = minimum, maximum(Max, Xs), minimum(Min, Xs)
),
labeling([enum], Xs) ).
... we run the following tests:
To estimate worst-case overhead of min/max constraint:
?- member(How, [0,v1,v2]), call_time(bench_(How,10,10), T_ms).
How = 0 , T_ms = 5910
; How = v1, T_ms = 19560
; How = v2, T_ms = 7190.
To measure the runtime costs of propagating min/max constraints in more typical cases:
?- between(6, 8, N), NN #= N+N, call_time(bench_(v1,N,NN),T_ms).
N = 6, NN = 12, T_ms = 50
; N = 7, NN = 14, T_ms = 300
; N = 8, NN = 16, T_ms = 2790.
?- between(6, 8, N), NN #= N+N, call_time(bench_(v2,N,NN),T_ms).
N = 6, NN = 12, T_ms = 20
; N = 7, NN = 14, T_ms = 100
; N = 8, NN = 16, T_ms = 830.
In both "use cases", the specialized constraints give impressive speedup.
Footnote 1: Using SICStus Prolog version 4.3.2 (64-bit).
Footnote 2: Answer sequences were post-processed to improve appearance.

creating lists, searching sth which has been given and making combination on prolog

I want to make a Prolog program.
Predicate will be like this:
name(name, failedCourse, age)
Database of the program is:
name(george, math, 20).
name(steve, phys, 21).
name(jane, chem, 22).
I want to implement the predicate nameList(A, B). A means list of names, B means number of names on the list. For example:
nameList([george, steve],2). returns true
nameList([george, steve],X). returns X=2
nameList(X,2). returns X=[george, steve]; X=[george, jane]; X=[steve, jane]
nameList([martin],1). returns false (because martin is not included database.)
I wanted to make a list that includes all names on the database. For that reason I made a findall.
descend(X,Y,A) :- name(X,Y,A).
descend(X,Y,A) :- name(X,Z,A),descend(Z,Y,A).
findall(director(X),descend(Y,X),Z).
?- findall(B,descend(B,X,Y),A). returns A = [george, steve, jane].
But I could not figure it out to use list A in predicates :( I cannot search the list for A in the nameList.
If you help me, I will be very grateful.
The main thing you need is a predicate that calculates combinations of a given length and of a given list:
comb(0, _, []).
comb(N, [X | T], [X | Comb]) :-
N > 0,
N1 is N - 1,
comb(N1, T, Comb).
comb(N, [_ | T], Comb) :-
N > 0,
comb(N, T, Comb).
Usage:
?- comb(2, [a, b, a], Comb).
Comb = [a, b] ;
Comb = [a, a] ;
Comb = [b, a] ;
false.
(See more here.)
Now you can just apply this predicate on your data:
name(george, math, 20).
name(steve, phys, 21).
name(jane, chem, 22).
name_list(L, N) :-
findall(X, name(X, _, _), Xs),
length(Xs, Len),
between(0, Len, N),
comb(N, Xs, L).
Usage examples:
?- name_list(L, N).
L = [],
N = 0 ;
L = [george],
N = 1 ;
L = [steve],
N = 1 ;
L = [jane],
N = 1 ;
L = [george, steve],
N = 2 ;
L = [george, jane],
N = 2 ;
L = [steve, jane],
N = 2 ;
L = [george, steve, jane],
N = 3 ;
false.
?- name_list([george, steve], N).
N = 2 ;
false.
?- name_list(L, 2).
L = [george, steve] ;
L = [george, jane] ;
L = [steve, jane] ;
false.
name(george, math, 20).
name(steve, phys, 21).
name(jane, chem, 22).
name_list(Name_List,N) :-
integer(N),
findall(Name,name(Name,_,_),L),
combination(L,N,Name_List).
name_list(Name_List,N) :-
var(N),
findall(Name,name(Name,_,_),L),
length(L,Len),
for(1,N,Len),
combination(L,N,Name_List).
combination(X,1,[A]) :-
member(A,X).
combination([A|Y],N,[A|X]) :-
N > 1,
M is N - 1,
combination(Y,M,X).
combination([_|Y],N,A) :-
N > 1,
combination(Y,N,A).

Resources