我正在尝试通过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。

10-07 12:04