Optaplanner Shadow Variable changed for every move, not every step

时间:2017-05-16 09:38:54

标签: optaplanner

I have a planning entity with a genuine variable

@PlanningEntity
public class PlannerTaskResourceAllocation extends AbstractDto {

...

    @PlanningVariable(valueRangeProviderRefs = "resources")
    public TaskResource getResource() {
        return resource;
    }

...

}

and one with a genuine variable and a shadow variable

@PlanningEntity
public class PlannerActivity extends AbstractEntity {

...

    @PlanningVariable(valueRangeProviderRefs = "possibleDates")
    public Integer getStartIndex() {
        return startIndex;
    }

...

    @CustomShadowVariable(variableListenerClass = TaskResourceVariableListener.class,
        sources = { @CustomShadowVariable.Source(entityClass = PlannerTaskResourceAllocation.class,
                variableName = "resource") })
    public Integer getLength() {
        return length;

and a custom variable listener that changes the length property (shadow var) of the second planning entity based on changes in the first planning entity's resource property (genuine var), and under certain conditions. my problem is is that it seems that the listener does it's change for every move, while as i understood it is supposed to do it for every step. as in if we look at the log below, there were 5 steps being tried for that move, when only one of them was accepted, but the change on the shadow variable happened 5 times. to simplify testing it i made a simpler method in the listener that simply extends the length by one:

public class TaskResourceVariableListener implements VariableListener<PlannerTaskResourceAllocation> {

    @Override
    public void afterEntityAdded(ScoreDirector scoreDirector, PlannerTaskResourceAllocation entity) {
    lengthenActivities(scoreDirector, entity);
    }

    @Override
    public void beforeVariableChanged(ScoreDirector scoreDirector, PlannerTaskResourceAllocation entity) {
    }

    @Override
    public void afterVariableChanged(ScoreDirector scoreDirector, PlannerTaskResourceAllocation entity) {
    lengthenActivities(scoreDirector, entity);
    }

    private void lengthenActivities(ScoreDirector scoreDirector, PlannerTaskResourceAllocation allocation) {
        for (PlannerActivity plannerActivity : ((ActivitySolution) scoreDirector.getWorkingSolution())
                .getActivities()) {
            scoreDirector.beforeVariableChanged(plannerActivity, "length");
            plannerActivity.setLength(plannerActivity.getLength() + 1);
            scoreDirector.afterVariableChanged(plannerActivity, "length");
            break;
        }
    } 

so between these two steps, the activity was lengthened 5 times, instead of one.

log output:

2017-05-15 16:30:33.834 DEBUG   --- [           main] o.o.c.i.l.DefaultLocalSearchPhase        :     LS step (3971), time spent (56826), score (0hard/-15120soft),     best score (0hard/-15070soft), accepted/selected move count (1/1), picked move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=47, created=2017-05-15 16:29:34.18, lastModified=2017-05-15 16:29:34.18), resource=dave2 daveson (ATV Driver)) {dave2 daveson (ATV Driver) -> dave daveson (ATV Driver)}).
2017-05-15 16:30:33.834 TRACE   --- [           main] o.o.c.i.l.decider.LocalSearchDecider     :         Move index (0) not doable, ignoring move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=46, created=2017-05-15 16:29:34.142, lastModified=2017-05-15 16:29:34.142), resource=Product(name=oil1)) {Product(name=oil1) -> Product(name=oil1)}).
2017-05-15 16:30:33.834 TRACE   --- [           main] o.o.c.i.l.decider.LocalSearchDecider     :         Move index (1) not doable, ignoring move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=47, created=2017-05-15 16:29:34.18, lastModified=2017-05-15 16:29:34.18), resource=atv1 (atv)) {atv1 (atv) -> atv1 (atv)}).
2017-05-15 16:30:33.834 TRACE   --- [           main] o.o.c.i.l.decider.LocalSearchDecider     :         Move index (2) not doable, ignoring move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=47, created=2017-05-15 16:29:34.18, lastModified=2017-05-15 16:29:34.18), resource=steve steveson (Spreader Driver)) {steve steveson (Spreader Driver) -> steve steveson (Spreader Driver)}).
2017-05-15 16:30:33.834 TRACE   --- [           main] o.o.c.i.l.decider.LocalSearchDecider     :         Move index (3), score (-1hard/-15120soft), accepted (false), move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=46, created=2017-05-15 16:29:34.142, lastModified=2017-05-15 16:29:34.142), resource=subguy subguyson (Tractor Driver)) {subguy subguyson (Tractor Driver) -> joe2 joeson (Tractor Driver)}).
2017-05-15 16:30:33.834 TRACE   --- [           main] o.o.c.i.l.decider.LocalSearchDecider     :         Move index (4) not doable, ignoring move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=46, created=2017-05-15 16:29:34.142, lastModified=2017-05-15 16:29:34.142), resource=spreader2 (spreader)) {spreader2 (spreader) -> spreader2 (spreader)}).
2017-05-15 16:30:33.834 TRACE   --- [           main] o.o.c.i.l.decider.LocalSearchDecider     :         Move index (5), score (0hard/-15110soft), accepted (true), move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=47, created=2017-05-15 16:29:34.18, lastModified=2017-05-15 16:29:34.18), resource=atv2 (atv)) {atv2 (atv) -> atv1 (atv)}).
2017-05-15 16:30:33.834 DEBUG   --- [           main] o.o.c.i.l.DefaultLocalSearchPhase        :     LS step (3972), time spent (56826), score (0hard/-15110soft),     best score (0hard/-15070soft), accepted/selected move count (1/2), picked move (PlannerTaskResourceAllocation(activity=AbstractEntity(id=47, created=2017-05-15 16:29:34.18, lastModified=2017-05-15 16:29:34.18), resource=atv2 (atv)) {atv2 (atv) -> atv1 (atv)}).

here's my solver config:

<?xml version="1.0" encoding="UTF-8"?>
<solver>
<!--    <environmentMode>FULL_ASSERT</environmentMode> -->

    <!-- Domain model configuration -->
    <solutionClass>com.rdthree.plenty.services.activities.planner.ActivitySolution</solutionClass>
    <entityClass>com.rdthree.plenty.services.activities.helpers.dtos.PlannerActivity</entityClass>
    <entityClass>com.rdthree.plenty.services.activities.helpers.dtos.PlannerTaskResourceAllocation</entityClass>

    <!-- Score configuration -->
    <scoreDirectorFactory>
        <scoreDefinitionType>HARD_SOFT</scoreDefinitionType>
        <scoreDrl>com/rdthree/plenty/services/activities/planner/activity-scoring.drl</scoreDrl>
        <initializingScoreTrend>ANY</initializingScoreTrend>
    </scoreDirectorFactory>

    <!-- Optimization algorithms configuration -->
    <termination>
        <terminationCompositionStyle>OR</terminationCompositionStyle>
        <bestScoreLimit>0hard/0soft</bestScoreLimit>
        <secondsSpentLimit>60</secondsSpentLimit>
    </termination>



    <constructionHeuristic>
        <queuedEntityPlacer>
            <entitySelector id="resourceAllocationSelector">
                <entityClass>com.rdthree.plenty.services.activities.helpers.dtos.PlannerTaskResourceAllocation</entityClass>
                <cacheType>PHASE</cacheType>
                <selectionOrder>SORTED</selectionOrder>
                <sorterManner>DECREASING_DIFFICULTY_IF_AVAILABLE</sorterManner>
            </entitySelector>
            <changeMoveSelector>
                <entitySelector mimicSelectorRef="resourceAllocationSelector" />
                <valueSelector>
                    <variableName>resource</variableName>
                    <cacheType>PHASE</cacheType>
                </valueSelector>
            </changeMoveSelector>
        </queuedEntityPlacer>
    </constructionHeuristic>

    <constructionHeuristic>
        <queuedEntityPlacer>
            <entitySelector id="activityIndexSelector">
                <entityClass>com.rdthree.plenty.services.activities.helpers.dtos.PlannerActivity</entityClass>
                <cacheType>PHASE</cacheType>
                <selectionOrder>SORTED</selectionOrder>
                <sorterManner>DECREASING_DIFFICULTY_IF_AVAILABLE</sorterManner>
            </entitySelector>
            <changeMoveSelector>
                <entitySelector mimicSelectorRef="activityIndexSelector" />
                <valueSelector>
                    <variableName>startIndex</variableName>
                    <cacheType>PHASE</cacheType>
                </valueSelector>
            </changeMoveSelector>
        </queuedEntityPlacer>
    </constructionHeuristic>

    <localSearch>
    <!-- run it with exhaustive search -->
        <unionMoveSelector>
             <changeMoveSelector>
                <fixedProbabilityWeight>1000.0</fixedProbabilityWeight>
                <entitySelector id="taskResourceAllocationSelector">
                    <entityClass>com.rdthree.plenty.services.activities.helpers.dtos.PlannerTaskResourceAllocation</entityClass>
                </entitySelector>
                <valueSelector>
                    <variableName>resource</variableName>
                </valueSelector>
                <filterClass>com.rdthree.plenty.services.activities.planner.filters.ResourceTypeMismatchFilter</filterClass>
            </changeMoveSelector>
            <changeMoveSelector>
                <fixedProbabilityWeight>1.0</fixedProbabilityWeight>
                <entitySelector id="activityelector">
                    <entityClass>com.rdthree.plenty.services.activities.helpers.dtos.PlannerActivity</entityClass>
                </entitySelector>
                <valueSelector>
                    <variableName>startIndex</variableName>
                </valueSelector>
            </changeMoveSelector>
        </unionMoveSelector>

        <acceptor>
            <simulatedAnnealingStartingTemperature>0hard/1000soft</simulatedAnnealingStartingTemperature>
        </acceptor>
        <forager>
            <acceptedCountLimit>1</acceptedCountLimit>
        </forager>
    </localSearch>

</solver>

Am i missing something?

0 个答案:

没有答案