Search code examples
optaplannertimefold

How to enable nullable InverseRelationShadowVariable or prevent to assign a value to a PlanningListVariable


I have to plan a list of orders into machines.

The relation order machine is by item code. If the machine has the order item code the machine can mange the order so the order can be assigned to the machine.

It could happen that some orders have an item code that don't match with any machine.

This is my code:

@PlanningEntity
public class Machine {

    private List<Order> m_plannedOrders;
...
    @PlanningListVariable
    public List<Order> getPlannedOrders() {
        return m_plannedOrders;
    }

    public void setPlannedOrders(List<Order> orders) {
        m_plannedOrders = orders;
    }
...
    public boolean canManage(String itemCode) {
        return true if this machine can handle the order by code.
    }
}

@PlanningEntity
public class Order {
    private Machine m_machine;
...
    @InverseRelationShadowVariable(sourceVariableName = "plannedOrders")
    public Machine getMachine() {
        return m_machine;
    }

    public void setMachine(Machine machine) {
        m_machine = machine;
    }

    public boolean isValidMachine() {
        boolean retVal = false;

        if (m_machine != null) {
            retVal = m_machine.canManage(m_itemCode);
        }

        return retVal;
    }
}

@PlanningSolution
public class MachineOrdersPlanning {
...
    @PlanningEntityCollectionProperty
    public List<Machine> getMachines() {
        return m_machines;
    }

    @ValueRangeProvider
    @ProblemFactCollectionProperty
    public List<Order> getOrders() {
        return m_orders;
    }

    @PlanningScore
    public HardSoftScore getScore() {
        return m_score;
    }
}

The constraints I use is:

    private Constraint orderOnCorrectMachine(ConstraintFactory constraintFactory) {
        return constraintFactory
                .forEach(Order.class)
                .filter(order -> order.getMachine() == null || !order.isValidMachine())
                .penalize(HardSoftScore.ONE_HARD).asConstraint("Order assigned to valid machine");
    }

I tried also without order.getMachine() == null ||

The problem is that for instante for Machine 1 the output is

  Machine: 1
    Order 29 canManaged? true
    Order 18 canManaged? true
    Order 16 canManaged? true
    Order 15 canManaged? true
    Order 14 canManaged? true
    Order 9 canManaged? true
    Order 8 canManaged? true
    Order 6 canManaged? false
    Order 4 canManaged? true

Order 6 is assigned to a machine because the planning variable cannot be null. But the InverseRelationshadowVariable does not accept nullable in annotation.

What I desire is that all unmanageable orders will not assigned. Do I have to work with nullable in annotation somewhere or I have to change the constraint or what else?

Any help is appreciated.


Solution

  • Simple (may be not optimal) approach will be to create an extra "dummy" machine capable to handle any type of order and work with HardMediumScore and two constraints:

    • Hard score constraint same as you have to penalize for incompatible order assigned
    • Medium score constraint to penalize simply for amount of orders assigned to "dummy" machine

    You can still use the soft score for you soft rules.