Skip to content

Commit

Permalink
PLANNER-355 A simple ChangeMoveSelector configuration should still wo…
Browse files Browse the repository at this point in the history
…rk if there are multiple variables
  • Loading branch information
ge0ffrey committed Jun 25, 2015
1 parent 341cfb3 commit f8ea829
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 8 deletions.
Expand Up @@ -78,6 +78,13 @@ public class EntitySelectorConfig extends SelectorConfig {

protected Long selectedCountLimit = null;

public EntitySelectorConfig() {
}

public EntitySelectorConfig(Class<?> entityClass) {
this.entityClass = entityClass;
}

public String getId() {
return id;
}
Expand Down
Expand Up @@ -168,7 +168,6 @@ public void setFixedProbabilityWeight(Double fixedProbabilityWeight) {
// ************************************************************************

/**
*
* @param configPolicy never null
* @param minimumCacheType never null, If caching is used (different from {@link SelectionCacheType#JUST_IN_TIME}),
* then it should be at least this {@link SelectionCacheType} because an ancestor already uses such caching
Expand All @@ -178,6 +177,11 @@ public void setFixedProbabilityWeight(Double fixedProbabilityWeight) {
*/
public MoveSelector buildMoveSelector(HeuristicConfigPolicy configPolicy,
SelectionCacheType minimumCacheType, SelectionOrder inheritedSelectionOrder) {
MoveSelectorConfig unfoldedMoveSelectorConfig = buildUnfoldedMoveSelectorConfig(configPolicy);
if (unfoldedMoveSelectorConfig != null) {
return unfoldedMoveSelectorConfig.buildMoveSelector(configPolicy, minimumCacheType, inheritedSelectionOrder);
}

SelectionCacheType resolvedCacheType = SelectionCacheType.resolve(cacheType, minimumCacheType);
SelectionOrder resolvedSelectionOrder = SelectionOrder.resolve(selectionOrder, inheritedSelectionOrder);

Expand All @@ -199,6 +203,13 @@ public MoveSelector buildMoveSelector(HeuristicConfigPolicy configPolicy,
return moveSelector;
}

/**
* @return null if no unfolding is needed
*/
protected MoveSelectorConfig buildUnfoldedMoveSelectorConfig(HeuristicConfigPolicy configPolicy) {
return null;
}

protected boolean determineBaseRandomSelection(
SelectionCacheType resolvedCacheType, SelectionOrder resolvedSelectionOrder) {
switch (resolvedSelectionOrder) {
Expand Down
Expand Up @@ -16,13 +16,23 @@

package org.optaplanner.core.config.heuristic.selector.move.generic;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.optaplanner.core.config.heuristic.policy.HeuristicConfigPolicy;
import org.optaplanner.core.config.heuristic.selector.common.SelectionCacheType;
import org.optaplanner.core.config.heuristic.selector.common.SelectionOrder;
import org.optaplanner.core.config.heuristic.selector.entity.EntitySelectorConfig;
import org.optaplanner.core.config.heuristic.selector.move.MoveSelectorConfig;
import org.optaplanner.core.config.heuristic.selector.move.composite.CartesianProductMoveSelectorConfig;
import org.optaplanner.core.config.heuristic.selector.value.ValueSelectorConfig;
import org.optaplanner.core.impl.domain.entity.descriptor.EntityDescriptor;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import org.optaplanner.core.impl.heuristic.selector.common.decorator.SelectionFilter;
import org.optaplanner.core.impl.heuristic.selector.entity.EntitySelector;
import org.optaplanner.core.impl.heuristic.selector.move.MoveSelector;
import org.optaplanner.core.impl.heuristic.selector.move.generic.ChangeMoveSelector;
Expand Down Expand Up @@ -59,18 +69,89 @@ public void setValueSelectorConfig(ValueSelectorConfig valueSelectorConfig) {

public MoveSelector buildBaseMoveSelector(HeuristicConfigPolicy configPolicy,
SelectionCacheType minimumCacheType, boolean randomSelection) {
EntitySelectorConfig entitySelectorConfig_ = entitySelectorConfig == null ? new EntitySelectorConfig()
: entitySelectorConfig;
EntitySelector entitySelector = entitySelectorConfig_.buildEntitySelector(configPolicy,
if (entitySelectorConfig == null) {
throw new IllegalStateException("The entitySelectorConfig (" + entitySelectorConfig
+ ") should haven been initialized during unfolding.");
}
EntitySelector entitySelector = entitySelectorConfig.buildEntitySelector(configPolicy,
minimumCacheType, SelectionOrder.fromRandomSelectionBoolean(randomSelection));
ValueSelectorConfig valueSelectorConfig_ = valueSelectorConfig == null ? new ValueSelectorConfig()
: valueSelectorConfig;
ValueSelector valueSelector = valueSelectorConfig_.buildValueSelector(configPolicy,
if (valueSelectorConfig == null) {
throw new IllegalStateException("The valueSelectorConfig (" + valueSelectorConfig
+ ") should haven been initialized during unfolding.");
}
ValueSelector valueSelector = valueSelectorConfig.buildValueSelector(configPolicy,
entitySelector.getEntityDescriptor(),
minimumCacheType, SelectionOrder.fromRandomSelectionBoolean(randomSelection));
return new ChangeMoveSelector(entitySelector, valueSelector, randomSelection);
}

@Override
protected MoveSelectorConfig buildUnfoldedMoveSelectorConfig(HeuristicConfigPolicy configPolicy) {
Collection<EntityDescriptor> entityDescriptors;
SolutionDescriptor solutionDescriptor = configPolicy.getSolutionDescriptor();
if (entitySelectorConfig != null && (entitySelectorConfig.getEntityClass() != null || entitySelectorConfig.getMimicSelectorRef() != null)) {
if (valueSelectorConfig != null && valueSelectorConfig.getVariableName() != null) {
return null;
}
EntityDescriptor entityDescriptor;
if (entitySelectorConfig.getEntityClass() != null) {
entityDescriptor = solutionDescriptor.getEntityDescriptorStrict(entitySelectorConfig.getEntityClass());
if (entityDescriptor == null) {
throw new IllegalArgumentException("The selectorConfig (" + entitySelectorConfig
+ ") has an entityClass (" + entitySelectorConfig.getEntityClass() + ") that is not a known planning entity.\n"
+ "Check your solver configuration. If that class (" + entitySelectorConfig.getEntityClass().getSimpleName()
+ ") is not in the entityClassSet (" + solutionDescriptor.getEntityClassSet()
+ "), check your Solution implementation's annotated methods too.");
}
} else {
entityDescriptor = configPolicy.getEntityMimicRecorder(entitySelectorConfig.getMimicSelectorRef()).getEntityDescriptor();
}
entityDescriptors = Collections.singletonList(entityDescriptor);
} else {
entityDescriptors = solutionDescriptor.getEntityDescriptors();
}

List<GenuineVariableDescriptor> variableDescriptorList = new ArrayList<GenuineVariableDescriptor>();
for (EntityDescriptor entityDescriptor : entityDescriptors) {
if (valueSelectorConfig != null && valueSelectorConfig.getVariableName() != null) {
GenuineVariableDescriptor variableDescriptor = entityDescriptor.getGenuineVariableDescriptor(valueSelectorConfig.getVariableName());
if (variableDescriptor == null) {
throw new IllegalArgumentException("The selectorConfig (" + valueSelectorConfig
+ ") has a variableName (" + valueSelectorConfig.getVariableName()
+ ") which is not a valid planning variable on entityClass ("
+ entityDescriptor.getEntityClass() + ").\n"
+ entityDescriptor.buildInvalidVariableNameExceptionMessage(valueSelectorConfig.getVariableName()));
}
variableDescriptorList.add(variableDescriptor);
} else {
variableDescriptorList.addAll(entityDescriptor.getGenuineVariableDescriptors());
}
}

List<MoveSelectorConfig> moveSelectorConfigList = new ArrayList<MoveSelectorConfig>(variableDescriptorList.size());
for (GenuineVariableDescriptor variableDescriptor : variableDescriptorList) {
ChangeMoveSelectorConfig changeMoveSelectorConfig = new ChangeMoveSelectorConfig();
changeMoveSelectorConfig.inherit(this);
EntitySelectorConfig entitySelectorConfig = new EntitySelectorConfig();
if (this.entitySelectorConfig != null) {
entitySelectorConfig.inherit(this.entitySelectorConfig);
}
if (entitySelectorConfig.getMimicSelectorRef() == null) {
entitySelectorConfig.setEntityClass(variableDescriptor.getEntityDescriptor().getEntityClass());
}
changeMoveSelectorConfig.setEntitySelectorConfig(entitySelectorConfig);
ValueSelectorConfig valueSelectorConfig = new ValueSelectorConfig();
if (this.valueSelectorConfig != null) {
valueSelectorConfig.inherit(this.valueSelectorConfig);
}
valueSelectorConfig.setVariableName(variableDescriptor.getVariableName());
changeMoveSelectorConfig.setValueSelectorConfig(valueSelectorConfig);
moveSelectorConfigList.add(changeMoveSelectorConfig);
}
return moveSelectorConfigList.size() == 1 ? moveSelectorConfigList.get(0)
: new CartesianProductMoveSelectorConfig(moveSelectorConfigList);
}

public void inherit(ChangeMoveSelectorConfig inheritedConfig) {
super.inherit(inheritedConfig);
if (entitySelectorConfig == null) {
Expand Down
Expand Up @@ -77,6 +77,13 @@ public class ValueSelectorConfig extends SelectorConfig {

protected Long selectedCountLimit = null;

public ValueSelectorConfig() {
}

public ValueSelectorConfig(String variableName) {
this.variableName = variableName;
}

public Class<?> getDowncastEntityClass() {
return downcastEntityClass;
}
Expand Down
Expand Up @@ -62,6 +62,10 @@ public static Move buildMove(List<Move> moveList) {
}
}

// ************************************************************************
// Non-static members
// ************************************************************************

protected final Move[] moves;

/**
Expand Down
Expand Up @@ -56,6 +56,10 @@ protected CompositeMoveSelector(List<MoveSelector> childMoveSelectorList, boolea
}
}

public List<MoveSelector> getChildMoveSelectorList() {
return childMoveSelectorList;
}

// ************************************************************************
// Worker methods
// ************************************************************************
Expand Down
Expand Up @@ -18,6 +18,7 @@

import org.optaplanner.core.config.heuristic.policy.HeuristicConfigPolicy;
import org.optaplanner.core.config.solver.EnvironmentMode;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.score.buildin.simple.SimpleScoreDefinition;
import org.optaplanner.core.impl.score.director.InnerScoreDirectorFactory;
import org.optaplanner.core.impl.testdata.domain.TestdataSolution;
Expand All @@ -27,8 +28,12 @@
public abstract class AbstractSelectorConfigTest {

public HeuristicConfigPolicy buildHeuristicConfigPolicy() {
return buildHeuristicConfigPolicy(TestdataSolution.buildSolutionDescriptor());
}

public HeuristicConfigPolicy buildHeuristicConfigPolicy(SolutionDescriptor solutionDescriptor) {
InnerScoreDirectorFactory scoreDirectorFactory = mock(InnerScoreDirectorFactory.class);
when(scoreDirectorFactory.getSolutionDescriptor()).thenReturn(TestdataSolution.buildSolutionDescriptor());
when(scoreDirectorFactory.getSolutionDescriptor()).thenReturn(solutionDescriptor);
when(scoreDirectorFactory.getScoreDefinition()).thenReturn(new SimpleScoreDefinition());
return new HeuristicConfigPolicy(EnvironmentMode.REPRODUCIBLE, scoreDirectorFactory);
}
Expand Down
@@ -0,0 +1,98 @@
/*
* Copyright 2015 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.optaplanner.core.config.heuristic.selector.move.generic;

import org.junit.Test;
import org.optaplanner.core.config.heuristic.policy.HeuristicConfigPolicy;
import org.optaplanner.core.config.heuristic.selector.AbstractSelectorConfigTest;
import org.optaplanner.core.config.heuristic.selector.common.SelectionCacheType;
import org.optaplanner.core.config.heuristic.selector.common.SelectionOrder;
import org.optaplanner.core.config.heuristic.selector.entity.EntitySelectorConfig;
import org.optaplanner.core.config.heuristic.selector.value.ValueSelectorConfig;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.heuristic.selector.move.MoveSelector;
import org.optaplanner.core.impl.heuristic.selector.move.composite.CartesianProductMoveSelector;
import org.optaplanner.core.impl.heuristic.selector.move.generic.ChangeMoveSelector;
import org.optaplanner.core.impl.testdata.domain.TestdataEntity;
import org.optaplanner.core.impl.testdata.domain.multientity.TestdataHerdEntity;
import org.optaplanner.core.impl.testdata.domain.multientity.TestdataMultiEntitySolution;
import org.optaplanner.core.impl.testdata.domain.multivar.TestdataMultiVarSolution;

import static org.junit.Assert.*;
import static org.optaplanner.core.impl.testdata.util.PlannerAssert.assertInstanceOf;

public class ChangeMoveSelectorConfigTest extends AbstractSelectorConfigTest {

@Test
public void deducibleMultiVar() {
SolutionDescriptor solutionDescriptor = TestdataMultiVarSolution.buildSolutionDescriptor();
ChangeMoveSelectorConfig moveSelectorConfig = new ChangeMoveSelectorConfig();
moveSelectorConfig.setValueSelectorConfig(new ValueSelectorConfig("secondaryValue"));
MoveSelector moveSelector = moveSelectorConfig.buildMoveSelector(
buildHeuristicConfigPolicy(solutionDescriptor), SelectionCacheType.JUST_IN_TIME, SelectionOrder.RANDOM);
assertInstanceOf(ChangeMoveSelector.class, moveSelector);
}

@Test(expected = IllegalArgumentException.class)
public void undeducibleMultiVar() {
SolutionDescriptor solutionDescriptor = TestdataMultiVarSolution.buildSolutionDescriptor();
ChangeMoveSelectorConfig moveSelectorConfig = new ChangeMoveSelectorConfig();
moveSelectorConfig.setValueSelectorConfig(new ValueSelectorConfig("nonExistingValue"));
MoveSelector moveSelector = moveSelectorConfig.buildMoveSelector(
buildHeuristicConfigPolicy(solutionDescriptor), SelectionCacheType.JUST_IN_TIME, SelectionOrder.RANDOM);
}

@Test
public void unfoldedMultiVar() {
SolutionDescriptor solutionDescriptor = TestdataMultiVarSolution.buildSolutionDescriptor();
ChangeMoveSelectorConfig moveSelectorConfig = new ChangeMoveSelectorConfig();
MoveSelector moveSelector = moveSelectorConfig.buildMoveSelector(
buildHeuristicConfigPolicy(solutionDescriptor), SelectionCacheType.JUST_IN_TIME, SelectionOrder.RANDOM);
assertInstanceOf(CartesianProductMoveSelector.class, moveSelector);
assertEquals(3, ((CartesianProductMoveSelector) moveSelector).getChildMoveSelectorList().size());
}

@Test
public void deducibleMultiEntity() {
SolutionDescriptor solutionDescriptor = TestdataMultiEntitySolution.buildSolutionDescriptor();
ChangeMoveSelectorConfig moveSelectorConfig = new ChangeMoveSelectorConfig();
moveSelectorConfig.setEntitySelectorConfig(new EntitySelectorConfig(TestdataHerdEntity.class));
MoveSelector moveSelector = moveSelectorConfig.buildMoveSelector(
buildHeuristicConfigPolicy(solutionDescriptor), SelectionCacheType.JUST_IN_TIME, SelectionOrder.RANDOM);
assertInstanceOf(ChangeMoveSelector.class, moveSelector);
}

@Test(expected = IllegalArgumentException.class)
public void undeducibleMultiEntity() {
SolutionDescriptor solutionDescriptor = TestdataMultiEntitySolution.buildSolutionDescriptor();
ChangeMoveSelectorConfig moveSelectorConfig = new ChangeMoveSelectorConfig();
moveSelectorConfig.setEntitySelectorConfig(new EntitySelectorConfig(TestdataEntity.class));
MoveSelector moveSelector = moveSelectorConfig.buildMoveSelector(
buildHeuristicConfigPolicy(solutionDescriptor), SelectionCacheType.JUST_IN_TIME, SelectionOrder.RANDOM);
}

@Test
public void unfoldedMultiEntity() {
SolutionDescriptor solutionDescriptor = TestdataMultiEntitySolution.buildSolutionDescriptor();
ChangeMoveSelectorConfig moveSelectorConfig = new ChangeMoveSelectorConfig();
MoveSelector moveSelector = moveSelectorConfig.buildMoveSelector(
buildHeuristicConfigPolicy(solutionDescriptor), SelectionCacheType.JUST_IN_TIME, SelectionOrder.RANDOM);
assertInstanceOf(CartesianProductMoveSelector.class, moveSelector);
assertEquals(2, ((CartesianProductMoveSelector) moveSelector).getChildMoveSelectorList().size());
}

}

0 comments on commit f8ea829

Please sign in to comment.