Marpec
Marpec

Reputation: 11

Drools rule for vehicle capacity in Optaplanner

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

Answers (2)

Marpec
Marpec

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

Philip Harding
Philip Harding

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

Related Questions