这是这个问题的后续。
我有两个这样的 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
中没有正确的位置,因此解算器无法正确跟踪。
事实证明这是由时间折叠错误引起的:https://github.com/TimefoldAI/timefold-solver/issues/309
如果我不用蛮力运行,一切都会按预期进行。