Skip to content

Commit e6ed2d3

Browse files
authored
chore: use declarative shadow variables in VRPTW (#60)
1 parent e8c8452 commit e6ed2d3

File tree

8 files changed

+39
-113
lines changed

8 files changed

+39
-113
lines changed

src/main/java/ai/timefold/solver/benchmarks/competitive/cvrplib/CVRPLIBConfiguration.java

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import ai.timefold.solver.core.api.score.buildin.hardsoftlong.HardSoftLongScore;
1616
import ai.timefold.solver.core.config.constructionheuristic.ConstructionHeuristicPhaseConfig;
1717
import ai.timefold.solver.core.config.localsearch.LocalSearchPhaseConfig;
18+
import ai.timefold.solver.core.config.solver.PreviewFeature;
1819
import ai.timefold.solver.core.config.solver.SolverConfig;
1920
import ai.timefold.solver.core.config.solver.termination.TerminationConfig;
2021

@@ -58,6 +59,7 @@ private static SolverConfig getCommunityEditionSolverConfig(CVRPLIBDataset datas
5859
.withUnimprovedSecondsSpentLimit(AbstractCompetitiveBenchmark.UNIMPROVED_SECONDS_TERMINATION)
5960
.withBestScoreLimit(HardSoftLongScore.ofSoft(threshold.longValue()).toString());
6061
return new SolverConfig()
62+
.withPreviewFeature(PreviewFeature.DECLARATIVE_SHADOW_VARIABLES)
6163
.withSolutionClass(VehicleRoutingSolution.class)
6264
.withEntityClasses(Vehicle.class, Customer.class, TimeWindowedCustomer.class)
6365
.withConstraintProviderClass(VehicleRoutingConstraintProvider.class)

src/main/java/ai/timefold/solver/benchmarks/examples/vehiclerouting/domain/timewindowed/TimeWindowedCustomer.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.Customer;
44
import ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.location.Location;
5-
import ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.timewindowed.solver.ArrivalTimeUpdatingVariableListener;
65
import ai.timefold.solver.core.api.domain.entity.PlanningEntity;
76
import ai.timefold.solver.core.api.domain.variable.ShadowVariable;
7+
import ai.timefold.solver.core.preview.api.domain.variable.declarative.ShadowSources;
88

99
import com.fasterxml.jackson.annotation.JsonIgnore;
1010

@@ -68,8 +68,7 @@ public void setServiceDuration(long serviceDuration) {
6868
*/
6969
// Arguable, to adhere to API specs (although this works), nextCustomer should also be a source,
7070
// because this shadow must be triggered after nextCustomer (but there is no need to be triggered by nextCustomer)
71-
@ShadowVariable(variableListenerClass = ArrivalTimeUpdatingVariableListener.class, sourceVariableName = "vehicle")
72-
@ShadowVariable(variableListenerClass = ArrivalTimeUpdatingVariableListener.class, sourceVariableName = "previousCustomer")
71+
@ShadowVariable(supplierName = "arrivalTimeSupplier")
7372
public Long getArrivalTime() {
7473
return arrivalTime;
7574
}
@@ -78,6 +77,31 @@ public void setArrivalTime(Long arrivalTime) {
7877
this.arrivalTime = arrivalTime;
7978
}
8079

80+
@ShadowSources({ "vehicle", "previousCustomer.arrivalTime" })
81+
public Long arrivalTimeSupplier() {
82+
if (vehicle == null) {
83+
return null;
84+
}
85+
86+
Long departureTime;
87+
if (previousCustomer == null) {
88+
departureTime = ((TimeWindowedDepot) vehicle.getDepot()).getMinStartTime();
89+
} else {
90+
departureTime = getPreviousCustomer().getDepartureTime();
91+
}
92+
93+
if (departureTime == null) {
94+
return null;
95+
}
96+
97+
return departureTime + getDistanceFromPreviousStandstill();
98+
}
99+
100+
@Override
101+
public TimeWindowedCustomer getPreviousCustomer() {
102+
return (TimeWindowedCustomer) super.getPreviousCustomer();
103+
}
104+
81105
// ************************************************************************
82106
// Complex methods
83107
// ************************************************************************

src/main/java/ai/timefold/solver/benchmarks/examples/vehiclerouting/domain/timewindowed/solver/ArrivalTimeUpdatingVariableListener.java

-107
This file was deleted.

src/main/java/ai/timefold/solver/benchmarks/micro/scoredirector/problems/AbstractProblem.java

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import ai.timefold.solver.core.api.solver.SolverFactory;
1414
import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig;
1515
import ai.timefold.solver.core.config.solver.EnvironmentMode;
16+
import ai.timefold.solver.core.config.solver.PreviewFeature;
1617
import ai.timefold.solver.core.config.solver.SolverConfig;
1718
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
1819
import ai.timefold.solver.core.impl.localsearch.DefaultLocalSearchPhase;
@@ -98,6 +99,7 @@ private Solution_ readOriginalSolution() {
9899
protected MoveRepository<Solution_> buildMoveRepository(SolutionDescriptor<Solution_> solutionDescriptor) {
99100
// Build the top-level local search move selector as the solver would've built it.
100101
var solverConfig = new SolverConfig()
102+
.withPreviewFeature(PreviewFeature.DECLARATIVE_SHADOW_VARIABLES)
101103
.withEnvironmentMode(EnvironmentMode.PHASE_ASSERT)
102104
.withSolutionClass(solutionDescriptor.getSolutionClass())
103105
.withEntityClasses(solutionDescriptor.getEntityClassSet().toArray(new Class[0]))

src/main/java/ai/timefold/solver/benchmarks/micro/scoredirector/problems/VehicleRoutingProblem.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package ai.timefold.solver.benchmarks.micro.scoredirector.problems;
22

3+
import java.util.EnumSet;
4+
35
import ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.Customer;
46
import ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.Vehicle;
57
import ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.VehicleRoutingSolution;
@@ -11,6 +13,7 @@
1113
import ai.timefold.solver.benchmarks.micro.scoredirector.Example;
1214
import ai.timefold.solver.benchmarks.micro.scoredirector.ScoreDirectorType;
1315
import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig;
16+
import ai.timefold.solver.core.config.solver.PreviewFeature;
1417
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
1518
import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO;
1619

@@ -34,8 +37,8 @@ protected ScoreDirectorFactoryConfig buildScoreDirectorFactoryConfig(ScoreDirect
3437

3538
@Override
3639
protected SolutionDescriptor<VehicleRoutingSolution> buildSolutionDescriptor() {
37-
return SolutionDescriptor.buildSolutionDescriptor(VehicleRoutingSolution.class, Vehicle.class, Customer.class,
38-
TimeWindowedCustomer.class);
40+
return SolutionDescriptor.buildSolutionDescriptor(EnumSet.of(PreviewFeature.DECLARATIVE_SHADOW_VARIABLES),
41+
VehicleRoutingSolution.class, Vehicle.class, Customer.class, TimeWindowedCustomer.class);
3942
}
4043

4144
@Override

src/main/resources/ai/timefold/solver/benchmarks/examples/vehiclerouting/vehicleRoutingSolverConfig.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<!--<environmentMode>FULL_ASSERT</environmentMode>-->
66
<!-- To solve faster by saturating multiple CPU cores -->
77
<!--<moveThreadCount>AUTO</moveThreadCount>-->
8-
8+
<enablePreviewFeature>DECLARATIVE_SHADOW_VARIABLES</enablePreviewFeature>
99
<solutionClass>ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.VehicleRoutingSolution</solutionClass>
1010
<entityClass>ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.Vehicle</entityClass>
1111
<entityClass>ai.timefold.solver.benchmarks.examples.vehiclerouting.domain.Customer</entityClass>

src/test/java/ai/timefold/solver/benchmarks/competitive/cvrplib/CVRPLIBDatasetTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ void runConstructionHeuristics(CVRPLIBDataset dataset) {
2424
var config = CVRPLIBConfiguration.ENTERPRISE_EDITION.getSolverConfig(dataset);
2525
var phases = config.getPhaseConfigList().subList(0, 1); // Keep only CH.
2626
config.setPhaseConfigList(phases);
27+
config.setMoveThreadCount("1"); // So that the tests can efficiently run in parallel, while still being MT.
2728

2829
var solverFactory = SolverFactory.create(config);
2930
var solver = solverFactory.buildSolver();

src/test/java/ai/timefold/solver/benchmarks/competitive/tsplib95/TSPLIBDatasetTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ void runConstructionHeuristics(TSPLIBDataset dataset) {
2424
var config = TSPLIBConfiguration.ENTERPRISE_EDITION.getSolverConfig(dataset);
2525
var phases = config.getPhaseConfigList().subList(0, 1); // Keep only CH.
2626
config.setPhaseConfigList(phases);
27+
config.setMoveThreadCount("1"); // So that the tests can efficiently run in parallel, while still being MT.
2728

2829
var solverFactory = SolverFactory.create(config);
2930
var solver = solverFactory.buildSolver();

0 commit comments

Comments
 (0)