-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Evolog Modules: module-based implementation of bin-packing problem in…
…cluding unit tests
- Loading branch information
1 parent
3540762
commit 1dcdbf0
Showing
4 changed files
with
159 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
%%% Module Bin-Packing %%% | ||
% | ||
% This module solves instances of the bin-packing decision problem: | ||
% Given a set of bins, described as terms bin(B, SIZE), and items, described as terms item(I, SIZE), | ||
% the module calculates all assignments of items to bins, such that all items are packed and no | ||
% bin size is exceeded. Each answer set corresponds to one valid assignment. | ||
% | ||
% Input: a fact "instance(BIN_LST, ITEM_LST)", where | ||
% - BIN_LST is a list of terms of form bin(B, SIZE), | ||
% - and ITEM_LST is a list of terms of form item(I, SIZE) | ||
% | ||
%%% | ||
#module bin_packing(instance/2 => {item_packed/2}) { | ||
% Unpack input lists. | ||
bin_element(E, TAIL) :- instance(lst(E, TAIL), _). | ||
bin_element(E, TAIL) :- bin_element(_, lst(E, TAIL)). | ||
bin(B, S) :- bin_element(bin(B, S), _). | ||
|
||
item_element(E, TAIL) :- instance(_, lst(E, TAIL)). | ||
item_element(E, TAIL) :- item_element(_, lst(E, TAIL)). | ||
item(I, S) :- item_element(item(I, S), _). | ||
|
||
% For every item, guess an assignment to each bin. | ||
{ item_packed(I, B) : bin(B, _) } :- item(I, _). | ||
% An item may only be assigned to one bin at a time | ||
:- item_packed(I, B1), item_packed(I, B2), B1 != B2. | ||
|
||
% We must not exceed the capacity of any bin. | ||
capacity_used(B, C) :- C = #sum{S : item(I, S), item_packed(I, B)}, bin(B, _). | ||
:- capacity_used(B, C), C > S, bin(B, S). | ||
|
||
% Every item must be packed. | ||
item_packed_somewhere(I) :- item_packed(I, _). | ||
:- item(I, _), not item_packed_somewhere(I). | ||
} | ||
|
||
|
||
%%% Bin-Packing Valuation %%% | ||
% | ||
% Given a Bin-Packing instance and a satisfying assignment, | ||
% calculates a numeric value for the given assignment. | ||
% The value is the sum of the capacities of all bins used in the assignment. | ||
% Intuitively, we calculate the cost in total storage capacity of a given bin assignment. | ||
% | ||
%%% | ||
#module bin_packing_valuation(instance_with_assignment/3 => {assignment_value/1}) { | ||
%% Unpack input | ||
% Extract bins from bin list | ||
bin_element(E, TAIL) :- instance_with_assignment(lst(E, TAIL), _, _). | ||
bin_element(E, TAIL) :- bin_element(_, lst(E, TAIL)). | ||
bin(B, S) :- bin_element(bin(B, S), _). | ||
|
||
% Extract items from item list | ||
item_element(E, TAIL) :- instance_with_assignment(_, lst(E, TAIL), _). | ||
item_element(E, TAIL) :- item_element(_, lst(E, TAIL)). | ||
item(I, S) :- item_element(item(I, S), _). | ||
|
||
% Extract individual item assignments | ||
assignment_element(E, TAIL) :- instance_with_assignment(_, _, lst(E, TAIL)). | ||
assignment_element(E, TAIL) :- assignment_element(_, lst(E, TAIL)). | ||
assigned(I, B) :- assignment_element(item_packed(I, B), _). | ||
|
||
% Get all bins that are used in the assignment | ||
bin_used(B) :- assigned(_, B). | ||
assignment_value(V) :- V = #sum{ S : bin(B, S), bin_used(B)}. | ||
} |
89 changes: 89 additions & 0 deletions
89
alpha-solver/src/test/resources/e2e-tests/bin-packing.test.evl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
%%% Unit tests for bin_packing module | ||
|
||
instance(BINS, ITEMS) :- BINS = #list{bin(B, S) : bin(B, S)}, ITEMS = #list{item(I, S) : item(I, S)}. | ||
instance_with_assignment(BINS, ITEMS, ASSGN) :- instance(BINS, ITEMS), #bin_packing[BINS, ITEMS](ASSGN). | ||
bin_assignment(A) :- instance_with_assignment(_, _, A). | ||
assignment_valuation_result(A, V) :- instance_with_assignment(B, I, A), #bin_packing_valuation[B, I, A](V). | ||
assignment_value(A, V) :- assignment_valuation_result(A, lst(assignment_value(V), lst_empty)). | ||
|
||
#test singleBinPositiveCase(expect: 1) { | ||
given { | ||
bin(a, 10). | ||
|
||
item(x, 3). | ||
item(y, 2). | ||
item(z, 4). | ||
|
||
} | ||
assertForAll { | ||
% There is excatly one assignment | ||
assignment_found :- bin_assignment(_). | ||
:- not assignment_found. | ||
:- bin_assignment(A1), bin_assignment(A2), A1 != A2. | ||
|
||
% Every item is in a bin | ||
assignment_element(lst(E, TAIL), E, TAIL) :- bin_assignment(lst(E, TAIL)). | ||
assignment_element(ASSGN, E, TAIL) :- assignment_element(ASSGN, _, lst(E, TAIL)). | ||
assigned(I, B, A) :- assignment_element(A, item_packed(I, B), _). | ||
item_assigned(A, I) :- item(I, _), assigned(I, _, A). | ||
:- bin_assignment(A), item(I, _), not item_assigned(A, I). | ||
|
||
% In no assignment, any bin capacity is exceeded | ||
capacity_used(A, B, C) :- C = #sum{S : item(I, S), item_assigned(I, B, A)}, bin_assignment(A), bin(B, _). | ||
:- bin_assignment(A), bin(B, S), capacity_used(A, B, C), C > S. | ||
|
||
% The single assignment has value 10 | ||
:- bin_assignment(A), not assignment_value(A, 10). | ||
} | ||
} | ||
|
||
#test singleBinNegativeCase(expect: 1) { | ||
given { | ||
bin(a, 7). | ||
|
||
item(x, 3). | ||
item(y, 2). | ||
item(z, 4). | ||
} | ||
assertForAll { | ||
% There is no assignment | ||
assignment_found :- bin_assignment(_). | ||
:- assignment_found. | ||
} | ||
} | ||
|
||
#test twoBinsTwoAssignments(expect: 1) { | ||
given { | ||
bin(a, 6). | ||
bin(b, 4). | ||
|
||
item(x, 3). | ||
item(y, 2). | ||
item(z, 4). | ||
|
||
} | ||
assertForAll { | ||
% There are exactly two assignments | ||
num_assignments(N) :- N = #count{ A : bin_assignment(A)}. | ||
assignment_found :- bin_assignment(_). | ||
:- num_assignments(N), N != 2. | ||
|
||
% Every item is in a bin | ||
assignment_element(lst(E, TAIL), E, TAIL) :- bin_assignment(lst(E, TAIL)). | ||
assignment_element(ASSGN, E, TAIL) :- assignment_element(ASSGN, _, lst(E, TAIL)). | ||
assigned(I, B, A) :- assignment_element(A, item_packed(I, B), _). | ||
item_assigned(A, I) :- item(I, _), assigned(I, _, A). | ||
:- bin_assignment(A), item(I, _), not item_assigned(A, I). | ||
|
||
% In no assignment, any bin capacity is exceeded | ||
capacity_used(A, B, C) :- C = #sum{S : item(I, S), item_assigned(I, B, A)}, bin_assignment(A), bin(B, _). | ||
:- bin_assignment(A), bin(B, S), capacity_used(A, B, C), C > S. | ||
|
||
% There is an assignment in which items y and z are in bin a. | ||
y_and_z_in_a :- bin_assignment(A), assigned(y, a, A), assigned(z, a, A). | ||
:- not y_and_z_in_a. | ||
|
||
% Both assignments have value 10 | ||
:- bin_assignment(A), not assignment_value(A, 10). | ||
} | ||
} |