OptaPlanner/TimeFold groupBy 与列表只有一个元素

问题描述 投票:0回答:1

这是这个问题的后续。

我有两个这样的 PlanningEntity 类:

@PlanningEntity
class Location {
    private static long nextId = 0;    
    private long id = nextId++;

    @PlanningVariable
    Coordinates coordinates;
}

@PlanningEntity
class LocationList {
    private static long nextId = 0;    
    private long id = nextId++;

    @ShadowVariable(
            variableListenerClass = LocationListUpdatingVariableListener.class,
            sourceVariableName = "coordinates",
            sourceEntityClass = Location.class)
    List<Location> locations = ArrayList<>();
    int weight;
}

解决方案类如下所示:

@PlanningSolution
class Solution {
    @PlanningEntityCollectionProperty
    List<Location> locations;

    @PlanningEntityCollectionProperty
    List<LocationList> locationLists;

    @ValueRangeProvider
    @ProblemFactCollectionProperty
    List<Coordinates> possibleCoordinates;
}

影子变量更新器如下所示:

class LocationListUpdatingVariableListener implements VariableListener<Solution, Location> {

    @Override
    public void beforeEntityAdded(ScoreDirector<Solution> scoreDirector, Location location) {
        // do nothing
    }

    @Override
    public void afterEntityAdded(ScoreDirector<Solution> scoreDirector, Location location) {
        updateLocationLists(scoreDirector, location);
    }

    @Override
    public void beforeEntityRemoved(ScoreDirector<Solution> scoreDirector, Location location) {
        // do nothing
    }

    @Override
    public void afterEntityRemoved(ScoreDirector<Solution> scoreDirector, Location location) {
        updateLocationLists(scoreDirector, location);
    }

    @Override
    public void beforeVariableChanged(ScoreDirector<Solution> scoreDirector, Location location) {
        // do nothing
    }

    @Override
    public void afterVariableChanged(ScoreDirector<Solution> scoreDirector, Location location) {
        updateLocationLists(scoreDirector, location);
    }
    
    protected void updateLocationLists(ScoreDirector<Solution> scoreDirector, Location location) {
        Solution solution = scoreDirector.getWorkingSolution();
        
        solution.locationLists.forEach(locationList -> {
            
            for(int i = 0; i < locationList.locations.size(); i++) {
                var locationListLocation = locationList.locations.get(i);
                
                if(locationListLocation.id == location.id) {
                    scoreDirector.beforeVariableChanged(locationList, "locations");
                    locationList.locations.set(i, location);
                    scoreDirector.afterVariableChanged(locationList, "locations");
                }
            }
        });
        
    }
}

我有这样的约束:

Constraint myConstraint(ConstraintFactory constraintFactory) {
    return constraintFactory.forEach(LocationList.class)
           .filter(locationList -> locationList.locations.size() == 2)
           .join(Location.class)
           .filter((locationList, location) -> locationList.locations.contains(location))
           .groupby((locationList, location) -> locationList, ConstraintCollectors.toList((locationList, location) -> child))
           .penalize(HardSoftScore.ONE_HARD, (locationList, locationsInList) -> weightedDistance(locationList.weight, locationsInList))
           .asConstraint("Shorter total distance is better");
}

weightedDistance
的作用类似于将指定坐标之间的所有距离相加并乘以权重。它实际上做的事情更复杂,但要点是它需要同时处理所有位置 - 它不能一次处理一个。

我过滤 2 个元素的部分不是实际约束的一部分。我只是把它放在那里来演示问题——代码让它过去了,但不知何故仍然只在

locationsInList
中得到 1 个位置。

我实际上可以直接从

locationList
中提取位置并且它可以工作,但我担心因为
locationsInList
中没有正确的位置,因此解算器无法正确跟踪。

optaplanner timefold
1个回答
0
投票

事实证明这是由时间折叠错误引起的:https://github.com/TimefoldAI/timefold-solver/issues/309

如果我不用蛮力运行,一切都会按预期进行。

© www.soinside.com 2019 - 2024. All rights reserved.