我正在尝试使用 Optaplanner 并使用 Optaplanner spring-boot-starter 创建了一个简单的项目来解决目前只有一个硬约束的简单 VRP 问题。
我的领域模型非常简单,一个具有体积、重量容量的车辆类和具有体积和重量值的订单类。
@Getter
@Setter
@ToString
@PlanningEntity
public class Vehicle {
@PlanningId
private Long id;
private int weightCapacity;
private int volumeCapacity;
private double guaranteeCheck;
@PlanningListVariable(valueRangeProviderRefs = "orderRange")
private List<Order> orders;
public Vehicle() {
}
public Vehicle(long id, int volume, int weight) {
this.id = id;
this.volumeCapacity=volume;
this.weightCapacity=weight;
}
public int getTotalVolumeCapacity() {
int totalDemand = 0;
for (Order order : orders) {
totalDemand += order.getVolume();
}
return totalDemand;
}
public int getTotalWeightCapacity(){
int totalDemand = 0;
for (Order order : orders) {
totalDemand += order.getWeight();
}
return totalDemand;
}
}
@Getter
@Setter
@ToString
public class Order {
private Long id;
private String reference;
private double weight;
private double volume;
// private double amount;
private Location location;
public Order() {
}
public Order(long id, int volume, int weight, Location location) {
this.id = id;
this.reference = this.id.toString();
this.volume = volume;
this.weight = weight;
this.location = location;
}
}
我创建了一个 vrp 解决方案类如下:
@NoArgsConstructor
@AllArgsConstructor
@PlanningSolution
@Getter
@Setter
public class VehicleRoutingSolution {
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "orderRange")
private List<Order> orders;
@ValueRangeProvider
@PlanningEntityCollectionProperty
private List<Vehicle> vehicles;
@PlanningScore
private HardSoftLongScore score;
public VehicleRoutingSolution(List<Order> orders, List<Vehicle> vehicles) {
this.orders = orders;
this.vehicles = vehicles;
}
}
这里的问题很简单,我试图根据如下基于 quarkus quickstart 官方示例制定的硬约束为车辆分配订单:
public class VehicleRoutingConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[]{
// Hard constraints
vehicleCapacity(constraintFactory),
// Soft constraints
};
}
protected Constraint vehicleCapacity(ConstraintFactory factory) {
return factory.forEach(Vehicle.class)
.filter(vehicle -> vehicle.getTotalVolumeCapacity() > vehicle.getVolumeCapacity())
.penalizeLong(HardSoftLongScore.ONE_HARD,
vehicle -> vehicle.getTotalVolumeCapacity() - vehicle.getVolumeCapacity())
.asConstraint("vehicleCapacity");
}
}
当我启动求解器以在我的数据集上找到解决方案时,它显示以下问题:
Cannot invoke "java.util.List.size()" because the return value of "org.optaplanner.core.impl.domain.variable.descriptor.ListVariableDescriptor.getListVariable(Object)" is null
我正在考虑我的解决方案变量之一的非初始化是否正确,或者最新版本(自 4 月 22 日起可用)确实发生了一些变化。
求解器执行代码如下:
List<Order> orders = createOrdersDataSet();
List<Vehicle> vehicles = createVehicleDataSet();
UUID problemId = UUID.randomUUID();
VehicleRoutingSolution problem = new VehicleRoutingSolution(orders, vehicles);
// Submit the problem to start solving
SolverJob<VehicleRoutingSolution, UUID> solverJob = solverManager.solve(problemId, problem);
VehicleRoutingSolution solution;
try {
// Wait until the solving ends
solution = solverJob.getFinalBestSolution();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException("Solving failed.", e);
}
log.info("Your best score is: {}", solution.getScore());
事实上,错误信息是不言自明的——你的列表变量是
null
。如果你像这样声明你的计划变量,这个问题就会消失:
@PlanningListVariable(valueRangeProviderRefs = "orderRange")
private List<Order> orders = new ArrayList<>();
或者,您可以在启动求解器之前在运行时设置字段。