/** * 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; import java.util.Random; import org.drools.RuleBase; import org.drools.planner.core.localsearch.bestsolution.BestSolutionRecaller; import org.drools.planner.core.localsearch.decider.Decider; import org.drools.planner.core.localsearch.termination.Termination; import org.drools.planner.core.move.Move; import org.drools.planner.core.score.calculator.ScoreCalculator; import org.drools.planner.core.score.definition.ScoreDefinition; import org.drools.planner.core.solution.Solution; import org.drools.planner.core.solution.initializer.StartingSolutionInitializer; import org.drools.planner.core.AbstractSolver; /** * Default implementation of {@link LocalSearchSolver}. * @author Geoffrey De Smet */ public class DefaultLocalSearchSolver extends AbstractSolver implements LocalSearchSolver, LocalSearchSolverLifecycleListener { protected Long randomSeed; // TODO refactor to AbstractSolver protected StartingSolutionInitializer startingSolutionInitializer = null; // TODO refactor to AbstractSolver protected BestSolutionRecaller bestSolutionRecaller; protected Termination termination; protected Decider decider; protected boolean assertStepScoreIsUncorrupted = false; protected LocalSearchSolverScope localSearchSolverScope = new LocalSearchSolverScope(); // TODO remove me public void setRandomSeed(long randomSeed) { this.randomSeed = randomSeed; } public void setRuleBase(RuleBase ruleBase) { localSearchSolverScope.setRuleBase(ruleBase); } public void setScoreDefinition(ScoreDefinition scoreDefinition) { localSearchSolverScope.setScoreDefinition(scoreDefinition); } public ScoreDefinition getScoreDefinition() { return localSearchSolverScope.getScoreDefinition(); } public void setScoreCalculator(ScoreCalculator scoreCalculator) { localSearchSolverScope.setWorkingScoreCalculator(scoreCalculator); } public StartingSolutionInitializer getStartingSolutionInitializer() { return startingSolutionInitializer; } public void setStartingSolutionInitializer(StartingSolutionInitializer startingSolutionInitializer) { this.startingSolutionInitializer = startingSolutionInitializer; } public void setBestSolutionRecaller(BestSolutionRecaller bestSolutionRecaller) { this.bestSolutionRecaller = bestSolutionRecaller; this.bestSolutionRecaller.setLocalSearchSolver(this); this.bestSolutionRecaller.setSolverEventSupport(solverEventSupport); } public void setTermination(Termination termination) { this.termination = termination; this.termination.setLocalSearchSolver(this); } public Decider getDecider() { return decider; } public void setDecider(Decider decider) { this.decider = decider; this.decider.setLocalSearchSolver(this); } public void setAssertStepScoreIsUncorrupted(boolean assertStepScoreIsUncorrupted) { this.assertStepScoreIsUncorrupted = assertStepScoreIsUncorrupted; } public void setStartingSolution(Solution startingSolution) { localSearchSolverScope.setWorkingSolution(startingSolution); } public Solution getBestSolution() { return this.localSearchSolverScope.getBestSolution(); } public long getTimeMillisSpend() { return this.localSearchSolverScope.calculateTimeMillisSpend(); } public LocalSearchSolverScope getLocalSearchSolverScope() { return localSearchSolverScope; } // ************************************************************************ // Worker methods // ************************************************************************ @Override protected void solveImplementation() { LocalSearchSolverScope localSearchSolverScope = this.localSearchSolverScope; solvingStarted(localSearchSolverScope); StepScope stepScope = createNextStepScope(localSearchSolverScope, null); while (!terminatedEarly.get() && !termination.isTerminated(stepScope)) { stepScope.setTimeGradient(termination.calculateTimeGradient(stepScope)); beforeDeciding(stepScope); decider.decideNextStep(stepScope); Move nextStep = stepScope.getStep(); if (nextStep == null) { // TODO JBRULES-2213 do not terminate, but warn and try again logger.warn("No move accepted for step index ({}) out of {} accepted moves. Terminating by exception.", stepScope.getStepIndex(), decider.getForager().getAcceptedMovesSize()); break; } logger.info("Step index ({}), time spend ({}) taking step ({}) out of {} accepted moves.", new Object[]{stepScope.getStepIndex(), localSearchSolverScope.calculateTimeMillisSpend(), nextStep, decider.getForager().getAcceptedMovesSize()}); stepDecided(stepScope); nextStep.doMove(stepScope.getWorkingMemory()); // there is no need to recalculate the score, but we still need to set it localSearchSolverScope.getWorkingSolution().setScore(stepScope.getScore()); if (assertStepScoreIsUncorrupted) { localSearchSolverScope.assertWorkingScore(stepScope.getScore()); } stepTaken(stepScope); stepScope = createNextStepScope(localSearchSolverScope, stepScope); } solvingEnded(localSearchSolverScope); } private StepScope createNextStepScope(LocalSearchSolverScope localSearchSolverScope, StepScope completedStepScope) { if (completedStepScope == null) { completedStepScope = new StepScope(localSearchSolverScope); completedStepScope.setScore(localSearchSolverScope.getStartingScore()); completedStepScope.setStepIndex(-1); completedStepScope.setTimeGradient(0.0); } localSearchSolverScope.setLastCompletedStepScope(completedStepScope); StepScope stepScope = new StepScope(localSearchSolverScope); stepScope.setStepIndex(completedStepScope.getStepIndex() + 1); return stepScope; } public void solvingStarted(LocalSearchSolverScope localSearchSolverScope) { if (localSearchSolverScope.getWorkingSolution() == null) { throw new IllegalStateException("The startingSolution must not be null." + " Use Solver.setStartingSolution(Solution)."); } localSearchSolverScope.reset(); if (randomSeed != null) { logger.info("Solving with random seed ({}).", randomSeed); localSearchSolverScope.setWorkingRandom(new Random(randomSeed)); } else { logger.info("Solving with a non-fixed random seed."); localSearchSolverScope.setWorkingRandom(new Random()); } if (startingSolutionInitializer != null) { if (!startingSolutionInitializer.isSolutionInitialized(localSearchSolverScope)) { logger.info("Initializing solution."); startingSolutionInitializer.initializeSolution(localSearchSolverScope); } else { logger.debug("Solution is already initialized."); } } localSearchSolverScope.setStartingScore(localSearchSolverScope.calculateScoreFromWorkingMemory()); bestSolutionRecaller.solvingStarted(localSearchSolverScope); termination.solvingStarted(localSearchSolverScope); decider.solvingStarted(localSearchSolverScope); } public void beforeDeciding(StepScope stepScope) { bestSolutionRecaller.beforeDeciding(stepScope); termination.beforeDeciding(stepScope); decider.beforeDeciding(stepScope); } public void stepDecided(StepScope stepScope) { bestSolutionRecaller.stepDecided(stepScope); termination.stepDecided(stepScope); decider.stepDecided(stepScope); } public void stepTaken(StepScope stepScope) { bestSolutionRecaller.stepTaken(stepScope); termination.stepTaken(stepScope); decider.stepTaken(stepScope); } public void solvingEnded(LocalSearchSolverScope localSearchSolverScope) { bestSolutionRecaller.solvingEnded(localSearchSolverScope); termination.solvingEnded(localSearchSolverScope); decider.solvingEnded(localSearchSolverScope); long timeMillisSpend = localSearchSolverScope.calculateTimeMillisSpend(); long averageCalculateCountPerSecond = localSearchSolverScope.getCalculateCount() * 1000L / timeMillisSpend; logger.info("Solved at step index ({}) with time spend ({}) for best score ({})" + " with average calculate count per second ({}).", new Object[] { localSearchSolverScope.getLastCompletedStepScope().getStepIndex(), timeMillisSpend, localSearchSolverScope.getBestScore(), averageCalculateCountPerSecond }); } }