/**
* Copyright 2010 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.drools.planner.core.localsearch.decider.acceptor.tabu;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.drools.planner.core.localsearch.LocalSearchSolverScope;
import org.drools.planner.core.localsearch.StepScope;
import org.drools.planner.core.localsearch.decider.MoveScope;
import org.drools.planner.core.localsearch.decider.acceptor.AbstractAcceptor;
import org.drools.planner.core.localsearch.decider.acceptor.Acceptor;
/**
* Abstract superclass for all Tabu Acceptors.
* @see Acceptor
* @author Geoffrey De Smet
*/
public abstract class AbstractTabuAcceptor extends AbstractAcceptor {
protected int completeTabuSize = -1;
protected int partialTabuSize = 0;
protected boolean aspirationEnabled = true;
protected Map<Object, Integer> tabuToStepIndexMap;
protected List<Object> tabuSequenceList;
public int getCompleteTabuSize() {
return completeTabuSize;
}
public void setCompleteTabuSize(int completeTabuSize) {
this.completeTabuSize = completeTabuSize;
}
public void setPartialTabuSize(int partialTabuSize) {
this.partialTabuSize = partialTabuSize;
}
public void setAspirationEnabled(boolean aspirationEnabled) {
this.aspirationEnabled = aspirationEnabled;
}
// ************************************************************************
// Worker methods
// ************************************************************************
@Override
public void solvingStarted(LocalSearchSolverScope localSearchSolverScope) {
if (completeTabuSize < 0) {
throw new IllegalArgumentException("The completeTabuSize (" + completeTabuSize
+ ") cannot be negative.");
}
if (partialTabuSize < 0) {
throw new IllegalArgumentException("The partialTabuSize (" + partialTabuSize
+ ") cannot be negative.");
}
if (completeTabuSize + partialTabuSize == 0) {
throw new IllegalArgumentException("The sum of completeTabuSize and partialTabuSize should be at least 1.");
}
tabuToStepIndexMap = new HashMap<Object, Integer>(completeTabuSize + partialTabuSize);
tabuSequenceList = new LinkedList<Object>();
}
public double calculateAcceptChance(MoveScope moveScope) {
Collection<? extends Object> tabus = findTabu(moveScope);
int maximumTabuStepIndex = -1;
for (Object tabu : tabus) {
Integer tabuStepIndexInteger = tabuToStepIndexMap.get(tabu);
if (tabuStepIndexInteger != null) {
maximumTabuStepIndex = Math.max(tabuStepIndexInteger, maximumTabuStepIndex);
}
}
if (maximumTabuStepIndex < 0) {
// The move isn't tabu at all
return 1.0;
}
if (aspirationEnabled) {
// Doesn't use the deciderScoreComparator because shifting penalties don't apply
if (moveScope.getScore().compareTo(
moveScope.getStepScope().getLocalSearchSolverScope().getBestScore()) > 0) {
logger.debug(" Proposed move ({}) is tabu, but aspiration undoes its tabu.", moveScope.getMove());
return 1.0;
}
}
int tabuStepCount = moveScope.getStepScope().getStepIndex() - maximumTabuStepIndex - 1;
if (tabuStepCount < completeTabuSize) {
logger.debug(" Proposed move ({}) is complete tabu.", moveScope.getMove());
return 0.0;
}
double acceptChance = calculatePartialTabuAcceptChance(tabuStepCount - completeTabuSize);
logger.debug(" Proposed move ({}) is partially tabu with accept chance ({}).",
moveScope.getMove(), acceptChance);
return acceptChance;
}
protected double calculatePartialTabuAcceptChance(int partialTabuTime) {
// The + 1's are because acceptChance should not be 0.0 or 1.0
// when (partialTabuTime == 0) or (partialTabuTime + 1 == partialTabuSize)
return ((double) (partialTabuTime + 1)) / ((double) (partialTabuSize + 1));
}
@Override
public void stepTaken(StepScope stepScope) {
Collection<? extends Object> tabus = findNewTabu(stepScope);
for (Object tabu : tabus) {
// required to push tabu to the end of the line
if (tabuToStepIndexMap.containsKey(tabu)) {
tabuToStepIndexMap.remove(tabu);
tabuSequenceList.remove(tabu);
}
int maximumTabuListSize = completeTabuSize + partialTabuSize; // is at least 1
while (tabuSequenceList.size() >= maximumTabuListSize) {
Iterator<Object> it = tabuSequenceList.iterator();
Object removeTabu = it.next();
it.remove();
tabuToStepIndexMap.remove(removeTabu);
}
tabuToStepIndexMap.put(tabu, stepScope.getStepIndex());
tabuSequenceList.add(tabu);
}
}
protected abstract Collection<? extends Object> findTabu(MoveScope moveScope);
protected abstract Collection<? extends Object> findNewTabu(StepScope stepScope);
}