1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /*                                                                           */
3 /*                  This file is part of the program and library             */
4 /*         SCIP --- Solving Constraint Integer Programs                      */
5 /*                                                                           */
6 /*    Copyright (C) 2002-2021 Konrad-Zuse-Zentrum                            */
7 /*                            fuer Informationstechnik Berlin                */
8 /*                                                                           */
9 /*  SCIP is distributed under the terms of the ZIB Academic License.         */
10 /*                                                                           */
11 /*  You should have received a copy of the ZIB Academic License              */
12 /*  along with SCIP; see the file COPYING. If not visit scipopt.org.         */
13 /*                                                                           */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file   solveknapsackexactly.c
17  * @brief  unit tests for exact dynamic programming algorithm for knapsack problem
18  * @author Marc Pfetsch
19  * @author Gregor Hendel
20  */
21 
22 #include "scip/cons_knapsack.h"
23 #include "scip/scip.h"
24 
25 #include "include/scip_test.h"
26 
27 #define EPS 1e-06
28 #define MAX_ARRAYLEN 1000
29 
30 /* GLOBAL VARIABLES */
31 static SCIP* scip;
32 static SCIP_Longint weights[MAX_ARRAYLEN];
33 static int nitems;
34 static SCIP_Real profits[MAX_ARRAYLEN];
35 static int items[MAX_ARRAYLEN];
36 static int solitems[MAX_ARRAYLEN];
37 static int nonsolitems[MAX_ARRAYLEN];
38 static SCIP_Longint capacity;
39 static SCIP_Bool success;
40 static SCIP_Real solval;
41 static int nnonsolitems;
42 static int nsolitems = -1;
43 
44 /* TEST SUITE */
45 static
setup(void)46 void setup(void)
47 {
48    int i;
49 
50    SCIPcreate(&scip);
51 
52    /* assign items */
53    for( i = 0; i < MAX_ARRAYLEN; ++i )
54       items[i] = i;
55 }
56 
57 static
teardown(void)58 void teardown(void)
59 {
60    SCIPfree(&scip);
61 }
62 
63 /** run exact knapsack algorithm on the given data  */
64 static
solveKnapsack(void)65 SCIP_RETCODE solveKnapsack(
66    void
67    )
68 {
69    SCIP_CALL( SCIPsolveKnapsackExactly(scip, nitems, weights, profits, capacity, items, solitems, nonsolitems, &nsolitems, &nnonsolitems, &solval, &success) );
70 
71    return SCIP_OKAY;
72 }
73 
74 /** is set1 contained in set2? */
75 static
checkSetContainment(int * set1,int * set2,int len1,int len2)76 SCIP_Bool checkSetContainment(
77    int*                  set1,
78    int*                  set2,
79    int                   len1,
80    int                   len2
81    )
82 {
83    int j;
84    int sortedset2 [MAX_ARRAYLEN];
85 
86    cr_assert(len1 <= MAX_ARRAYLEN);
87    cr_assert(len2 <= MAX_ARRAYLEN);
88 
89    if( len1 > len2 )
90       return FALSE;
91 
92    BMScopyMemoryArray(sortedset2, set2, len2);
93    SCIPsortInt(sortedset2, len2);
94 
95    /* find each item of set 1 in set 2 */
96    for( j = 0; j < len1; ++j )
97    {
98       int pos;
99       if( ! SCIPsortedvecFindInt(sortedset2, set1[j], len2, &pos) )
100          return FALSE;
101    }
102 
103    return TRUE;
104 }
105 
106 TestSuite(solveknapsackexactly, .init = setup, .fini = teardown);
107 
108 /* TESTS  */
109 
110 /*
111  * test trivial cases
112  */
113 Test(solveknapsackexactly, test1, .description="check whether the case that all items are redundant is caught correctly")
114 {
115    nitems = 2;
116    capacity = 1LL;
117    weights[0] = 2LL;
118    weights[1] = 1LL;
119    profits[0] = 1.0;   /* should not be taken, since capacity is too large */
120    profits[1] = -1.0;  /* should not be taken, since profit is negative */
121 
122    solveKnapsack();
123 
124    cr_assert( success );
125    cr_assert( nsolitems == 0 );
126    cr_assert( nnonsolitems == 2 );
127    cr_assert_float_eq(solval, 0.0, EPS);
128 }
129 
130 Test(solveknapsackexactly, test2, .description="test whether the correct items are sorted out (weight == 0 or negative profit)")
131 {
132    /* also tests whether the case of all items fitting into the knapsack is caught correctly. */
133    nitems = 5;
134    capacity = 1LL;
135    weights[0] = 0LL;
136    weights[1] = 0LL;
137    weights[2] = 2LL;
138    weights[3] = 2LL;
139    weights[4] = 1LL;
140 
141    profits[0] = 1.0;   /* should take item 1, since weight is 0 */
142    profits[1] = -1.0;  /* should not take item 1, since profit is negative, although weight is 0 */
143    profits[2] = 1.0;   /* should not take item 2, since weight is too large */
144    profits[3] = -1.0;  /* should not take item 3, since weight is too large and profit is negative */
145    profits[4] = 1.0;   /* should take item */
146 
147    solveKnapsack();
148 
149    cr_assert( success );
150    cr_assert( nsolitems == 2 );
151    cr_assert( nnonsolitems == 3 );
152    cr_assert( solitems[0] == 0 );
153    cr_assert( solitems[1] == 4 );
154    cr_assert( nonsolitems[0] == 1 );
155    cr_assert( nonsolitems[1] == 2 );
156    cr_assert( nonsolitems[2] == 3 );
157    cr_assert_float_eq(solval, 2.0, EPS);
158 }
159 
160 Test(solveknapsackexactly, test3, .description="test whether the case of all equal weights is handled correctly")
161 {
162    nitems = 4;
163    capacity = 4LL;
164    weights[0] = 2LL;
165    weights[1] = 2LL;
166    weights[2] = 2LL;
167    weights[3] = 2LL;
168 
169    profits[0] = 1.0;
170    profits[1] = 2.0;
171    profits[2] = 3.0;
172    profits[3] = 4.0;
173 
174    solveKnapsack();
175 
176    cr_assert( success );
177    cr_assert( nsolitems == 2 );
178    cr_assert( nnonsolitems == 2 );
179    cr_assert( (solitems[0] == 2 && solitems[1] == 3) || (solitems[0] == 3 && solitems[1] == 2) );
180    cr_assert( (nonsolitems[0] == 0 && nonsolitems[1] == 1) || (nonsolitems[0] == 1 && nonsolitems[1] == 0) );
181    cr_assert_float_eq(solval, 7.0, EPS);
182 }
183 
184 Test(solveknapsackexactly, test4, .description="test whether the case that only one item fits into the knapsack is handled correctly")
185 {
186    nitems = 3;
187    capacity = 3LL;
188    weights[0] = 2LL;
189    weights[1] = 3LL;
190    weights[2] = 2LL;
191 
192    profits[0] = 3.0;
193    profits[1] = 2.0;
194    profits[2] = 1.0;
195 
196    solveKnapsack();
197 
198    cr_assert( success );
199    cr_assert( nsolitems == 1 );
200    cr_assert( nnonsolitems == 2 );
201    cr_assert( solitems[0] == 0 );
202    cr_assert( (nonsolitems[0] == 1 && nonsolitems[1] == 2) || (nonsolitems[0] == 2 && nonsolitems[1] == 1) );
203    cr_assert_float_eq(solval, 3.0, EPS);
204 }
205 
206 Test(solveknapsackexactly, test_greedy1, .description="test greedy algorithm")
207 {
208    nitems = 3;
209    capacity = 3LL;
210    weights[0] = 1LL;
211    weights[1] = 2LL;
212    weights[2] = 1LL;
213 
214    profits[0] = 3.0;
215    profits[1] = 2.0;
216    profits[2] = 0.5;
217 
218    /* Due to the weights and profits, the ordering is just 0,1,2. The greedy solution will pick {0,1}. This is also
219     * optimal, since we used all the capacity and this solution is an integral optimal solution. So the greedy algorithm
220     * should allow to terminate the process. Note, however, that this cannot be seen from the outside, so, e.g., a
221     * debugger has to be used. */
222    solveKnapsack();
223 
224    cr_assert( success );
225    cr_assert( nsolitems == 2 );
226    cr_assert( nnonsolitems == 1 );
227    cr_assert( (solitems[0] == 0 && solitems[1] == 1) || (solitems[0] == 1 && solitems[1] == 0) );
228    cr_assert( nonsolitems[0] == 2 );
229    cr_assert_float_eq(solval, 5, EPS);
230 }
231 
232 Test(solveknapsackexactly, test_greedy2, .description="test whether greedy solution is equal to the rounded LP value")
233 {
234    nitems = 3;
235    capacity = 4LL;
236    weights[0] = 1LL;
237    weights[1] = 2LL;
238    weights[2] = 2LL;
239 
240    profits[0] = 3.0;
241    profits[1] = 1.0;
242    profits[2] = 1.0;
243 
244    solveKnapsack();
245 
246    /* the solution packs two items and is optimal, since the LP optimal value is 4.5 */
247    cr_assert( success );
248    cr_assert( nsolitems == 2 );
249    cr_assert( nnonsolitems == 1 );
250    cr_assert_float_eq(solval, 4.0, EPS);
251 }
252 
253 Test(solveknapsackexactly, test_general, .description="general test")
254 {
255    nitems = 6;
256    capacity = 13LL;
257    weights[0] = 7LL;
258    weights[1] = 2LL;
259    weights[2] = 7LL;
260    weights[3] = 5LL;
261    weights[4] = 1LL;
262    weights[5] = 3LL;
263 
264    profits[0] = 1.0;
265    profits[1] = 1.0;
266    profits[2] = 1.0;
267    profits[3] = 1.0;
268    profits[4] = 1.0;
269    profits[5] = 1.0;
270 
271    solveKnapsack();
272 
273    cr_assert( success );
274    cr_assert( nsolitems == 4 );
275    cr_assert( nnonsolitems == 2 );
276    cr_assert( (nonsolitems[0] == 0 && nonsolitems[1] == 2) || (nonsolitems[0] == 2 && nonsolitems[1] == 0) );
277    cr_assert_float_eq(solval, 4.0, EPS);
278 }
279 
280 /* large test */
281 Test(solveknapsackexactly, test_large, .description="large test")
282 {
283    int j;
284 
285    nitems = 1000;
286    capacity = nitems + 1;
287 
288    /* half of the items have a weight of 2 and a smaller profit */
289    for (j = 0; j < nitems/2; ++j)
290    {
291       weights[j] = 2LL;
292       profits[j] = j;
293    }
294 
295    /* the remaining half of the items have a weight of 1 but a larger profit; all of them should be selected */
296    for (j = nitems/2; j < nitems; ++j)
297    {
298       weights[j] = 1LL;
299       profits[j] = j;
300    }
301 
302    solveKnapsack();
303 
304    cr_assert( success );
305    cr_assert( nsolitems == 750 ); /* the 500 latter items with higher profit, and 250 of the less profitable items */
306    cr_assert( nnonsolitems == nitems - 750 );
307    cr_assert( checkSetContainment(&items[nitems/2], solitems, nitems / 2, nsolitems) );
308    cr_assert( checkSetContainment(&items[250], solitems, 250, nsolitems) );
309    cr_assert( checkSetContainment(items, nonsolitems, 250, nnonsolitems) );
310 }
311