Reputation: 11
I am using Optaplanner for CVRP and have made some changes, namely wanted to use several dimensions for the vehicle capacities. I have set up a new class
public class Load {
protected int[] capacity;
protected int myDimension=1;}
It also has several methods for managing the data, most important being
public void add (Load otherLoad) {
if (myDimension != otherLoad.myDimension) {
throw new IllegalArgumentException("Different load dimensions (" + myDimension +" / "+ otherLoad.myDimension + ").");
} else {
for (int i=0; i < myDimension; i++) {
capacity[i]+=otherLoad.capacity[i];
}
}
}
and
public boolean fitsInto (Load otherContainer) {
if (myDimension != otherContainer.myDimension) {
throw new IllegalArgumentException("Different load dimensions (" + myDimension +" / "+ otherContainer.myDimension + ").");
} else {
for (int i=0; i < myDimension; i++) {
if (capacity[i]>otherContainer.capacity[i]) return false;
}
}
return true;
}
At the moment I am having difficulties with Drools rules, since I am totally new to this. The old rule was:
rule "vehicleCapacity"
when
$vehicle : Vehicle($capacity : capacity)
accumulate(
Customer(
vehicle == $vehicle,
$demand : demand);
$demandTotal : sum($demand);
$demandTotal > $capacity
)
then
scoreHolder.addHardConstraintMatch(kcontext, $capacity - $demandTotal);
end
I need to modify it so that it will use the Load class instead of integers, but not sure how. My lame experiment after reading some Drools documentation goes something like this
rule "vehicleCapacity"
when
$demandTotal: Load
$vehicle : Vehicle($capacity : capacity)
accumulate(
Customer(
vehicle == $vehicle,
$demand : demand);
$demandTotal.add($demand);
not ($demandTotal.fitsInto($capacity))
)
then
scoreHolder.addHardConstraintMatch(kcontext, $capacity.sumValues() - $demandTotal.sumValues());
end
Of course it doesn't go through, so I would be happy to get some hints and advice. Thank you!
Upvotes: 0
Views: 228
Reputation: 11
I wasn't able to solve the problem, so I actually just copied the capacities to int capacity1, int capacity2, and copied and modified the rules accordingly. It is neither elegant, not scalable, but it does the job so far...
Upvotes: 0
Reputation: 394
From what I understood you are trying to add more dimensions to demand
and therefore to capacity
, where the constraint is that none of the capacities of any dimension may be exceeded. To achieve this, you can simply amend Vehicle
and Customers
as follows
class Vehicle extends AbstractPersistable implements Standstill{
protected Location location;
protected List<Integer> capacities;
...
}
and
class Customer extends AbstractPersistable implements Standstill{
protected List<Integer> demands;
...
}
Then, in Drools, you modify your rule (absolutely untested; in particular, you may have to add .intValue()
after the get methods):
rule "vehicleCapacity"
when
$vehicle : Vehicle($capacity: capacities.get($loadType:lt))
accumulate(
Customer(
vehicle == $vehicle,
$demand : demands.get($loadType));
$demandTotal : sum($demand);
$demandTotal > $capacity
)
then
scoreHolder.addHardConstraintMatch(kcontext, $capacity - $demandTotal);
This will make sure that if demand exceeds capacity for any dimension, the hard score will penalise the difference. Just make sure to make all lists the same size, with the dimension $loadType
in capacities
corresponding to the same dimension in demands
.
You could also try by just by changing protected int capacity
to protected int[] capacities
in Vehicle
and analogously in Customer
with demand
, but I'm not sure you can bind to array elements in Drools. Perhaps worth a try: The Drools rule then runs so (again absolutely untested):
rule "vehicleCapacity"
when
$vehicle : Vehicle($capacity: capacities[$lt:lt])
accumulate(
Customer(
vehicle == $vehicle,
$demand : demands[lt]);
$demandTotal : sum($demand);
$demandTotal > $capacity
)
then
scoreHolder.addHardConstraintMatch(kcontext, $capacity - $demandTotal);
(then at least the .intValue()
is not necessary)
Upvotes: 1