/*
* Copyright 2010 Red Hat, Inc. and/or its affiliates.
*
* 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.impl.phase;
import java.util.Iterator;
import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.score.Score;
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.localsearch.DefaultLocalSearchPhase;
import org.optaplanner.core.impl.phase.event.PhaseLifecycleListener;
import org.optaplanner.core.impl.phase.event.PhaseLifecycleSupport;
import org.optaplanner.core.impl.phase.scope.AbstractPhaseScope;
import org.optaplanner.core.impl.phase.scope.AbstractStepScope;
import org.optaplanner.core.impl.score.director.InnerScoreDirector;
import org.optaplanner.core.impl.solver.DefaultSolver;
import org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller;
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope;
import org.optaplanner.core.impl.solver.termination.Termination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
* @see DefaultLocalSearchPhase
*/
public abstract class AbstractPhase<Solution_> implements Phase<Solution_> {
protected final transient Logger logger = LoggerFactory.getLogger(getClass());
protected final int phaseIndex;
protected final String logIndentation;
protected final BestSolutionRecaller<Solution_> bestSolutionRecaller;
protected final Termination termination;
/** Used for {@link DefaultSolver#addPhaseLifecycleListener(PhaseLifecycleListener)}. */
protected PhaseLifecycleSupport<Solution_> solverPhaseLifecycleSupport;
/** Used for {@link #addPhaseLifecycleListener(PhaseLifecycleListener)}. */
protected PhaseLifecycleSupport<Solution_> phaseLifecycleSupport = new PhaseLifecycleSupport<>();
protected boolean assertStepScoreFromScratch = false;
protected boolean assertExpectedStepScore = false;
protected boolean assertShadowVariablesAreNotStaleAfterStep = false;
public AbstractPhase(int phaseIndex, String logIndentation,
BestSolutionRecaller<Solution_> bestSolutionRecaller, Termination termination) {
this.phaseIndex = phaseIndex;
this.logIndentation = logIndentation;
this.bestSolutionRecaller = bestSolutionRecaller;
this.termination = termination;
}
public int getPhaseIndex() {
return phaseIndex;
}
public Termination getTermination() {
return termination;
}
@Override
public void setSolverPhaseLifecycleSupport(PhaseLifecycleSupport<Solution_> solverPhaseLifecycleSupport) {
this.solverPhaseLifecycleSupport = solverPhaseLifecycleSupport;
}
public boolean isAssertStepScoreFromScratch() {
return assertStepScoreFromScratch;
}
public void setAssertStepScoreFromScratch(boolean assertStepScoreFromScratch) {
this.assertStepScoreFromScratch = assertStepScoreFromScratch;
}
public boolean isAssertExpectedStepScore() {
return assertExpectedStepScore;
}
public void setAssertExpectedStepScore(boolean assertExpectedStepScore) {
this.assertExpectedStepScore = assertExpectedStepScore;
}
public boolean isAssertShadowVariablesAreNotStaleAfterStep() {
return assertShadowVariablesAreNotStaleAfterStep;
}
public void setAssertShadowVariablesAreNotStaleAfterStep(boolean assertShadowVariablesAreNotStaleAfterStep) {
this.assertShadowVariablesAreNotStaleAfterStep = assertShadowVariablesAreNotStaleAfterStep;
}
public abstract String getPhaseTypeString();
// ************************************************************************
// Lifecycle methods
// ************************************************************************
@Override
public void solvingStarted(DefaultSolverScope<Solution_> solverScope) {
// bestSolutionRecaller.solvingStarted(...) is called by DefaultSolver
// solverPhaseLifecycleSupport.solvingStarted(...) is called by DefaultSolver
termination.solvingStarted(solverScope);
phaseLifecycleSupport.fireSolvingStarted(solverScope);
}
@Override
public void solvingEnded(DefaultSolverScope<Solution_> solverScope) {
// bestSolutionRecaller.solvingEnded(...) is called by DefaultSolver
// solverPhaseLifecycleSupport.solvingEnded(...) is called by DefaultSolver
termination.solvingEnded(solverScope);
phaseLifecycleSupport.fireSolvingEnded(solverScope);
}
@Override
public void phaseStarted(AbstractPhaseScope<Solution_> phaseScope) {
phaseScope.startingNow();
phaseScope.reset();
bestSolutionRecaller.phaseStarted(phaseScope);
solverPhaseLifecycleSupport.firePhaseStarted(phaseScope);
termination.phaseStarted(phaseScope);
phaseLifecycleSupport.firePhaseStarted(phaseScope);
}
@Override
public void stepStarted(AbstractStepScope<Solution_> stepScope) {
bestSolutionRecaller.stepStarted(stepScope);
solverPhaseLifecycleSupport.fireStepStarted(stepScope);
termination.stepStarted(stepScope);
phaseLifecycleSupport.fireStepStarted(stepScope);
}
protected void calculateWorkingStepScore(AbstractStepScope<Solution_> stepScope, Object completedAction) {
AbstractPhaseScope<Solution_> phaseScope = stepScope.getPhaseScope();
Score score = phaseScope.calculateScore();
stepScope.setScore(score);
if (assertStepScoreFromScratch) {
phaseScope.assertWorkingScoreFromScratch(stepScope.getScore(), completedAction);
}
if (assertShadowVariablesAreNotStaleAfterStep) {
phaseScope.assertShadowVariablesAreNotStale(stepScope.getScore(), completedAction);
}
}
protected void predictWorkingStepScore(AbstractStepScope<Solution_> stepScope, Object completedAction) {
AbstractPhaseScope<Solution_> phaseScope = stepScope.getPhaseScope();
// There is no need to recalculate the score, but we still need to set it
phaseScope.getSolutionDescriptor().setScore(phaseScope.getWorkingSolution(), stepScope.getScore());
if (assertStepScoreFromScratch) {
phaseScope.assertWorkingScoreFromScratch(stepScope.getScore(), completedAction);
}
if (assertExpectedStepScore) {
phaseScope.assertExpectedWorkingScore(stepScope.getScore(), completedAction);
}
if (assertShadowVariablesAreNotStaleAfterStep) {
phaseScope.assertShadowVariablesAreNotStale(stepScope.getScore(), completedAction);
}
}
@Override
public void stepEnded(AbstractStepScope<Solution_> stepScope) {
bestSolutionRecaller.stepEnded(stepScope);
solverPhaseLifecycleSupport.fireStepEnded(stepScope);
termination.stepEnded(stepScope);
phaseLifecycleSupport.fireStepEnded(stepScope);
}
@Override
public void phaseEnded(AbstractPhaseScope<Solution_> phaseScope) {
bestSolutionRecaller.phaseEnded(phaseScope);
solverPhaseLifecycleSupport.firePhaseEnded(phaseScope);
termination.phaseEnded(phaseScope);
phaseLifecycleSupport.firePhaseEnded(phaseScope);
}
@Override
public void addPhaseLifecycleListener(PhaseLifecycleListener<Solution_> phaseLifecycleListener) {
phaseLifecycleSupport.addEventListener(phaseLifecycleListener);
}
@Override
public void removePhaseLifecycleListener(PhaseLifecycleListener<Solution_> phaseLifecycleListener) {
phaseLifecycleSupport.removeEventListener(phaseLifecycleListener);
}
// ************************************************************************
// Assert methods
// ************************************************************************
protected void assertWorkingSolutionInitialized(AbstractPhaseScope<Solution_> phaseScope) {
if (!phaseScope.getStartingScore().isSolutionInitialized()) {
InnerScoreDirector<Solution_> scoreDirector = phaseScope.getScoreDirector();
SolutionDescriptor<Solution_> solutionDescriptor = scoreDirector.getSolutionDescriptor();
Solution_ workingSolution = scoreDirector.getWorkingSolution();
for (Iterator<Object> it = solutionDescriptor.extractAllEntitiesIterator(workingSolution); it.hasNext();) {
Object entity = it.next();
EntityDescriptor<Solution_> entityDescriptor = solutionDescriptor.findEntityDescriptorOrFail(
entity.getClass());
if (!entityDescriptor.isEntityInitializedOrImmovable(scoreDirector, entity)) {
String variableRef = null;
for (GenuineVariableDescriptor<Solution_> variableDescriptor : entityDescriptor.getGenuineVariableDescriptors()) {
if (!variableDescriptor.isInitialized(entity)) {
variableRef = variableDescriptor.getSimpleEntityAndVariableName();
break;
}
}
throw new IllegalStateException(getPhaseTypeString() + " phase (" + phaseIndex
+ ") needs to start from an initialized solution, but the planning variable (" + variableRef
+ ") is uninitialized for the entity (" + entity + ").\n"
+ "Maybe there is no Construction Heuristic configured before this phase to initialize the solution.\n"
+ "Or maybe the getter/setters of your planning variables in your domain classes aren't implemented correctly.");
}
}
}
}
}