使用 TimeWindows 在 VehicleRouting 中忽略规则
Rules are Ignored in VehicleRouting with TimeWindows
我使用 Optaplanner 的示例来计算带有 Timewindows 的 VRP 的最佳路线,并对代码进行了一些修改以满足我们的需求。
我们有技术人员在客户处工作。
每个技术人员都是自己的 "depot",因为他们可以从不同的位置开始。
因此每个仓库只有一辆车。
对于给定的示例,这非常有效。
现在我们要添加一条规则,只有具备所需资格的技术人员才能被安排给客户(例如,只允许部分技术人员进行钻孔)
客户和技术人员的资质都需要匹配。
我们在drl文件中添加了一条规则:
rule "qulificationcorrect"
when
$vehicle : Vehicle($vehicleQualifications:qualifications)
$customer : Customer(vehicle==$vehicle,$customerQualification:qualifications)
$test : Integer(RuleHelper.qualified($vehicleQualifications,$customerQualification)<1)
then
System.out.println(RuleHelper.qualified($depotQualifications,$customerQualification));
scoreHolder.addHardConstraintMatch(kcontext, -100L);
Rulehelper 看起来像这样:
public class RuleHelper
{
public static int qualified(Integer[] vehicle, Integer[] customer)
{
if (customer == null || customer.length < 1)
{
System.out.println("CUSTOMER EMPTY");
return 1;
}
if (vehicle == null || vehicle.length < 1)
{
System.out.println("VEHICLE EMPTY");
return 0;
}
List<Integer> vehicleList = Arrays.asList(vehicle);
List<Integer> customerList = Arrays.asList(customer);
System.out.println("VehicleList=" + vehicleList + " CustomerList" + customerList + " rule = " + vehicleList.containsAll(customerList));
return vehicleList.containsAll(customerList) ? 1 : 0;
}
}
但是当我查看我的解决方案时,它没有硬分 (0),这是输出:
{
"solutions" : [
{
"employee" : {
"name" : "Technician 1",
"qualifications" : [
1,
2,
5,
999
],
"lat" : 49.70103,
"lng" : 8.32404,
"employeeId" : 31,
"startTime" : 480,
"endTime" : 1170,
"maxOrderCount" : 0,
"solutionId" : 1
},
"orders" : [
{
"qualifications" : [
1000
],
"transmittedStart" : 435,
"transmittedEnd" : 660,
"startTime" : 480,
"endTime" : 540,
"lat" : 49.96685,
"lng" : 8.0308,
"orderId" : 638411,
"fixed" : false,
"calculatedStart" : 522,
"calculatedEnd" : 567,
"solutionId" : 4
},
{
"qualifications" : [
999
],
"transmittedStart" : 615,
"transmittedEnd" : 840,
"startTime" : 660,
"endTime" : 720,
"lat" : 49.89585,
"lng" : 8.0809,
"orderId" : 637001,
"fixed" : false,
"calculatedStart" : 583,
"calculatedEnd" : 660,
"solutionId" : 3
}
]
},
{
"employee" : {
"name" : "Technician 2",
"qualifications" : [
3,
1000
],
"lat" : 49.70103,
"lng" : 8.32404,
"employeeId" : 264,
"startTime" : 480,
"endTime" : 1170,
"maxOrderCount" : 0,
"solutionId" : 2
},
"orders" : [ ]
}
]
}
虽然第一个技术员的资格只有1,2,5,999 而不是1000,但基本上第一个技术员获得了两个订单(客户)。第二个技术员没有获得订单,而是获得了资格1000 的订单。
我希望这些信息足以让别人告诉我哪里出了问题...
国王问好
编辑:感谢 Geoffrey De Smet 的帮助
我将规则更改为
rule "qualificationcorrect"
when
$customer : Customer(hasAllQualifications() == false)
then
scoreHolder.addHardConstraintMatch(kcontext, -100L);
结束
并且还在客户中实施了 hasAllQualifications:
public boolean hasAllQualifications()
{
if (qualifications.length < 1)
{
return true;
}
if (vehicle == null || vehicle.getQualifications() == null || vehicle.getQualifications().length < 1)
{
return false;
}
List<Integer> vehicleList = Arrays.asList(vehicle.getQualifications());
List<Integer> customerList = Arrays.asList(this.getQualifications());
return vehicleList.containsAll(customerList);
}
现在解决方案显示了预期的行为
再次感谢您的快速回答
这可能与 "incremental score calculation" 的工作方式有关(它依赖于调用 modify()
流口水事实)。 打开 environmentMode
FULL_ASSERT
- 如果它抛出分数损坏异常,这就是问题所在。
如果是这样,让我们看看你的规则
when
$vehicle : Vehicle($vehicleQualifications:qualifications)
// When the Customer.vehicle changes, the rule engine gets notified
$customer : Customer(vehicle==$vehicle,$customerQualification:qualifications)
// That should retrigger the evaluation of this line every time a customer's vehicle changes
$test : Integer(RuleHelper.qualified($vehicleQualifications,$customerQualification)<1)
在任何情况下,您可能都想像这样重写它:
when
$customer : Customer(hasAllQualifications() == false)
然后 Customer.hasAllQualifications()
看看 Customer.vehicle
我使用 Optaplanner 的示例来计算带有 Timewindows 的 VRP 的最佳路线,并对代码进行了一些修改以满足我们的需求。
我们有技术人员在客户处工作。 每个技术人员都是自己的 "depot",因为他们可以从不同的位置开始。 因此每个仓库只有一辆车。
对于给定的示例,这非常有效。
现在我们要添加一条规则,只有具备所需资格的技术人员才能被安排给客户(例如,只允许部分技术人员进行钻孔)
客户和技术人员的资质都需要匹配。 我们在drl文件中添加了一条规则:
rule "qulificationcorrect"
when
$vehicle : Vehicle($vehicleQualifications:qualifications)
$customer : Customer(vehicle==$vehicle,$customerQualification:qualifications)
$test : Integer(RuleHelper.qualified($vehicleQualifications,$customerQualification)<1)
then
System.out.println(RuleHelper.qualified($depotQualifications,$customerQualification));
scoreHolder.addHardConstraintMatch(kcontext, -100L);
Rulehelper 看起来像这样:
public class RuleHelper
{
public static int qualified(Integer[] vehicle, Integer[] customer)
{
if (customer == null || customer.length < 1)
{
System.out.println("CUSTOMER EMPTY");
return 1;
}
if (vehicle == null || vehicle.length < 1)
{
System.out.println("VEHICLE EMPTY");
return 0;
}
List<Integer> vehicleList = Arrays.asList(vehicle);
List<Integer> customerList = Arrays.asList(customer);
System.out.println("VehicleList=" + vehicleList + " CustomerList" + customerList + " rule = " + vehicleList.containsAll(customerList));
return vehicleList.containsAll(customerList) ? 1 : 0;
}
}
但是当我查看我的解决方案时,它没有硬分 (0),这是输出:
{
"solutions" : [
{
"employee" : {
"name" : "Technician 1",
"qualifications" : [
1,
2,
5,
999
],
"lat" : 49.70103,
"lng" : 8.32404,
"employeeId" : 31,
"startTime" : 480,
"endTime" : 1170,
"maxOrderCount" : 0,
"solutionId" : 1
},
"orders" : [
{
"qualifications" : [
1000
],
"transmittedStart" : 435,
"transmittedEnd" : 660,
"startTime" : 480,
"endTime" : 540,
"lat" : 49.96685,
"lng" : 8.0308,
"orderId" : 638411,
"fixed" : false,
"calculatedStart" : 522,
"calculatedEnd" : 567,
"solutionId" : 4
},
{
"qualifications" : [
999
],
"transmittedStart" : 615,
"transmittedEnd" : 840,
"startTime" : 660,
"endTime" : 720,
"lat" : 49.89585,
"lng" : 8.0809,
"orderId" : 637001,
"fixed" : false,
"calculatedStart" : 583,
"calculatedEnd" : 660,
"solutionId" : 3
}
]
},
{
"employee" : {
"name" : "Technician 2",
"qualifications" : [
3,
1000
],
"lat" : 49.70103,
"lng" : 8.32404,
"employeeId" : 264,
"startTime" : 480,
"endTime" : 1170,
"maxOrderCount" : 0,
"solutionId" : 2
},
"orders" : [ ]
}
]
}
虽然第一个技术员的资格只有1,2,5,999 而不是1000,但基本上第一个技术员获得了两个订单(客户)。第二个技术员没有获得订单,而是获得了资格1000 的订单。
我希望这些信息足以让别人告诉我哪里出了问题...
国王问好
编辑:感谢 Geoffrey De Smet 的帮助 我将规则更改为
rule "qualificationcorrect"
when
$customer : Customer(hasAllQualifications() == false)
then
scoreHolder.addHardConstraintMatch(kcontext, -100L);
结束
并且还在客户中实施了 hasAllQualifications:
public boolean hasAllQualifications()
{
if (qualifications.length < 1)
{
return true;
}
if (vehicle == null || vehicle.getQualifications() == null || vehicle.getQualifications().length < 1)
{
return false;
}
List<Integer> vehicleList = Arrays.asList(vehicle.getQualifications());
List<Integer> customerList = Arrays.asList(this.getQualifications());
return vehicleList.containsAll(customerList);
}
现在解决方案显示了预期的行为 再次感谢您的快速回答
这可能与 "incremental score calculation" 的工作方式有关(它依赖于调用 modify()
流口水事实)。 打开 environmentMode
FULL_ASSERT
- 如果它抛出分数损坏异常,这就是问题所在。
如果是这样,让我们看看你的规则
when
$vehicle : Vehicle($vehicleQualifications:qualifications)
// When the Customer.vehicle changes, the rule engine gets notified
$customer : Customer(vehicle==$vehicle,$customerQualification:qualifications)
// That should retrigger the evaluation of this line every time a customer's vehicle changes
$test : Integer(RuleHelper.qualified($vehicleQualifications,$customerQualification)<1)
在任何情况下,您可能都想像这样重写它:
when
$customer : Customer(hasAllQualifications() == false)
然后 Customer.hasAllQualifications()
看看 Customer.vehicle