272 lines
7.9 KiB
Python
Executable File
272 lines
7.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright 2010-2025 Google LLC
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# Copyright 2011 Google Inc. All Rights Reserved.
|
|
|
|
"""knapsack_solver unittest file."""
|
|
|
|
from absl import app
|
|
from absl.testing import absltest
|
|
from ortools.algorithms.python import knapsack_solver
|
|
|
|
|
|
class PyWrapAlgorithmsKnapsackSolverTest(absltest.TestCase):
|
|
|
|
def RealSolve(self, profits, weights, capacities, solver_type, use_reduction):
|
|
solver = knapsack_solver.KnapsackSolver(solver_type, "solver")
|
|
solver.set_use_reduction(use_reduction)
|
|
solver.init(profits, weights, capacities)
|
|
profit = solver.solve()
|
|
|
|
return profit
|
|
|
|
def SolveKnapsackProblemUsingSpecificSolver(
|
|
self, profits, weights, capacities, solver_type
|
|
):
|
|
result_when_reduction = self.RealSolve(
|
|
profits, weights, capacities, solver_type, True
|
|
)
|
|
result_when_no_reduction = self.RealSolve(
|
|
profits, weights, capacities, solver_type, False
|
|
)
|
|
|
|
if result_when_reduction == result_when_no_reduction:
|
|
return result_when_reduction
|
|
else:
|
|
return self._invalid_solution
|
|
|
|
def SolveKnapsackProblem(self, profits, weights, capacities):
|
|
self._invalid_solution = -1
|
|
max_number_of_items_for_brute_force = 15
|
|
max_number_of_items_for_divide_and_conquer = 32
|
|
max_number_of_items_for_64_items_solver = 64
|
|
number_of_items = len(profits)
|
|
# This test is ran as size = 'small. To be fast enough, the dynamic
|
|
# programming solver should be limited to instances with capacities smaller
|
|
# than 10^6.
|
|
max_capacity_for_dynamic_programming_solver = 1000000
|
|
generic_profit = self.SolveKnapsackProblemUsingSpecificSolver(
|
|
profits,
|
|
weights,
|
|
capacities,
|
|
knapsack_solver.SolverType.KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER,
|
|
)
|
|
|
|
if generic_profit == self._invalid_solution:
|
|
return self._invalid_solution
|
|
|
|
# Disabled due to ASAN raising a runtime error:
|
|
# outside the range of representable values of type 'int'
|
|
# cbc_profit = self.SolveKnapsackProblemUsingSpecificSolver(
|
|
# profits,
|
|
# weights,
|
|
# capacities,
|
|
# knapsack_solver.SolverType.
|
|
# KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER)
|
|
# if cbc_profit != generic_profit:
|
|
# return self._invalid_solution
|
|
|
|
try:
|
|
scip_profit = self.SolveKnapsackProblemUsingSpecificSolver(
|
|
profits,
|
|
weights,
|
|
capacities,
|
|
knapsack_solver.SolverType.KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER,
|
|
)
|
|
if scip_profit != generic_profit:
|
|
return self._invalid_solution
|
|
except AttributeError:
|
|
print("SCIP support not compiled in")
|
|
|
|
if len(weights) > 1:
|
|
return generic_profit
|
|
|
|
if number_of_items <= max_number_of_items_for_brute_force:
|
|
brute_force_profit = self.SolveKnapsackProblemUsingSpecificSolver(
|
|
profits,
|
|
weights,
|
|
capacities,
|
|
knapsack_solver.SolverType.KNAPSACK_BRUTE_FORCE_SOLVER,
|
|
)
|
|
if brute_force_profit != generic_profit:
|
|
return self._invalid_solution
|
|
|
|
if number_of_items <= max_number_of_items_for_64_items_solver:
|
|
items64_profit = self.SolveKnapsackProblemUsingSpecificSolver(
|
|
profits,
|
|
weights,
|
|
capacities,
|
|
knapsack_solver.SolverType.KNAPSACK_64ITEMS_SOLVER,
|
|
)
|
|
if items64_profit != generic_profit:
|
|
return self._invalid_solution
|
|
|
|
if capacities[0] <= max_capacity_for_dynamic_programming_solver:
|
|
dynamic_programming_profit = self.SolveKnapsackProblemUsingSpecificSolver(
|
|
profits,
|
|
weights,
|
|
capacities,
|
|
knapsack_solver.SolverType.KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER,
|
|
)
|
|
if dynamic_programming_profit != generic_profit:
|
|
return self._invalid_solution
|
|
|
|
if number_of_items <= max_number_of_items_for_divide_and_conquer:
|
|
divide_and_conquer_profit = self.SolveKnapsackProblemUsingSpecificSolver(
|
|
profits,
|
|
weights,
|
|
capacities,
|
|
knapsack_solver.SolverType.KNAPSACK_DIVIDE_AND_CONQUER_SOLVER,
|
|
)
|
|
if divide_and_conquer_profit != generic_profit:
|
|
return self._invalid_solution
|
|
|
|
return generic_profit
|
|
|
|
def testSolveOneDimension(self):
|
|
profits = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
weights = [[1, 2, 3, 4, 5, 6, 7, 8, 9]]
|
|
capacities = [34]
|
|
optimal_profit = 34
|
|
profit = self.SolveKnapsackProblem(profits, weights, capacities)
|
|
self.assertEqual(optimal_profit, profit)
|
|
|
|
def testSolveTwoDimensions(self):
|
|
profits = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
weights = [[1, 2, 3, 4, 5, 6, 7, 8, 9], [1, 1, 1, 1, 1, 1, 1, 1, 1]]
|
|
capacities = [34, 4]
|
|
optimal_profit = 30
|
|
profit = self.SolveKnapsackProblem(profits, weights, capacities)
|
|
self.assertEqual(optimal_profit, profit)
|
|
|
|
def testSolveBigOneDimension(self):
|
|
profits = [
|
|
360,
|
|
83,
|
|
59,
|
|
130,
|
|
431,
|
|
67,
|
|
230,
|
|
52,
|
|
93,
|
|
125,
|
|
670,
|
|
892,
|
|
600,
|
|
38,
|
|
48,
|
|
147,
|
|
78,
|
|
256,
|
|
63,
|
|
17,
|
|
120,
|
|
164,
|
|
432,
|
|
35,
|
|
92,
|
|
110,
|
|
22,
|
|
42,
|
|
50,
|
|
323,
|
|
514,
|
|
28,
|
|
87,
|
|
73,
|
|
78,
|
|
15,
|
|
26,
|
|
78,
|
|
210,
|
|
36,
|
|
85,
|
|
189,
|
|
274,
|
|
43,
|
|
33,
|
|
10,
|
|
19,
|
|
389,
|
|
276,
|
|
312,
|
|
]
|
|
weights = [
|
|
[
|
|
7,
|
|
0,
|
|
30,
|
|
22,
|
|
80,
|
|
94,
|
|
11,
|
|
81,
|
|
70,
|
|
64,
|
|
59,
|
|
18,
|
|
0,
|
|
36,
|
|
3,
|
|
8,
|
|
15,
|
|
42,
|
|
9,
|
|
0,
|
|
42,
|
|
47,
|
|
52,
|
|
32,
|
|
26,
|
|
48,
|
|
55,
|
|
6,
|
|
29,
|
|
84,
|
|
2,
|
|
4,
|
|
18,
|
|
56,
|
|
7,
|
|
29,
|
|
93,
|
|
44,
|
|
71,
|
|
3,
|
|
86,
|
|
66,
|
|
31,
|
|
65,
|
|
0,
|
|
79,
|
|
20,
|
|
65,
|
|
52,
|
|
13,
|
|
]
|
|
]
|
|
capacities = [850]
|
|
optimal_profit = 7534
|
|
profit = self.SolveKnapsackProblem(profits, weights, capacities)
|
|
self.assertEqual(optimal_profit, profit)
|
|
|
|
|
|
def main(_):
|
|
absltest.main()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(main)
|