-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #77 from ProvideQ/feat/knapsack-problem-mask
Added Knapsack problem mask and solver
- Loading branch information
Showing
9 changed files
with
241 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Knapsack solver using the knapsack-pip library | ||
|
||
import sys | ||
from knapsack01 import HSKnapsack | ||
|
||
if len(sys.argv) != 3: | ||
raise TypeError('This script expects exactly 2 arguments. Input file (argument 1) and output file (argument 2).') | ||
|
||
input_path = sys.argv[1] | ||
output_path = sys.argv[2] | ||
|
||
with open(input_path, 'r') as input_file: | ||
lines = input_file.readlines() | ||
|
||
# first line gives number of items | ||
number_items : int = int(lines[0]) | ||
|
||
profits: list[int] = [] | ||
weights: list[int] = [] | ||
|
||
# read items into profits, weights lists | ||
for i in range(number_items): | ||
item = lines[i + 1].split(' ') | ||
profits.append(int(item[1])) | ||
weights.append(int(item[2])) | ||
|
||
# last line contains maximum capacity of the knapsack | ||
capacity: int = int(lines[-1]) | ||
|
||
# let the library solve the problem ^^ | ||
knapsack = HSKnapsack(capacity, profits, weights) | ||
max_profit, max_solution = knapsack.maximize() | ||
|
||
# max_solution only contains indicator whether item is present, collect items for solutions in a list | ||
items_in_solution: list[list[int]] = [] | ||
for i in range(number_items): | ||
if max_solution[i] == 1: | ||
items_in_solution.append([profits[i], weights[i]]) | ||
|
||
# returning the solution as a single line with the maximum profit | ||
# followed by the items present represented by profit/weight | ||
with open(output_path, 'w') as output_file: | ||
output_file.write(str(max_profit) + '\n') | ||
for item in items_in_solution: | ||
output_file.write(f"{item[0]} {item[1]}\n") |
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,3 @@ | ||
# This file describes the python package requirements for the knapsack problem solver | ||
|
||
knapsack-pip |
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
58 changes: 58 additions & 0 deletions
58
src/main/java/edu/kit/provideq/toolbox/knapsack/KnapsackConfiguration.java
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,58 @@ | ||
package edu.kit.provideq.toolbox.knapsack; | ||
|
||
import edu.kit.provideq.toolbox.ResourceProvider; | ||
import edu.kit.provideq.toolbox.knapsack.solvers.PythonKnapsackSolver; | ||
import edu.kit.provideq.toolbox.meta.Problem; | ||
import edu.kit.provideq.toolbox.meta.ProblemManager; | ||
import edu.kit.provideq.toolbox.meta.ProblemType; | ||
import java.io.IOException; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
/** | ||
* Definition and registration of the Knapsack problem. | ||
*/ | ||
@Configuration | ||
public class KnapsackConfiguration { | ||
/** | ||
* An optimization problem: | ||
* For given items each with a weight and value, determine which items are part of a collection | ||
* where the total weight is less than or equal to a given limit | ||
* and the sum of values is as large as possible. | ||
*/ | ||
public static final ProblemType<String, String> KNAPSACK = new ProblemType<>( | ||
"knapsack", | ||
String.class, | ||
String.class | ||
); | ||
|
||
@Bean | ||
ProblemManager<String, String> getKnapsackManager( | ||
PythonKnapsackSolver pythonKnapsackSolver, | ||
ResourceProvider resourceProvider | ||
) { | ||
return new ProblemManager<>( | ||
KNAPSACK, | ||
Set.of(pythonKnapsackSolver), | ||
loadExampleProblems(resourceProvider) | ||
); | ||
} | ||
|
||
private Set<Problem<String, String>> loadExampleProblems( | ||
ResourceProvider resourceProvider | ||
) { | ||
try { | ||
var problemInputStream = Objects.requireNonNull( | ||
getClass().getResourceAsStream("10-items.txt"), | ||
"10-items example for Knapsack is unavailable!" | ||
); | ||
var problem = new Problem<>(KNAPSACK); | ||
problem.setInput(resourceProvider.readStream(problemInputStream)); | ||
return Set.of(problem); | ||
} catch (IOException e) { | ||
throw new RuntimeException("Could not load example problems", e); | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/main/java/edu/kit/provideq/toolbox/knapsack/solvers/KnapsackSolver.java
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,15 @@ | ||
package edu.kit.provideq.toolbox.knapsack.solvers; | ||
|
||
import edu.kit.provideq.toolbox.knapsack.KnapsackConfiguration; | ||
import edu.kit.provideq.toolbox.meta.ProblemSolver; | ||
import edu.kit.provideq.toolbox.meta.ProblemType; | ||
|
||
/** | ||
* A solver for Knapsack problems. | ||
*/ | ||
public abstract class KnapsackSolver implements ProblemSolver<String, String> { | ||
@Override | ||
public ProblemType<String, String> getProblemType() { | ||
return KnapsackConfiguration.KNAPSACK; | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
src/main/java/edu/kit/provideq/toolbox/knapsack/solvers/PythonKnapsackSolver.java
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,48 @@ | ||
package edu.kit.provideq.toolbox.knapsack.solvers; | ||
|
||
import edu.kit.provideq.toolbox.PythonProcessRunner; | ||
import edu.kit.provideq.toolbox.Solution; | ||
import edu.kit.provideq.toolbox.meta.SubRoutineResolver; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.stereotype.Component; | ||
import reactor.core.publisher.Mono; | ||
|
||
@Component | ||
public class PythonKnapsackSolver extends KnapsackSolver { | ||
private final String knapsackPath; | ||
private final ApplicationContext context; | ||
|
||
@Autowired | ||
public PythonKnapsackSolver( | ||
@Value("${python.directory}/knapsack") String knapsackPath, | ||
ApplicationContext context) { | ||
this.knapsackPath = knapsackPath; | ||
this.context = context; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "Python Knapsack"; | ||
} | ||
|
||
@Override | ||
public Mono<Solution<String>> solve( | ||
String input, | ||
SubRoutineResolver subRoutineResolver | ||
) { | ||
var solution = new Solution<String>(); | ||
|
||
var processResult = context | ||
.getBean( | ||
PythonProcessRunner.class, | ||
knapsackPath, | ||
"knapsack.py") | ||
.addProblemFilePathToProcessCommand() | ||
.addSolutionFilePathToProcessCommand() | ||
.run(getProblemType(), solution.getId(), input); | ||
|
||
return Mono.just(processResult.applyTo(solution)); | ||
} | ||
} |
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
12 changes: 12 additions & 0 deletions
12
src/main/resources/edu/kit/provideq/toolbox/knapsack/10-items.txt
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,12 @@ | ||
10 | ||
1 505 23 | ||
2 352 26 | ||
3 458 20 | ||
4 220 18 | ||
5 354 32 | ||
6 414 27 | ||
7 498 29 | ||
8 545 26 | ||
9 473 30 | ||
10 543 27 | ||
99 |
56 changes: 56 additions & 0 deletions
56
src/test/java/edu/kit/provideq/toolbox/api/KnapsackSolversTest.java
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,56 @@ | ||
package edu.kit.provideq.toolbox.api; | ||
|
||
import static edu.kit.provideq.toolbox.knapsack.KnapsackConfiguration.KNAPSACK; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
|
||
import edu.kit.provideq.toolbox.SolutionStatus; | ||
import edu.kit.provideq.toolbox.meta.ProblemManagerProvider; | ||
import edu.kit.provideq.toolbox.meta.ProblemSolver; | ||
import edu.kit.provideq.toolbox.meta.ProblemState; | ||
import java.time.Duration; | ||
import java.util.stream.Stream; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.TestInstance; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.test.web.reactive.server.WebTestClient; | ||
|
||
@TestInstance(TestInstance.Lifecycle.PER_CLASS) | ||
@SpringBootTest | ||
@AutoConfigureMockMvc | ||
public class KnapsackSolversTest { | ||
@Autowired | ||
private WebTestClient client; | ||
|
||
@Autowired | ||
private ProblemManagerProvider problemManagerProvider; | ||
|
||
@BeforeEach | ||
void beforeEach() { | ||
this.client = this.client.mutate() | ||
.responseTimeout(Duration.ofSeconds(20)) | ||
.build(); | ||
} | ||
|
||
@SuppressWarnings("OptionalGetWithoutIsPresent") | ||
Stream<Arguments> provideArguments() { | ||
var problemManager = problemManagerProvider.findProblemManagerForType(KNAPSACK).get(); | ||
|
||
return ApiTestHelper.getAllArgumentCombinations(problemManager) | ||
.map(list -> Arguments.of(list.get(0), list.get(1))); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("provideArguments") | ||
void testKnapsackSolver(ProblemSolver<String, String> solver, String input) { | ||
var problem = ApiTestHelper.createProblem(client, solver, input, KNAPSACK); | ||
assertEquals(ProblemState.SOLVED, problem.getState()); | ||
assertNotNull(problem.getSolution()); | ||
assertEquals(SolutionStatus.SOLVED, problem.getSolution().getStatus()); | ||
} | ||
} |