Is there a way of obtaining itemsets from transactions efficiently witho prolog? - prolog

I am sorry to ask this question, but it has been a lot since I programmed in Prolog. I think I am used to imperative paradigm. :-(
I am trying to obtain itemsets from a Prolog relation:
% transaction(Time, Client, Item)
transaction(1, 2, 10).
transaction(1, 2, 20).
transaction(2, 5, 90).
transaction(3, 2, 30).
transaction(4, 2, 40).
transaction(4, 2, 60).
transaction(4, 2, 70).
transaction(5, 4, 30).
transaction(5, 3, 30).
transaction(5, 3, 50).
transaction(5, 3, 70).
transaction(5, 1, 30).
transaction(6, 1, 90).
transaction(6, 4, 40).
transaction(6, 4, 70).
transaction(7, 4, 90).
% Transformation of transactions to Lists of items per Time per Client.
transaction2(Time, Client, List) :-
setof(Item, Time^Client^transaction(Time, Client, Item), List).
% Itemsets.
itemsets :-
transaction(Time, Client, _),
transaction2(Time, Client, List),
assert(init(List)).
% Main:
main(Itemsets) :-
itemsets,
setof(Basket, init(Basket), Itemsets),
retractall(init(Basket)).
Then if I consult main(X) I would like to obtain:
X = [[10, 20], [30], [30, 50, 70], [40, 60, 70], [40, 70], [90]]
I just can't figure out a proper way of doing this.
If I can get a pointer or a little help I will appreciate very much.
Bests,
B.

Try
itemsets(L):-
setof(Items,
Time^Client^Item^Nil^(
transaction(Time, Client, Nil),
setof(Item, transaction(Time, Client, Item), Items)
), L).
and just call itemsets(Itemsets).

main(Items) :-
findsetof([T1,C1],transaction(T1,C1,_),L1),
findsetof(L2,(
append(_,[[T2,C2]|_],L1),
findsetof(Item,transaction(T2,C2,Item),L2)),
Items).
findsetof(A,B,L) :-
findall(A,B,C),
setof(A,member(A,C),L).

Related

how to print all nodes from a particular node

I have the following knowledge base:
connects(1,11,10,8).
connects(1,12,2,6).
connects(1,13,-3,-2).
connects(1,14,1,-5).
I have this predicates:
friends(Node, L):-
findall(X,(connects(Node,X,_,_);connects(X,Node,_,_)),L).
when i make the question ?- friends(1,L) i have this:
L = [11, 12, 13, 14].
But my goal is print a list like this:
L = [11,10,8,12,2,6,13,-3,-2,14,-5]
How can i achieve it?
It's not clear to me what relationship the predicate actually represents. Anyway, you can proceed as follows:
First, use findall to collect the data you need as a list of lists:
?- findall([X,Y,Z], connects(1,X,Y,Z), L).
L = [[11, 10, 8], [12, 2, 6], [13, -3, -2], [14, 1, -5]].
After that, you can use append to get a flatten list:
?- findall([X,Y,Z], connects(1,X,Y,Z), L0), append(L0, L1).
L0 = [[11, 10, 8], [12, 2, 6], [13, -3, -2], [14, 1, -5]],
L1 = [11, 10, 8, 12, 2, 6, 13, -3, -2, 14, 1, -5].
Putting it all together, you can define friends/2 as:
friends(Node, L1):-
findall([X,Y,Z],
( connects(Node, X,Y,Z)
; connects(X, Node, Y,Z) ), L0),
append(L0, L1).
Assuming that you are can change the predicate. Replacing the _s with variables will give you the values you need. And then use flatten/2 to turn the matrix (2D list) into a simple 1D list.
This should solve your problem.
friends(Node, L):-
findall([X, Y, Z], (connects(Node, X, Y, Z)), A),
flatten(A, L).
Example output:
?- friends(1, L)
L = [11, 10, 8, 12, 2, 6, 13, -3, -2, 14, 1, -5]

Variable bin packing problem with Prolog (CLP)

I am trying to find an algorithm for the NP-hard 2D Variable Size Bin Packing Problem (2DVSBPP) in (Swi-)Prolog using Constraint Logic Programming (CLP).
The problem could be explained like this: some ordered Products need to be packed as efficiently as possible into some Boxes (bins). The products have some given Width and Length (squares or rectangles, eg 2x3). There are four different size of boxes, each with a given cost for the shipper (eg $4 for the 5x5 box, $5 for 5x7 box). The goal is to minimize the total cost from the boxes.
I've been looking for an answer to this problem for a while now and have read numerous papers and similar examples in other languages. However, I can't find any working solution. I'm especially struggling with how to handle the unknown number of Boxes (bins).
To be able to find a solution to this problem I've tried to adapt a similar problem but really have no idea how to handle the variable amount of boxes. The following code can choose the cheapest possible box to fit all the products as long as there is only one box needed to fit them all. From the moment we need multiple boxes, the program just fails.
The boxes and products:
:- use_module(library(clpfd)).
:- use_module(library(clpr)).
:- expects_dialect(sicstus).
%% These are the possible productsizes that could need packing
% product (id, width, length)
product(1, 2, 2).
product(2, 1, 2).
product(2, 2, 1). % repeating product n2 because it can lay horizontal or vertical
product(3, 1, 3).
product(3, 3, 1). % idem
product(4, 3, 3). % is square so does not need it
product(5, 2, 3).
product(5, 3, 2). % iden
product(6, 4, 2).
product(6, 2, 4). % idem
% because it can lay virtically or horizontally in a box
product_either_way(Number, Width, Length) :-
product(Number, Width, Length).
product_either_way(Number, Width, Length) :-
product(Number, Length, Width).
%% These are the so called bins from the 2DVSBPP problem
%% There are 4 sizes, but there is an unlimited supply
% box(Width, Length, Cost)
box(4,4,4).
box(4,6,6).
box(5,5,7).
box(9,9,9).
The constraints:
area_box_pos_combined(W_total*H_total,prod(N),X+Y,f(X,Width,Y,Height)) :-
product_either_way(N, Width, Height), % Getting the width and height (length) of a product
% Constraint: the product should 'fit' inside the choosen box
% thus limiting its coordinates (XY)
X #>= 1,
X #=< W_total-Width+1,
Y #>= 1,
Y #=< H_total-Height+1.
positions_vars([],[]).
positions_vars([X+Y|XYs],[X,Y|Zs]) :-
positions_vars(XYs,Zs).
area_boxes_positions_(ProductList,Ps,Zs) :-
box(W, H, Cost), % finding a suitable box with a W & H
%% minimize(Cost),
maplist(area_box_pos_combined(W*H),ProductList,Ps,Cs), % Setting up constraints for each product
disjoint2(Cs), % making sure they dont overlap with other product inside the box
positions_vars(Ps,Zs).
A possible query which asks to pack 4 products (numbers 2, 1, 3 and 5)
area_boxes_positions_([prod(2),prod(1),prod(3),prod(5)],Positions,Zs),
labeling([ffc],Zs).
Gives the following as output, one possible way to pack the products:
Positions = [3+1, 1+1, 4+1, 1+3],
Zs = [3, 1, 1, 1, 4, 1, 1, 3] .
But how do I model multiple boxes, when we would have an order with more products that would not fit inside one box?
Any help or examples are really appreciated!
I'm especially struggling with how to handle the unknown number of Boxes (bins).
You can put an upper bound on the number of boxes: For N indivisible elements you will never need more than N boxes. Furthermore, we can define a special "unused" kind of box with 0 size but 0 cost. Then we can ask for a solution with an assignment of items to exactly N (or any other number of) boxes, some of which can remain unused.
Here is a description of a single box, relating its kind, size, and cost using disjunctive and conjunctive constraints:
kind_width_length_cost(Kind, Width, Length, Cost) :-
% unused box
(Kind #= 0 #/\ Width #= 0 #/\ Length #= 0 #/\ Cost #= 0) #\/
% small box
(Kind #= 1 #/\ Width #= 4 #/\ Length #= 4 #/\ Cost #= 4) #\/
% medium box
(Kind #= 2 #/\ Width #= 4 #/\ Length #= 6 #/\ Cost #= 6) #\/
% large box
(Kind #= 3 #/\ Width #= 5 #/\ Length #= 5 #/\ Cost #= 7) #\/
% X-large box
(Kind #= 4 #/\ Width #= 9 #/\ Length #= 9 #/\ Cost #= 9),
% make sure all variables have finite domains, the above disjunction is
% not enough for the system to infer this
Kind in 0..4,
Width in 0..9,
Length in 0..9,
Cost in 0..9.
A collection of N boxes can be represented as a term boxes(Numbers, Kinds, Widths, Lengths, Costs) where Numbers are [1, 2, ..., N] and the I-th element of each of the other lists is the length/width/cost of box number I:
n_boxes(N, boxes(Numbers, Kinds, Widths, Lengths, Costs)) :-
numlist(1, N, Numbers),
length(Kinds, N),
maplist(kind_width_length_cost, Kinds, Widths, Lengths, Costs).
For example, three boxes are:
?- n_boxes(3, Boxes).
Boxes = boxes([1, 2, 3], [_G9202, _G9205, _G9208], [_G9211, _G9214, _G9217], [_G9220, _G9223, _G9226], [_G9229, _G9232, _G9235]),
_G9202 in 0..4,
_G9202#=4#<==>_G9257,
_G9202#=3#<==>_G9269,
_G9202#=2#<==>_G9281,
_G9202#=1#<==>_G9293,
_G9202#=0#<==>_G9305,
... a lot more constraints
Note that this uses a term containing lists rather than the more "usual" representation with a list containing terms box(Num, Width, Length, Cost). The reason for this is that we will want to index into these lists of FD variables using element/3. This predicate cannot be used to index into lists of other terms.
Turning to products, here is the FD version of your disjunctive product_either_way predicate:
product_either_way_fd(Number, Width, Length) :-
product_width_length(Number, W, L),
(Width #= W #/\ Length #= L) #\/ (Width #= L #/\ Length #= W),
% make sure Width and Length have finite domains
Width #>= min(W, L),
Width #=< max(W, L),
Length #>= min(W, L),
Length #=< max(W, L).
The placement of an item is expressed with a term box_x_y_w_l containing the number of the box, the X and Y coordinates inside the box, and the item's width and length. The placement must be compatible with the dimensions of the chosen box:
product_placement(Widths, Lengths, Number, Placement) :-
product_either_way_fd(Number, W, L),
Placement = box_x_y_w_l(_Box, _X, _Y, W, L),
placement(Widths, Lengths, Placement).
placement(Widths, Lengths, box_x_y_w_l(Box, X, Y, W, L)) :-
X #>= 0,
X + W #=< Width,
Y #>= 0,
Y + L #=< Length,
element(Box, Widths, Width),
element(Box, Lengths, Length).
This is where we use the Widths and Lengths lists of FD variables. The number of the chosen box is itself an FD variable that we use as an index to look up the box's width and length using the element/3 constraint.
Now we must model non-overlapping placements. Two items placed in different boxes are automatically non-overlapping. For two items in the same box we must check their coordinates and sizes. This binary relation must be applied to all unordered pairs of items:
placement_disjoint(box_x_y_w_l(Box1, X1, Y1, W1, L1),
box_x_y_w_l(Box2, X2, Y2, W2, L2)) :-
Box1 #\= Box2 #\/
(Box1 #= Box2 #/\
(X1 #>= X2 + W2 #\/ X1 + W1 #< X2) #/\
(Y1 #>= Y2 + L2 #\/ Y1 + L1 #< Y2)).
alldisjoint([]).
alldisjoint([Placement | Placements]) :-
maplist(placement_disjoint(Placement), Placements),
alldisjoint(Placements).
Now we are ready to put everything together. Given a list of products and a number N of boxes (some of which may be unused), the following predicate computes constraints on placements in boxes, the kinds of boxes used, their costs, and a total cost:
placements_(Products, N, Placements, BoxKinds, Costs, Cost) :-
n_boxes(N, boxes(_BoxNumbers, BoxKinds, Widths, Lengths, Costs)),
maplist(product_placement(Widths, Lengths), Products, Placements),
alldisjoint(Placements),
sum(Costs, #=, Cost).
This constructs a term representing N boxes, computes placement constraints for each product, ensures the placements are disjoint, and sets up the computation of the total cost. That is all!
I'm using the following products copied from the question. Note that I have removed duplicates with swapped widths/lengths since this swapping is done by product_either_way_fd when needed.
product_width_length(1, 2, 2).
product_width_length(2, 1, 2).
product_width_length(3, 1, 3).
product_width_length(4, 3, 3).
product_width_length(5, 2, 3).
product_width_length(6, 4, 2).
We're ready for testing. To reproduce your example of placing items 2, 1, 3, and 5 in a single box:
?- placements_([2, 1, 3, 5], 1, Placements, Kinds, Costs, Cost).
Placements = [box_x_y_w_l(1, _G17524, _G17525, _G17526, _G17527), box_x_y_w_l(1, _G17533, _G17534, 2, 2), box_x_y_w_l(1, _G17542, _G17543, _G17544, _G17545), box_x_y_w_l(1, _G17551, _G17552, _G17553, _G17554)],
Kinds = [_G17562],
Costs = [Cost],
_G17524 in 0..8,
_G17524+_G17526#=_G17599,
_G17524+_G17526#=_G17611,
_G17524+_G17526#=_G17623,
...
With labeling:
?- placements_([2, 1, 3, 5], 1, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), labeling([], Variables).
Placements = [box_x_y_w_l(1, 0, 0, 1, 2), box_x_y_w_l(1, 7, 7, 2, 2), box_x_y_w_l(1, 4, 6, 3, 1), box_x_y_w_l(1, 2, 3, 2, 3)],
Kinds = [4],
Costs = [9],
Cost = 9,
Variables = [0, 0, 1, 2, 7, 7, 4, 6, 3|...] .
(You might want to check this carefully for correctness!) Everything was placed in box number 1, which is of kind 4 (size 9x9) with cost 9.
Is there a way to fit these items in a cheaper box?
?- Cost #< 9, placements_([2, 1, 3, 5], 1, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), labeling([], Variables).
false.
Now, how about putting all the products in (up to) 6 boxes?
?- placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), labeling([], Variables).
Placements = [box_x_y_w_l(1, 0, 0, 2, 2), box_x_y_w_l(1, 3, 3, 1, 2), box_x_y_w_l(1, 5, 6, 1, 3), box_x_y_w_l(2, 0, 0, 3, 3), box_x_y_w_l(2, 4, 4, 2, 3), box_x_y_w_l(3, 0, 0, 2, 4)],
Kinds = [4, 4, 1, 0, 0, 0],
Costs = [9, 9, 4, 0, 0, 0],
Cost = 22,
Variables = [1, 0, 0, 1, 3, 3, 1, 2, 1|...] .
The first solution found uses three boxes and left the other three unused. Can we go cheaper?
?- Cost #< 22, placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), labeling([], Variables).
Cost = 21,
Placements = [box_x_y_w_l(1, 0, 0, 2, 2), box_x_y_w_l(1, 3, 3, 1, 2), box_x_y_w_l(1, 5, 6, 1, 3), box_x_y_w_l(2, 0, 0, 3, 3), box_x_y_w_l(3, 0, 0, 2, 3), box_x_y_w_l(4, 0, 0, 2, 4)],
Kinds = [4, 1, 1, 1, 0, 0],
Costs = [9, 4, 4, 4, 0, 0],
Variables = [1, 0, 0, 1, 3, 3, 1, 2, 1|...] .
Yes! This solution uses more boxes, but ones that are overall slightly cheaper. Can we do even better?
?- Cost #< 21, placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), labeling([], Variables).
% ... takes far too long
We need to be a bit more sophisticated. Playing around with the number of boxes it's clear that cheaper solutions with fewer boxes are available:
?- Cost #< 21, placements_([1, 2, 3, 4, 5, 6], 2, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), labeling([], Variables).
Cost = 18,
Placements = [box_x_y_w_l(1, 0, 0, 2, 2), box_x_y_w_l(1, 3, 3, 1, 2), box_x_y_w_l(1, 5, 6, 1, 3), box_x_y_w_l(2, 0, 6, 3, 3), box_x_y_w_l(2, 6, 4, 3, 2), box_x_y_w_l(2, 4, 0, 2, 4)],
Kinds = [4, 4],
Costs = [9, 9],
Variables = [1, 0, 0, 1, 3, 3, 1, 2, 1|...] .
Maybe directing the search to label box kinds first is useful, since the up strategy will essentially try to use as few boxes as possible:
?- Cost #< 21, placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), time(( labeling([], Kinds), labeling([ff], Variables) )).
% 35,031,786 inferences, 2.585 CPU in 2.585 seconds (100% CPU, 13550491 Lips)
Cost = 15,
Placements = [box_x_y_w_l(5, 2, 4, 2, 2), box_x_y_w_l(6, 8, 7, 1, 2), box_x_y_w_l(6, 5, 6, 3, 1), box_x_y_w_l(6, 2, 3, 3, 3), box_x_y_w_l(6, 0, 0, 2, 3), box_x_y_w_l(5, 0, 0, 2, 4)],
Kinds = [0, 0, 0, 0, 2, 4],
Costs = [0, 0, 0, 0, 6, 9],
Variables = [5, 2, 4, 6, 8, 7, 1, 2, 6|...] .
This really does need ff or ffc, the default leftmost strategy doesn't return results in a reasonable time frame.
Can we do even better?
?- Cost #< 15, placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), time(( labeling([], Kinds), labeling([ff], Variables) )).
% 946,355,675 inferences, 69.984 CPU in 69.981 seconds (100% CPU, 13522408 Lips)
false.
No! The solution with cost 15 is optimal (but not unique).
However, I find 70 seconds to be too slow for this very small problem size. Are there some some symmetries we can exploit? Consider:
?- Cost #= 15, placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), time(( labeling([], Kinds), labeling([ff], Variables) )).
% 8,651,030 inferences, 0.611 CPU in 0.611 seconds (100% CPU, 14163879 Lips)
Cost = 15,
Placements = [box_x_y_w_l(5, 2, 4, 2, 2), box_x_y_w_l(6, 8, 7, 1, 2), box_x_y_w_l(6, 5, 6, 3, 1), box_x_y_w_l(6, 2, 3, 3, 3), box_x_y_w_l(6, 0, 0, 2, 3), box_x_y_w_l(5, 0, 0, 2, 4)],
Kinds = [0, 0, 0, 0, 2, 4],
Costs = [0, 0, 0, 0, 6, 9],
Variables = [5, 2, 4, 6, 8, 7, 1, 2, 6|...] .
?- Kinds = [4, 2, 0, 0, 0, 0], Cost #= 15, placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), time(( labeling([], Kinds), labeling([ff], Variables) )).
% 11,182,689 inferences, 0.790 CPU in 0.790 seconds (100% CPU, 14153341 Lips)
Kinds = [4, 2, 0, 0, 0, 0],
Cost = 15,
Placements = [box_x_y_w_l(1, 7, 7, 2, 2), box_x_y_w_l(1, 6, 5, 1, 2), box_x_y_w_l(2, 3, 3, 1, 3), box_x_y_w_l(2, 0, 0, 3, 3), box_x_y_w_l(1, 4, 2, 2, 3), box_x_y_w_l(1, 0, 0, 4, 2)],
Costs = [9, 6, 0, 0, 0, 0],
Variables = [1, 7, 7, 1, 6, 5, 1, 2, 2|...] .
These aren't permutations of the same solution, but they are permutations of the same boxes and therefore have identical costs. We don't need to consider both of them! In addition to labeling Kinds a bit more intelligently than in the beginning, we can also require the Kinds list to be monotonically increasing. This excludes lots of redundant solutions and gives much faster termination, and even with better solutions first:
?- placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), chain(Kinds, #=<), time(( labeling([], Kinds), labeling([ff], Variables) )).
% 34,943,765 inferences, 2.865 CPU in 2.865 seconds (100% CPU, 12195550 Lips)
Placements = [box_x_y_w_l(5, 2, 4, 2, 2), box_x_y_w_l(6, 8, 7, 1, 2), box_x_y_w_l(6, 5, 6, 3, 1), box_x_y_w_l(6, 2, 3, 3, 3), box_x_y_w_l(6, 0, 0, 2, 3), box_x_y_w_l(5, 0, 0, 2, 4)],
Kinds = [0, 0, 0, 0, 2, 4],
Costs = [0, 0, 0, 0, 6, 9],
Cost = 15,
Variables = [5, 2, 4, 6, 8, 7, 1, 2, 6|...] .
?- Cost #< 15, placements_([1, 2, 3, 4, 5, 6], 6, Placements, Kinds, Costs, Cost), term_variables(Placements, Variables, [Cost | Costs]), chain(Kinds, #=<), time(( labeling([], Kinds), labeling([ff], Variables) )).
% 31,360,608 inferences, 2.309 CPU in 2.309 seconds (100% CPU, 13581762 Lips)
false.
More tweaks are possible and probably necessary for larger problem sizes. I found that adding bisect in the final labeling helps a bit. So does removing the logically redundant Box1 #= Box2 constraint in placement_disjoint/2. Finally, given the use of chain/2 to restrict Kinds, we can remove the preliminary labeling of Kinds entirely to get a nice speedup! I'm sure there's more, but for a prototype I think it's reasonable enough.
Thank you for this interesting problem!
There are some redundancies in your partial solution, maybe caused by premature optimization.
First, since you have a product_either_way/3, you should not change your input specification, adding products with same id and dimensions swapped. After all, width and height are properties you cannot swap arbitrarly in the real world, and you already have produced a predicate that takes care of this, so I have started removing such duplicates.
Second, the purpose of disjoint/2 is to compute a placement of a set of rectangles, so your area_box_pos_combined/4 and positions_vars/2 are pretty much useless.
Here is how I would approach this problem. First, write a predicate that given a list of products and a box, puts as many as possible into it, and 'returns' those that didn't fit. For instance
fill_box([P|Ps],W,H,Placed,Rs) :-
( product(P,W_i,H_i)
; product(P,H_i,W_i)
),
W_p #= W - W_i,
H_p #= H - H_i,
X_i in 0..W_p,
Y_i in 0..H_p,
U=[p(X_i, W_i, Y_i, H_i)|Placed],
disjoint2(U),
fill_box(Ps,W,H,U,Rs).
fill_box(Rs,_,_,_,Rs).
It's somewhat buggy, because it will stop at the first product it cannot place, but there could be more placeable after this. But what's important, now we can start to test if it's working, given the interaction with key concepts of CLP(FD). disjoint/2 works on bounded variables, so the domain declaration of X_i and Y_i is needed.
?- fill_box([1,1],4,2,[],R).
R = [] .
?- fill_box([1,1],3,2,[],R).
R = [1] .
Now we can provide a driver, maybe as simple as
products_placed_cost([],0).
products_placed_cost(Ps,C) :-
box(W,H,C0),
fill_box(Ps,W,H,[],Rs),
Ps\=Rs,
products_placed_cost(Rs,C1),
C #= C0+C1.
and then let Prolog generate as many solutions as it can, just order them by cost, by means of library(solution_sequences):
?- order_by([asc(C)],products_placed_cost([1,1],C)).
C = 4 ;
C = 4 ;
C = 4 ;
C = 4 ;
C = 6 ;
...
But we don't know which placements have been generated. We have to add arguments that carry back the information. Then
products_placed_cost([],[],0).
products_placed_cost(Ps,[box(W,H,C0,Q)|Qs],C) :-
box(W,H,C0),
fill_box(Ps,W,H,[],Rs,Q),
Ps\=Rs,
products_placed_cost(Rs,Qs,C1),
C #= C0+C1.
fill_box([P|Ps],W,H,Placed,Rs,[P|Qs]) :-
( product(P,W_i,H_i)
; product(P,H_i,W_i)
),
W_p #= W - W_i,
H_p #= H - H_i,
X_i in 0..W_p,
Y_i in 0..H_p,
U=[p(X_i, W_i, Y_i, H_i)|Placed],
disjoint2(U),
fill_box(Ps,W,H,U,Rs,Qs).
fill_box(Rs,_,_,_,Rs,[]).
To be true, library(clpfd) is used just as commodity, but mixed with the searching capabilities of (pure) Prolog gives us a short and declarative solution.
See the specific documentation of library(clpBNR) for a better approach.

Removing list within lists based on nth element

I've got a list of lists, for example:
[[1, 2, 3, 2], [1, 3, 4, 3], [1, 4, 5, 4], [2, 3, 5, 6], [1, 5, 6, 5],
[2, 4, 6, 8], [1, 6, 7, 6], [2, 5, 7, 10], [3, 4, 7, 12], [2, 6, 8, 12]]
I'd like to get the last element of and check to see if is the same as the 4th element of any of the other lists. If it is the same then leave the list alone, but if it is unique then remove the list. So in the example above I would be left with:
[[3, 4, 7, 12], [2, 6, 8, 12]]
Essentially I want to be remove all the lists where the last element is unique.
I've written a predicate to get the nth element:
my_membership(X, [X|_]).
my_membership(X, [_|Tail]) :-
my_membership(X, Tail).
where:
my_membership([_,_,_,Fourth],[[3, 4, 7, 12], [2, 6, 8, 12]]).
gives:
Fourth = 12
Fourth = 12
Start by building two basic predicates:
last([X], X).
last([_|T], X) :- last(T, X).
forth([_,_,_,F|_], F).
The first predicate extracts the last element of a list; the second predicate extracts the forth element of a list.
Now you can make a predicate that counts how many tomes an element X appears in forth place in any of the lists of a list of lists. Below, H in [H|T] is a list:
matching_forth([], _, 0).
matching_forth([H|T], X, R) :- forth(H, X), matching_forth(T, X, RR), R is RR + 1.
matching_forth([_|T], X, R) :- matching_forth(T, X, R).
With these predicates in place you can build a predicate for checking your condition. It will have three clauses - for a situation when the list is empty, for when the head list has a matching forth element in another list, and for situations when it doesn't:
my_membership([], [], _).
my_membership([H|T], [H|R], A) :-
last(H, X), matching_forth(A, X, C), C > 1, my_membership(T, R, A).
my_membership([_|T], R, A) :- my_membership(T, R, A).
The first and last clauses are self-explanatory. The middle clause extracts the last element from the head list, counts how many times it matches the forth element in the original list of lists (A stands for "all"), and adds H to the result when there is a match. Adding happens through unification with the head of the result list.
Finally, you need a my_membership/2 predicate to start off the recursive chain that passes along the original list of lists:
my_membership(L, R) :- my_membership(L, R, L).
Demo.
Here's a different twist on a potential solution. It uses an accumulator to collect members that we've seen already and checks along the way. The result saves those that have either been seen or are currently in the tail. It requires the use of the built-in, memberchk/2.
my_membership(L, R) :-
my_membership(L, [], R).
my_membership([], _, []).
my_membership([X|T], Acc, R) :-
X = [_,_,_,D],
( memberchk([_,_,_,D], Acc)
-> R = [X|T1],
Acc1 = Acc
; memberchk([_,_,_,D], T)
-> R = [X|T1],
Acc1 = [X|Acc]
; R = T1,
Acc1 = Acc
),
my_membership(T, Acc1, T1).
| ?- my_membership([[1, 2, 3, 2], [1, 3, 4, 3], [1, 4, 5, 4], [2, 3, 5, 6], [1, 5, 6, 5],
[2, 4, 6, 8], [1, 6, 7, 6], [2, 5, 7, 10], [3, 4, 7, 12], [2, 6, 8, 12]], L).
L = [[2,3,5,6],[1,6,7,6],[3,4,7,12],[2,6,8,12]]
yes

SWI-Prolog sort predicate not working

I just made up a program, doing following task: "Get elements, which values are equal to their indexes".
Here is the code:
% get element's index
get_index([Element|_], Element, 0).
get_index([_|T], Element, Index) :-
get_index(T, Element, Index1),
Index is Index1+1.
index_equals_to_element(List, List2) :-
member(X, List),
get_index(List, X, Index),
Index =:= X,
append([], [X], List2).
It works pretty well. But there is one problem. For list [0, 3, 2, 4, 0] my predicate index_equals_to_element returns [0, 2, 0].
Okay, let it happen. But when I'm trying to output only unique elements, I'm getting the same list without any changes. Example:
?- index_equals_to_element([0, 3, 2, 4, 0], List).
% Outputs [0, 2, 0]
?- sort(List, List2).
% Outputs [0, 2, 0] either, when expected [0, 2]
It's very strange for me, because this works fine:
?- sort([0, 2, 1, 0], List).
% Outputs [0, 1, 2].
Why sort doesn't work only with the list, generated by my predicate?
A simple solution is:
index_equals_to_element(List1, List2) :-
% assume that list position index starts at 0
index_equals_to_element(List1, 0, List2).
index_equals_to_element([], _, []).
index_equals_to_element([X| Xs], Index, List2) :-
NextIndex is Index + 1,
( X == Index ->
List2 = [X| Tail],
index_equals_to_element(Xs, NextIndex, Tail)
; index_equals_to_element(Xs, NextIndex, List2)
).
Example call:
?- index_equals_to_element([0, 3, 2, 4, 0], List).
List = [0, 2].
I suggest you study it by using the trace feature of your Prolog system by typing the query:
?- trace, index_equals_to_element([0, 3, 2, 4, 0], List).
Step trough the execution until is the predicate definition is clear for you.
Your index_equals_to_element([0, 3, 2, 4, 0], List). doesn't output [0, 2, 0] as you claim, but gives three answers [0], [2] and [0]:
?- index_equals_to_element([0, 3, 2, 4, 0], List).
List = [0] ;
List = [2] ;
List = [0] ;
false.
You can use findall to get what you want:
?- findall(X, index_equals_to_element([0, 3, 2, 4, 0], [X]), List).
List = [0, 2, 0].
Update. Here is what I think a better implementation of index_equals_to_element/2:
index_equals_to_element(List, List2) :-
index_equals_to_element(List, 0, List2).
index_equals_to_element([], _, []).
index_equals_to_element([X | Rest], I, Rest2) :-
Inext is I + 1,
index_equals_to_element(Rest, Inext, NewRest),
( X =:= I ->
Rest2 = [X | NewRest]
;
Rest2 = NewRest
).
Test run:
?- index_equals_to_element([0, 3, 2, 4, 0], List).
List = [0, 2].
?- index_equals_to_element([0, 1, 2, 2, 4, 5], List).
List = [0, 1, 2, 4, 5].
The other answers are best for learning the nuts and bolts of Prolog. But here's a more concise (but also easier to grok) solution using the higher-order predicate findall/3 and nth0/3 from the SWI-Prolog library(lists):
elements_equal_to_index(List, Elements) :-
findall(Index, nth0(Index, List, Index), Elements).
Edit:
As #Paulo Moura pointed out in a comment, the above answer is only equivalent to the others offered here if all argument are instantiated. I.e., if the above encounters a free variable in the list, I will bind that variable to its index in the list instead of rejecting it as an unsatisfactory element. The addition of a test for strong equality between the index and the list element should make the answer conform:
elements_equal_to_index(List, Elements) :-
findall( Index,
( nth0(Index, List, Elem),
Elem == Index ),
Elements
).

8 queen solution that use a space state "graph" in Prolog don't work

I am studying Prolog on Ivan Bratko book: Programming for Artificial Intelligence and on the book I have found this version of 8 Queens problem that use a space state "graph" to solve the problem:
s(Queens, [Queen|Queens]) :- member(Queen, [1,2,3,4,5,6,7,8]),
noattack(Queen, Queens).
goal([_,_,_,_,_,_,_,_]).
noattack(_,[],_).
noattack(Y,[Y1|Ylist],Xdist) :-
Y1-Y =\= Xdist,
Y-Y1 =\= Xdist,
Dist1 is Xdist + 1,
noattack(Y,Ylist,Dist1).
solve(N,[N]) :- goal(N).
solve(N, [N|Sol1]) :- s(N,N1),
solve(N1,Sol1).
It combines the 8 Queens problem solution based on permutation (use its noattack/3 relation) and the s/2 predicate that I think that build the possible successor states state (the nodes of my graph). So I have something like:
s(ActualState, SuccessorState)
The goal/1 predicate I think that only specify that I have to place exactly 8 queens.
On the book say me that executing this query: solve([],Solution) it will produce a list of board positions with increasing number of queens and that this list will end with a safe configuration of the eight queens.
But if I try to execute this query don't work and I will obtain this output:
?- solve([],Solution).
ERROR: s/2: Undefined procedure: noattack/2
ERROR: However, there are definitions for:
ERROR: noattack/3
Because,rightly, the noattack predicate called in the line 2 take only 2 parameters but the noattack predicate must have 3 parameters...bue on the book is given in this wrong way and I don't know how solve this problem...
Why? What am I missing?
few bugs:
s(Queens, [Queen|Queens]) :- member(Queen, [1,2,3,4,5,6,7,8]),
noattack(Queen, Queens, 1).
noattack(_,[],_) :- !.
noattack(Y,[Y1|Ylist],Xdist) :- Y =\= Y1,
Y1-Y =\= Xdist,
Y-Y1 =\= Xdist,
Dist1 is Xdist + 1,
noattack(Y,Ylist,Dist1).
Then,
18 ?- solve([],_X), last(_X,S).
S = [4, 2, 7, 3, 6, 8, 5, 1] ;
S = [5, 2, 4, 7, 3, 8, 6, 1] ;
S = [3, 5, 2, 8, 6, 4, 7, 1] ;
S = [3, 6, 4, 2, 8, 5, 7, 1] ;
S = [5, 7, 1, 3, 8, 6, 4, 2] ;
S = [4, 6, 8, 3, 1, 7, 5, 2]
and,
25 ?- findall( X, solve([],X), _S), length(_S,N).
N = 92.

Resources