Skip to content

Commit f3e186c

Browse files
committed
added bound comparison
1 parent 96c082c commit f3e186c

File tree

13 files changed

+157
-8
lines changed

13 files changed

+157
-8
lines changed

src/main/java/edu/kit/provideq/toolbox/BoundType.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,21 @@ public enum BoundType {
77
/**
88
* An upper bound.
99
*/
10-
UPPER,
10+
UPPER {
11+
@Override
12+
public int compare(int bound, int actual) {
13+
return 1 - (bound / actual);
14+
}
15+
},
1116
/**
1217
* A lower bound.
1318
*/
14-
LOWER
19+
LOWER {
20+
@Override
21+
public int compare(int bound, int actual) {
22+
return actual / bound - 1;
23+
}
24+
};
25+
26+
public abstract int compare(int bound, int actual);
1527
}

src/main/java/edu/kit/provideq/toolbox/api/EstimationRouter.java

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
1111
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
1212

13+
import com.google.common.collect.Streams;
1314
import edu.kit.provideq.toolbox.meta.Problem;
1415
import edu.kit.provideq.toolbox.meta.ProblemManager;
1516
import edu.kit.provideq.toolbox.meta.ProblemManagerProvider;
1617
import edu.kit.provideq.toolbox.meta.ProblemType;
1718
import io.swagger.v3.oas.annotations.enums.ParameterIn;
1819
import java.util.NoSuchElementException;
1920
import java.util.UUID;
21+
import java.util.regex.Pattern;
2022
import org.springdoc.core.fn.builders.operation.Builder;
2123
import org.springframework.beans.factory.annotation.Autowired;
2224
import org.springframework.context.annotation.Bean;
@@ -42,10 +44,15 @@ public class EstimationRouter {
4244

4345
@Bean
4446
RouterFunction<ServerResponse> getEstimationRoutes() {
45-
return managerProvider.getProblemManagers().stream()
46-
.filter(manager -> manager.getType().getEstimator().isPresent())
47-
.map(this::defineGetRoute)
48-
.reduce(RouterFunction::and)
47+
var managers = managerProvider.getProblemManagers();
48+
return Streams.concat(
49+
managers.stream()
50+
.filter(manager -> manager.getType().getEstimator().isPresent())
51+
.map(this::defineGetRoute),
52+
managers.stream()
53+
.filter(manager -> manager.getType().getSolutionPattern() != null)
54+
.map(this::defineCompareRoute)
55+
).reduce(RouterFunction::and)
4956
.orElse(null);
5057
}
5158

@@ -61,6 +68,18 @@ private RouterFunction<ServerResponse> defineGetRoute(ProblemManager<?, ?> manag
6168
).build();
6269
}
6370

71+
/**
72+
* Compare Operation: GET /problems/TYPE/{problemId}/bound/compare.
73+
*/
74+
private RouterFunction<ServerResponse> defineCompareRoute(ProblemManager<?, ?> manager) {
75+
return route().GET(
76+
getCompareRouteForProblemType(manager.getType()),
77+
accept(APPLICATION_JSON),
78+
req -> handleCompare(manager, req),
79+
ops -> handleCompareDocumentation(manager, ops)
80+
).build();
81+
}
82+
6483
private <InputT, ResultT> Mono<ServerResponse> handleGet(
6584
ProblemManager<InputT, ResultT> manager,
6685
ServerRequest req
@@ -80,6 +99,37 @@ private <InputT, ResultT> Mono<ServerResponse> handleGet(
8099
});
81100
}
82101

102+
private <InputT, ResultT> Mono<ServerResponse> handleCompare(
103+
ProblemManager<InputT, ResultT> manager,
104+
ServerRequest req
105+
) {
106+
var problemId = req.pathVariable(PROBLEM_ID_PARAM_NAME);
107+
var problem = findProblemOrThrow(manager, problemId);
108+
109+
if (problem.getSolution() == null) {
110+
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Problem not solved yet!");
111+
}
112+
113+
if (problem.getBound().isEmpty()) {
114+
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Bound not estimated yet!");
115+
}
116+
117+
int bound = Integer.parseInt(problem.getBound().get().bound().value());
118+
var pattern = Pattern.compile(manager.getType().getSolutionPattern());
119+
var solutionMatcher = pattern.matcher(problem.getSolution().getSolutionData().toString());
120+
int solutionValue;
121+
if (solutionMatcher.find()) {
122+
solutionValue = Integer.parseInt(solutionMatcher.group(1));
123+
} else {
124+
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not parse solution value!");
125+
}
126+
127+
Integer comparison = problem.getBound().get().bound().boundType().compare(bound, solutionValue);
128+
129+
return ok().body(Mono.just(comparison), new ParameterizedTypeReference<>() {
130+
});
131+
}
132+
83133
private void handleGetDocumentation(
84134
ProblemManager<?, ?> manager,
85135
Builder ops
@@ -96,12 +146,35 @@ private void handleGetDocumentation(
96146
);
97147
}
98148

149+
private void handleCompareDocumentation(
150+
ProblemManager<?, ?> manager,
151+
Builder ops
152+
) {
153+
ProblemType<?, ?> problemType = manager.getType();
154+
ops
155+
.operationId(getCompareRouteForProblemType(problemType))
156+
.tag(problemType.getId())
157+
.description("Compares the solution value with the bound for "
158+
+ "the problem with the given ID.")
159+
.parameter(parameterBuilder().in(ParameterIn.PATH).name(PROBLEM_ID_PARAM_NAME))
160+
.response(responseBuilder()
161+
.responseCode(String.valueOf(HttpStatus.OK.value()))
162+
.content(getOkResponseContent())
163+
);
164+
}
165+
99166
private static org.springdoc.core.fn.builders.content.Builder getOkResponseContent() {
100167
return contentBuilder()
101168
.mediaType(APPLICATION_JSON_VALUE)
102169
.schema(schemaBuilder().implementation(BoundDto.class));
103170
}
104171

172+
private static org.springdoc.core.fn.builders.content.Builder getComparisonResponseContent() {
173+
return contentBuilder()
174+
.mediaType(APPLICATION_JSON_VALUE)
175+
.schema(schemaBuilder().implementation(Integer.class));
176+
}
177+
105178
private <InputT, ResultT> Problem<InputT, ResultT> findProblemOrThrow(
106179
ProblemManager<InputT, ResultT> manager,
107180
String id
@@ -122,6 +195,10 @@ private String getEstimationRouteForProblemType(ProblemType<?, ?> type) {
122195
return "/problems/%s/{%s}/bound".formatted(type.getId(), PROBLEM_ID_PARAM_NAME);
123196
}
124197

198+
private String getCompareRouteForProblemType(ProblemType<?, ?> type) {
199+
return getEstimationRouteForProblemType(type) + "/compare";
200+
}
201+
125202
@Autowired
126203
void setManagerProvider(ProblemManagerProvider managerProvider) {
127204
this.managerProvider = managerProvider;

src/main/java/edu/kit/provideq/toolbox/demonstrators/DemonstratorConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class DemonstratorConfiguration {
1717
"demonstrator",
1818
String.class,
1919
String.class,
20+
null,
2021
null
2122
);
2223

src/main/java/edu/kit/provideq/toolbox/featuremodel/anomaly/dead/DeadFeatureConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class DeadFeatureConfiguration {
2727
"feature-model-anomaly-dead",
2828
String.class,
2929
String.class,
30+
null,
3031
null
3132
);
3233

src/main/java/edu/kit/provideq/toolbox/featuremodel/anomaly/voidmodel/VoidModelConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class VoidModelConfiguration {
2727
"feature-model-anomaly-void",
2828
String.class,
2929
String.class,
30+
null,
3031
null
3132
);
3233

src/main/java/edu/kit/provideq/toolbox/knapsack/KnapsackConfiguration.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package edu.kit.provideq.toolbox.knapsack;
22

3+
import edu.kit.provideq.toolbox.Bound;
4+
import edu.kit.provideq.toolbox.BoundType;
35
import edu.kit.provideq.toolbox.ResourceProvider;
46
import edu.kit.provideq.toolbox.exception.MissingExampleException;
57
import edu.kit.provideq.toolbox.knapsack.solvers.PythonKnapsackSolver;
@@ -8,8 +10,14 @@
810
import edu.kit.provideq.toolbox.meta.ProblemManager;
911
import edu.kit.provideq.toolbox.meta.ProblemType;
1012
import java.io.IOException;
13+
import java.util.AbstractMap;
14+
import java.util.ArrayList;
15+
import java.util.Comparator;
16+
import java.util.List;
17+
import java.util.Map;
1118
import java.util.Objects;
1219
import java.util.Set;
20+
import java.util.function.Function;
1321
import org.springframework.context.annotation.Bean;
1422
import org.springframework.context.annotation.Configuration;
1523

@@ -18,6 +26,41 @@
1826
*/
1927
@Configuration
2028
public class KnapsackConfiguration {
29+
/**
30+
* an upper bound estimator by solving the greedy fractional knapsack problem.
31+
*/
32+
private static final Function<String, Bound> estimator = input -> {
33+
var parts = input.split("\n");
34+
var weightLimit = Integer.parseInt(parts[parts.length - 1]);
35+
// map item value to item weight
36+
List<Map.Entry<Integer, Integer>> items = new ArrayList<>();
37+
for (int i = 1; i < parts.length - 1; i++) {
38+
var item = parts[i].split(" ");
39+
items.add(new AbstractMap.SimpleEntry<>(
40+
Integer.parseInt(item[1]),
41+
Integer.parseInt(item[2]))
42+
);
43+
}
44+
items.sort(Comparator.comparingInt(a -> -a.getKey() / a.getValue()));
45+
// keep adding items until the weight limit is reached, then add a fraction of the next item
46+
int bound = 0;
47+
int currentWeight = 0;
48+
while (currentWeight < weightLimit) {
49+
var item = items.remove(0);
50+
var value = item.getKey();
51+
var weight = item.getValue();
52+
if (currentWeight + weight <= weightLimit) {
53+
bound += value;
54+
currentWeight += weight;
55+
} else {
56+
var fraction = (weightLimit - currentWeight) / (double) weight;
57+
bound += (int) (fraction * value);
58+
break;
59+
}
60+
}
61+
return new Bound(String.valueOf(bound), BoundType.UPPER);
62+
};
63+
2164
/**
2265
* An optimization problem:
2366
* For given items each with a weight and value, determine which items are part of a collection
@@ -28,7 +71,8 @@ public class KnapsackConfiguration {
2871
"knapsack",
2972
String.class,
3073
String.class,
31-
null
74+
estimator,
75+
"^(\\d+)"
3276
);
3377

3478
@Bean

src/main/java/edu/kit/provideq/toolbox/maxcut/MaxCutConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class MaxCutConfiguration {
2828
"max-cut",
2929
String.class,
3030
String.class,
31+
null,
3132
null
3233
);
3334

src/main/java/edu/kit/provideq/toolbox/meta/ProblemType.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class ProblemType<InputT, ResultT> {
1212
private final Class<InputT> inputClass;
1313
private final Class<ResultT> resultClass;
1414
private final Function<InputT, Bound> estimator;
15+
private final String solutionPattern;
1516

1617
/**
1718
* Defines a new problem type.
@@ -26,12 +27,14 @@ public ProblemType(
2627
String id,
2728
Class<InputT> inputClass,
2829
Class<ResultT> resultClass,
29-
Function<InputT, Bound> estimator
30+
Function<InputT, Bound> estimator,
31+
String solutionPattern
3032
) {
3133
this.id = id;
3234
this.inputClass = inputClass;
3335
this.resultClass = resultClass;
3436
this.estimator = estimator;
37+
this.solutionPattern = solutionPattern;
3538
}
3639

3740
/**
@@ -62,6 +65,10 @@ public Optional<Function<InputT, Bound>> getEstimator() {
6265
return Optional.ofNullable(estimator);
6366
}
6467

68+
public String getSolutionPattern() {
69+
return solutionPattern;
70+
}
71+
6572
@Override
6673
public String toString() {
6774
return "ProblemType{"

src/main/java/edu/kit/provideq/toolbox/qubo/QuboConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class QuboConfiguration {
2929
"qubo",
3030
String.class,
3131
String.class,
32+
null,
3233
null
3334
);
3435

src/main/java/edu/kit/provideq/toolbox/sat/SatConfiguration.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class SatConfiguration {
2626
"sat",
2727
String.class,
2828
DimacsCnfSolution.class,
29+
null,
2930
null
3031
);
3132

0 commit comments

Comments
 (0)