/* * Copyright 2011 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.solver; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import org.optaplanner.core.api.domain.solution.PlanningSolution; import org.optaplanner.core.api.score.Score; import org.optaplanner.core.api.solver.Solver; import org.optaplanner.core.config.solver.EnvironmentMode; import org.optaplanner.core.impl.phase.Phase; import org.optaplanner.core.impl.score.director.InnerScoreDirectorFactory; import org.optaplanner.core.impl.solver.random.RandomFactory; import org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller; import org.optaplanner.core.impl.solver.scope.DefaultSolverScope; import org.optaplanner.core.impl.solver.termination.BasicPlumbingTermination; import org.optaplanner.core.impl.solver.termination.Termination; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Default implementation for {@link Solver}. * @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation * @see Solver * @see AbstractSolver */ public class DefaultSolver<Solution_> extends AbstractSolver<Solution_> { protected final transient Logger logger = LoggerFactory.getLogger(getClass()); protected EnvironmentMode environmentMode; protected RandomFactory randomFactory; protected BasicPlumbingTermination basicPlumbingTermination; protected final AtomicBoolean solving = new AtomicBoolean(false); protected final DefaultSolverScope<Solution_> solverScope; // ************************************************************************ // Constructors and simple getters/setters // ************************************************************************ public DefaultSolver(EnvironmentMode environmentMode, RandomFactory randomFactory, BasicPlumbingTermination basicPlumbingTermination, Termination termination, BestSolutionRecaller<Solution_> bestSolutionRecaller, List<Phase<Solution_>> phaseList, DefaultSolverScope<Solution_> solverScope) { super(termination, bestSolutionRecaller, phaseList); this.environmentMode = environmentMode; this.randomFactory = randomFactory; this.basicPlumbingTermination = basicPlumbingTermination; this.solverScope = solverScope; } public EnvironmentMode getEnvironmentMode() { return environmentMode; } public RandomFactory getRandomFactory() { return randomFactory; } @Override public InnerScoreDirectorFactory<Solution_> getScoreDirectorFactory() { return solverScope.getScoreDirector().getScoreDirectorFactory(); } public BestSolutionRecaller<Solution_> getBestSolutionRecaller() { return bestSolutionRecaller; } public List<Phase<Solution_>> getPhaseList() { return phaseList; } public DefaultSolverScope<Solution_> getSolverScope() { return solverScope; } // ************************************************************************ // Complex getters // ************************************************************************ @Override public Solution_ getBestSolution() { return solverScope.getBestSolution(); } @Override public Score getBestScore() { return solverScope.getBestScore(); } @Override public long getTimeMillisSpent() { Long endingSystemTimeMillis = solverScope.getEndingSystemTimeMillis(); if (endingSystemTimeMillis == null) { endingSystemTimeMillis = System.currentTimeMillis(); } return endingSystemTimeMillis - solverScope.getStartingSystemTimeMillis(); } @Override public boolean isSolving() { return solving.get(); } @Override public boolean terminateEarly() { boolean terminationEarlySuccessful = basicPlumbingTermination.terminateEarly(); if (terminationEarlySuccessful) { logger.info("Terminating solver early."); } return terminationEarlySuccessful; } @Override public boolean isTerminateEarly() { return basicPlumbingTermination.isTerminateEarly(); } @Override public boolean addProblemFactChange(ProblemFactChange<Solution_> problemFactChange) { return basicPlumbingTermination.addProblemFactChange(problemFactChange); } @Override public boolean addProblemFactChanges(List<ProblemFactChange<Solution_>> problemFactChangeList) { return basicPlumbingTermination.addProblemFactChanges(problemFactChangeList); } @Override public boolean isEveryProblemFactChangeProcessed() { return basicPlumbingTermination.isEveryProblemFactChangeProcessed(); } // ************************************************************************ // Worker methods // ************************************************************************ @Override public final Solution_ solve(Solution_ planningProblem) { if (planningProblem == null) { throw new IllegalArgumentException("The planningProblem (" + planningProblem + ") must not be null."); } solverScope.setBestSolution(planningProblem); outerSolvingStarted(solverScope); boolean restartSolver = true; while (restartSolver) { solvingStarted(solverScope); runPhases(solverScope); solvingEnded(solverScope); restartSolver = checkProblemFactChanges(); } outerSolvingEnded(solverScope); return solverScope.getBestSolution(); } public void outerSolvingStarted(DefaultSolverScope<Solution_> solverScope) { solving.set(true); basicPlumbingTermination.resetTerminateEarly(); solverScope.setStartingSolverCount(0); solverScope.setWorkingRandom(randomFactory.createRandom()); } @Override public void solvingStarted(DefaultSolverScope<Solution_> solverScope) { solverScope.startingNow(); solverScope.getScoreDirector().resetCalculationCount(); super.solvingStarted(solverScope); int startingSolverCount = solverScope.getStartingSolverCount() + 1; solverScope.setStartingSolverCount(startingSolverCount); logger.info("Solving {}: time spent ({}), best score ({}), environment mode ({}), random ({}).", (startingSolverCount == 1 ? "started" : "restarted"), solverScope.calculateTimeMillisSpentUpToNow(), solverScope.getBestScore(), environmentMode.name(), (randomFactory != null ? randomFactory : "not fixed")); } @Override public void solvingEnded(DefaultSolverScope<Solution_> solverScope) { super.solvingEnded(solverScope); solverScope.endingNow(); } public void outerSolvingEnded(DefaultSolverScope<Solution_> solverScope) { // Must be kept open for doProblemFactChange solverScope.getScoreDirector().dispose(); logger.info("Solving ended: time spent ({}), best score ({}), score calculation speed ({}/sec)," + " phase total ({}), environment mode ({}).", solverScope.getTimeMillisSpent(), solverScope.getBestScore(), solverScope.getScoreCalculationSpeed(), phaseList.size(), environmentMode.name()); solving.set(false); } private boolean checkProblemFactChanges() { boolean restartSolver = basicPlumbingTermination.waitForRestartSolverDecision(); if (!restartSolver) { return false; } else { BlockingQueue<ProblemFactChange> problemFactChangeQueue = basicPlumbingTermination.startProblemFactChangesProcessing(); solverScope.setWorkingSolutionFromBestSolution(); Score score = null; int stepIndex = 0; ProblemFactChange<Solution_> problemFactChange = problemFactChangeQueue.poll(); while (problemFactChange != null) { score = doProblemFactChange(problemFactChange, stepIndex); stepIndex++; problemFactChange = problemFactChangeQueue.poll(); } basicPlumbingTermination.endProblemFactChangesProcessing(); bestSolutionRecaller.updateBestSolution(solverScope); logger.info("Real-time problem fact changes done: step total ({}), new best score ({}).", stepIndex, score); return true; } } private Score doProblemFactChange(ProblemFactChange<Solution_> problemFactChange, int stepIndex) { problemFactChange.doChange(solverScope.getScoreDirector()); Score score = solverScope.calculateScore(); logger.debug(" Step index ({}), new score ({}) for real-time problem fact change.", stepIndex, score); return score; } }