我正在尝试通过Spring Boot在Java中使用Optaplanner解决TSP。我想获得穿过各个DeliveryLocation的最佳路线,并且遇到以下异常:
Caused by: java.lang.IllegalStateException: The supply (ExternalizedSingletonInverseVariableSupply(previousStandstill)) is corrupted, because the entity (DeliveryLocation{location=location 2}) for sourceVariable (previousStandstill) cannot be retracted: the entity was never inserted for that value (DeliveryLocation{location=location 3}).
at org.optaplanner.core.impl.domain.variable.inverserelation.ExternalizedSingletonInverseVariableSupply.retract(ExternalizedSingletonInverseVariableSupply.java:116)
at org.optaplanner.core.impl.domain.variable.inverserelation.ExternalizedSingletonInverseVariableSupply.beforeVariableChanged(ExternalizedSingletonInverseVariableSupply.java:74)
at org.optaplanner.core.impl.domain.variable.listener.support.VariableListenerSupport.beforeVariableChanged(VariableListenerSupport.java:173)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.beforeVariableChanged(AbstractScoreDirector.java:432)
at org.optaplanner.core.impl.score.director.incremental.IncrementalScoreDirector.beforeVariableChanged(IncrementalScoreDirector.java:158)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.changeVariableFacade(AbstractScoreDirector.java:445)
at org.optaplanner.core.impl.heuristic.selector.move.generic.chained.ChainedSwapMove.doMoveOnGenuineVariables(ChainedSwapMove.java:86)
at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:36)
at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:31)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:190)
at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:132)
at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:116)
DeliveryLocation看起来像这样:
@PlanningEntity
public class DeliveryLocation implements Standstill {
private RoadLocation location;
//Planning variable: changes during planning between score calculation;
@JsonIgnore
private Standstill previousStandstill;
public DeliveryLocation() {
}
public DeliveryLocation(RoadLocation location,
Standstill previousStandstill) {
this.location = location;
this.previousStandstill = previousStandstill;
}
@Override
public RoadLocation getLocation() {
return location;
}
public void setLocation(RoadLocation location) {
this.location = location;
}
@PlanningVariable(valueRangeProviderRefs = {"deliveryLocationRange",
"sourceRange"}, graphType = PlanningVariableGraphType.CHAINED,
strengthWeightFactoryClass = SourceDistanceStandstillStrengthWeightFactory.class)
public Standstill getPreviousStandstill() {
return previousStandstill;
}
public void setPreviousStandstill(Standstill previousStandstill) {
this.previousStandstill = previousStandstill;
}
// ********************************
// Complex Methods
// ********************************
public long getDistanceFromPreviousStandstill() {
if (previousStandstill == null) {
return 0L;
}
return getDistanceFrom(previousStandstill);
}
public long getDistanceFrom(Standstill standstill) {
return standstill.getLocation().getDistanceTo(location);
}
@Override
public long getDistanceTo(Standstill standstill) {
if (standstill == null) {
return 0L;
}
return location.getDistanceTo(standstill.getLocation());
}
@Override
public String toString() {
return "DeliveryLocation{" + "location=" + location.getName() + "}";
}
}
目前,我正在手动加载每个位置到其他位置的距离图:
String name = "name";
Source source = new Source(new RoadLocation(23.614895, 46.782025));
source.getLocation().setName("source");
String measureUnit = "km";
List<DeliveryLocation> deliveryLocationList = new ArrayList<>();
DeliveryLocation location1 = new DeliveryLocation(new RoadLocation(23.615318, 46.788198),
null);
location1.setPreviousStandstill(location1);
location1.getLocation().setName("location 1");
DeliveryLocation location2 = new DeliveryLocation(new RoadLocation(23.610762, 46.795077),
null);
location2.getLocation().setName("location 2");
DeliveryLocation location3 = new DeliveryLocation(new RoadLocation(23.607437, 46.80074),
null);
location3.getLocation().setName("location 3");
Map<RoadLocation, Double> travelDistanceMap = new HashMap<>();
// source distance map
travelDistanceMap.put(source.getLocation(), 0.0);
travelDistanceMap.put(location1.getLocation(), 943.0);
travelDistanceMap.put(location2.getLocation(), 2025.0);
travelDistanceMap.put(location3.getLocation(), 2644.0);
source.getLocation().setTravelDistanceMap(new HashMap<>(travelDistanceMap));
travelDistanceMap.clear();
// location1 distance map
travelDistanceMap.put(source.getLocation(), 980.0);
travelDistanceMap.put(location1.getLocation(), 0.0);
travelDistanceMap.put(location2.getLocation(), 1081.0);
travelDistanceMap.put(location3.getLocation(), 1700.0);
location1.getLocation().setTravelDistanceMap(new HashMap<>(travelDistanceMap));
travelDistanceMap.clear();
//location2 distance map
travelDistanceMap.put(source.getLocation(), 2042.0);
travelDistanceMap.put(location1.getLocation(), 1062.0);
travelDistanceMap.put(location2.getLocation(), 0.0);
travelDistanceMap.put(location3.getLocation(), 752.0);
location2.getLocation().setTravelDistanceMap(new HashMap<>(travelDistanceMap));
travelDistanceMap.clear();
//location3 distance map
travelDistanceMap.put(source.getLocation(), 2687.0);
travelDistanceMap.put(location1.getLocation(), 1707.0);
travelDistanceMap.put(location2.getLocation(), 734.0);
travelDistanceMap.put(location3.getLocation(), 0.0);
location3.getLocation().setTravelDistanceMap(new HashMap<>(travelDistanceMap));
List<RoadLocation> locationList = new ArrayList<>();
locationList.add(location3.getLocation());
locationList.add(location2.getLocation());
locationList.add(location1.getLocation());
deliveryLocationList.add(location1);
deliveryLocationList.add(location2);
deliveryLocationList.add(location3);
BestRoute problem = new BestRoute(measureUnit, name, locationList, source,
deliveryLocationList);
UUID problemId = UUID.randomUUID();
BestRoute bestRoute;
SolverJob<BestRoute, UUID> solverJob = solverManager.solve(problemId, problem);
try {
bestRoute = solverJob.getFinalBestSolution();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException("Solving failed", e);
}
return bestRoute;
}
这是我的BestRoute类(计划解决方案):
@PlanningSolution
public class BestRoute implements Serializable {
@JsonProperty(value = "unit-of-measurement")
protected String distanceUnitOfMeasurement;
private String name;
@JsonProperty(value = "location-list")
private List<RoadLocation> locationList;
private Source source;
@JsonProperty(value = "delivery-location-list")
private List<DeliveryLocation> deliveryLocationList;
private SimpleLongScore score;
public BestRoute() {
}
public BestRoute(String distanceUnitOfMeasurement,
String name,
List<RoadLocation> locationList,
Source source,
List<DeliveryLocation> deliveryLocationList) {
this.distanceUnitOfMeasurement = distanceUnitOfMeasurement;
this.name = name;
this.locationList = locationList;
this.source = source;
this.deliveryLocationList = deliveryLocationList;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ProblemFactCollectionProperty
public List<RoadLocation> getLocationList() {
return locationList;
}
public void setLocationList(List<RoadLocation> locationList) {
this.locationList = locationList;
}
@ProblemFactProperty
public Source getSource() {
return source;
}
public void setSource(Source source) {
this.source = source;
}
@PlanningEntityCollectionProperty
@ValueRangeProvider(id = "deliveryLocationRange")
public List<DeliveryLocation> getDeliveryLocationList() {
return deliveryLocationList;
}
public void setDeliveryLocationList(List<DeliveryLocation> deliveryLocationList) {
this.deliveryLocationList = deliveryLocationList;
}
@PlanningScore
public SimpleLongScore getScore() {
return score;
}
public void setScore(SimpleLongScore score) {
this.score = score;
}
// ************************************************************************
// Complex methods
// ************************************************************************
@ValueRangeProvider(id = "sourceRange")
public List<Source> getSourceRange() {
return Collections.singletonList(source);
}
public String getDistanceString(NumberFormat numberFormat) {
if (score == null) {
return null;
}
long distance = -score.getScore();
if (distanceUnitOfMeasurement == null) {
return numberFormat.format(((double) distance) / 1000.0);
}
switch (distanceUnitOfMeasurement) {
case "sec": {
long hours = distance / 3600000;
long minutes = distance % 3600000 / 60000;
long seconds = distance % 60000 / 1000;
long milliseconds = distance % 1000;
return hours + "h " + minutes + "m " + seconds + "s " + milliseconds + "ms";
}
case "km":
case "meter": {
long km = distance / 1000;
long meter = distance % 1000;
return km + "km " + meter + "m";
}
default:
return numberFormat.format(((double) distance) / 1000.0) + " " +
distanceUnitOfMeasurement;
}
}
}
如果需要其他代码段,请询问我。
谢谢您的帮助 :)
最佳答案
临时打开environmentMode FULL_ASSERT
(此后将其关闭)。
我怀疑输入数据已损坏,FULL_ASSERT可能会更清楚地检测到它。如果安访问A,B,C,那么如果B.previous = A,那么A.next = B,则必须。我怀疑您输入的数据中,A.next不是B。