Skip to content

Commit

Permalink
PLANNER-491 Extract PartSolver + create ScoreDirector during Config.b…
Browse files Browse the repository at this point in the history
…uild instead of during outerSolverStarted
  • Loading branch information
ge0ffrey committed Oct 12, 2016
1 parent 8c79c51 commit 118fe4a
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 158 deletions.
Expand Up @@ -80,7 +80,7 @@ public List<File> getGraphFileList() {
@Override @Override
public void open(Solver<Solution_> solver) { public void open(Solver<Solution_> solver) {
DefaultSolver<Solution_> defaultSolver = (DefaultSolver<Solution_>) solver; DefaultSolver<Solution_> defaultSolver = (DefaultSolver<Solution_>) solver;
defaultSolver.setConstraintMatchEnabledPreference(true); defaultSolver.getSolverScope().getScoreDirector().overwriteConstraintMatchEnabledPreference(true);
defaultSolver.addPhaseLifecycleListener(listener); defaultSolver.addPhaseLifecycleListener(listener);
} }


Expand Down
Expand Up @@ -79,7 +79,7 @@ public List<File> getGraphFileList() {
@Override @Override
public void open(Solver<Solution_> solver) { public void open(Solver<Solution_> solver) {
DefaultSolver<Solution_> defaultSolver = (DefaultSolver<Solution_>) solver; DefaultSolver<Solution_> defaultSolver = (DefaultSolver<Solution_>) solver;
defaultSolver.setConstraintMatchEnabledPreference(true); defaultSolver.getSolverScope().getScoreDirector().overwriteConstraintMatchEnabledPreference(true);
defaultSolver.addPhaseLifecycleListener(listener); defaultSolver.addPhaseLifecycleListener(listener);
} }


Expand Down
Expand Up @@ -44,6 +44,7 @@
import org.optaplanner.core.impl.solver.random.DefaultRandomFactory; import org.optaplanner.core.impl.solver.random.DefaultRandomFactory;
import org.optaplanner.core.impl.solver.random.RandomFactory; import org.optaplanner.core.impl.solver.random.RandomFactory;
import org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller; 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.optaplanner.core.impl.solver.termination.Termination;


import static org.apache.commons.lang3.ObjectUtils.*; import static org.apache.commons.lang3.ObjectUtils.*;
Expand Down Expand Up @@ -208,32 +209,30 @@ public void offerRandomSeedFromSubSingleIndex(long subSingleIndex) {
*/ */
public <Solution_> Solver<Solution_> buildSolver(SolverConfigContext configContext) { public <Solution_> Solver<Solution_> buildSolver(SolverConfigContext configContext) {
configContext.validate(); configContext.validate();
DefaultSolver<Solution_> solver = new DefaultSolver<>();
EnvironmentMode environmentMode_ = determineEnvironmentMode(); EnvironmentMode environmentMode_ = determineEnvironmentMode();
solver.setEnvironmentMode(environmentMode_);
boolean daemon_ = defaultIfNull(daemon, false); boolean daemon_ = defaultIfNull(daemon, false);
BasicPlumbingTermination basicPlumbingTermination = new BasicPlumbingTermination(daemon_);
solver.setBasicPlumbingTermination(basicPlumbingTermination);


solver.setRandomFactory(buildRandomFactory(environmentMode_)); RandomFactory randomFactory = buildRandomFactory(environmentMode_);
SolutionDescriptor<Solution_> solutionDescriptor = buildSolutionDescriptor(configContext); SolutionDescriptor<Solution_> solutionDescriptor = buildSolutionDescriptor(configContext);
ScoreDirectorFactoryConfig scoreDirectorFactoryConfig_ ScoreDirectorFactoryConfig scoreDirectorFactoryConfig_
= scoreDirectorFactoryConfig == null ? new ScoreDirectorFactoryConfig() = scoreDirectorFactoryConfig == null ? new ScoreDirectorFactoryConfig()
: scoreDirectorFactoryConfig; : scoreDirectorFactoryConfig;
InnerScoreDirectorFactory<Solution_> scoreDirectorFactory = scoreDirectorFactoryConfig_.buildScoreDirectorFactory( InnerScoreDirectorFactory<Solution_> scoreDirectorFactory = scoreDirectorFactoryConfig_.buildScoreDirectorFactory(
configContext, environmentMode_, solutionDescriptor); configContext, environmentMode_, solutionDescriptor);
solver.setConstraintMatchEnabledPreference(environmentMode_.isAsserted()); boolean constraintMatchEnabledPreference = environmentMode_.isAsserted();
solver.setScoreDirectorFactory(scoreDirectorFactory); DefaultSolverScope<Solution_> solverScope = new DefaultSolverScope<>();
solverScope.setScoreDirector(scoreDirectorFactory.buildScoreDirector(constraintMatchEnabledPreference));


HeuristicConfigPolicy configPolicy = new HeuristicConfigPolicy(environmentMode_, scoreDirectorFactory); HeuristicConfigPolicy configPolicy = new HeuristicConfigPolicy(environmentMode_, scoreDirectorFactory);
TerminationConfig terminationConfig_ = terminationConfig == null ? new TerminationConfig() TerminationConfig terminationConfig_ = terminationConfig == null ? new TerminationConfig()
: terminationConfig; : terminationConfig;
BasicPlumbingTermination basicPlumbingTermination = new BasicPlumbingTermination(daemon_);
Termination termination = terminationConfig_.buildTermination(configPolicy, basicPlumbingTermination); Termination termination = terminationConfig_.buildTermination(configPolicy, basicPlumbingTermination);
solver.setTermination(termination);
BestSolutionRecaller<Solution_> bestSolutionRecaller = new BestSolutionRecallerConfig() BestSolutionRecaller<Solution_> bestSolutionRecaller = new BestSolutionRecallerConfig()
.buildBestSolutionRecaller(environmentMode_); .buildBestSolutionRecaller(environmentMode_);
solver.setBestSolutionRecaller(bestSolutionRecaller); List<Phase<Solution_>> phaseList = buildPhaseList(configPolicy, bestSolutionRecaller, termination);
solver.setPhaseList(buildPhaseList(configPolicy, bestSolutionRecaller, termination)); DefaultSolver<Solution_> solver = new DefaultSolver<>(environmentMode_, randomFactory,
basicPlumbingTermination, termination, bestSolutionRecaller, phaseList, solverScope);
return solver; return solver;
} }


Expand Down Expand Up @@ -283,7 +282,7 @@ public <Solution_> SolutionDescriptor<Solution_> buildSolutionDescriptor(SolverC
} }
} }


protected List<Phase> buildPhaseList(HeuristicConfigPolicy configPolicy, protected <Solution_> List<Phase<Solution_>> buildPhaseList(HeuristicConfigPolicy configPolicy,
BestSolutionRecaller bestSolutionRecaller, BestSolutionRecaller bestSolutionRecaller,
Termination termination) { Termination termination) {
List<PhaseConfig> phaseConfigList_ = phaseConfigList; List<PhaseConfig> phaseConfigList_ = phaseConfigList;
Expand All @@ -292,10 +291,10 @@ protected List<Phase> buildPhaseList(HeuristicConfigPolicy configPolicy,
new ConstructionHeuristicPhaseConfig(), new ConstructionHeuristicPhaseConfig(),
new LocalSearchPhaseConfig()); new LocalSearchPhaseConfig());
} }
List<Phase> phaseList = new ArrayList<>(phaseConfigList_.size()); List<Phase<Solution_>> phaseList = new ArrayList<>(phaseConfigList_.size());
int phaseIndex = 0; int phaseIndex = 0;
for (PhaseConfig phaseConfig : phaseConfigList_) { for (PhaseConfig phaseConfig : phaseConfigList_) {
Phase phase = phaseConfig.buildPhase(phaseIndex, configPolicy, Phase<Solution_> phase = phaseConfig.buildPhase(phaseIndex, configPolicy,
bestSolutionRecaller, termination); bestSolutionRecaller, termination);
phaseList.add(phase); phaseList.add(phase);
phaseIndex++; phaseIndex++;
Expand Down
Expand Up @@ -65,11 +65,9 @@ public void linkVariableListeners() {
notificationQueuesAreEmpty = true; notificationQueuesAreEmpty = true;
for (EntityDescriptor<Solution_> entityDescriptor : scoreDirector.getSolutionDescriptor().getEntityDescriptors()) { for (EntityDescriptor<Solution_> entityDescriptor : scoreDirector.getSolutionDescriptor().getEntityDescriptors()) {
for (VariableDescriptor variableDescriptor : entityDescriptor.getDeclaredVariableDescriptors()) { for (VariableDescriptor variableDescriptor : entityDescriptor.getDeclaredVariableDescriptors()) {
List<VariableListenerNotifiable> variableNotifiableList = new ArrayList<>(); sourceVariableToNotifiableMap.put(variableDescriptor, new ArrayList<>());
sourceVariableToNotifiableMap.put(variableDescriptor, variableNotifiableList);
} }
List<VariableListenerNotifiable> entityNotifiableList = new ArrayList<>(); sourceEntityToNotifiableMap.put(entityDescriptor, new ArrayList<>());
sourceEntityToNotifiableMap.put(entityDescriptor, entityNotifiableList);
} }
for (EntityDescriptor<Solution_> entityDescriptor : scoreDirector.getSolutionDescriptor().getEntityDescriptors()) { for (EntityDescriptor<Solution_> entityDescriptor : scoreDirector.getSolutionDescriptor().getEntityDescriptors()) {
for (ShadowVariableDescriptor<Solution_> shadowVariableDescriptor : entityDescriptor.getDeclaredShadowVariableDescriptors()) { for (ShadowVariableDescriptor<Solution_> shadowVariableDescriptor : entityDescriptor.getDeclaredShadowVariableDescriptors()) {
Expand Down
Expand Up @@ -29,7 +29,6 @@
import org.optaplanner.core.impl.partitionedsearch.partitioner.SolutionPartitioner; import org.optaplanner.core.impl.partitionedsearch.partitioner.SolutionPartitioner;
import org.optaplanner.core.impl.phase.AbstractPhase; import org.optaplanner.core.impl.phase.AbstractPhase;
import org.optaplanner.core.impl.phase.Phase; import org.optaplanner.core.impl.phase.Phase;
import org.optaplanner.core.impl.phase.event.PhaseLifecycleSupport;
import org.optaplanner.core.impl.solver.ChildThreadType; import org.optaplanner.core.impl.solver.ChildThreadType;
import org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller; import org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller;
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope; import org.optaplanner.core.impl.solver.scope.DefaultSolverScope;
Expand Down Expand Up @@ -77,8 +76,8 @@ public void solve(DefaultSolverScope<Solution_> solverScope) {
List<Solution_> partList = solutionPartitioner.splitWorkingSolution(solverScope.getScoreDirector()); List<Solution_> partList = solutionPartitioner.splitWorkingSolution(solverScope.getScoreDirector());
List<Future> futureList = new ArrayList<>(partList.size()); List<Future> futureList = new ArrayList<>(partList.size());
for (Solution_ part : partList) { for (Solution_ part : partList) {
PartPhaseRunner partPhaseRunner = new PartPhaseRunner(part, solverScope); PartSolver<Solution_> partSolver = buildPartSolver(solverScope);
Future<?> future = executorService.submit(partPhaseRunner); Future<?> future = executorService.submit(() -> partSolver.solve(part));
futureList.add(future); futureList.add(future);
} }
for (Future future : futureList) { for (Future future : futureList) {
Expand Down Expand Up @@ -123,58 +122,21 @@ public void solve(DefaultSolverScope<Solution_> solverScope) {
// phaseEnded(phaseScope); // phaseEnded(phaseScope);
} }


protected class PartPhaseRunner implements Runnable { public PartSolver<Solution_> buildPartSolver(DefaultSolverScope<Solution_> solverScope) {

Termination partTermination = termination.createChildThreadTermination(solverScope, ChildThreadType.PART_THREAD);
protected final Solution_ part; BestSolutionRecaller<Solution_> bestSolutionRecaller = new BestSolutionRecallerConfig()
protected final PhaseLifecycleSupport<Solution_> partPhaseLifecycleSupport = new PhaseLifecycleSupport<>(); .buildBestSolutionRecaller(configPolicy.getEnvironmentMode());
protected final BestSolutionRecaller<Solution_> partBestSolutionRecaller; List<Phase<Solution_>> phaseList = new ArrayList<>(phaseConfigList.size());
protected final Termination partTermination; int partPhaseIndex = 0;
protected final List<Phase<Solution_>> phaseList; for (PhaseConfig phaseConfig : phaseConfigList) {

phaseList.add(phaseConfig.buildPhase(partPhaseIndex, configPolicy, bestSolutionRecaller, partTermination));
protected final DefaultSolverScope<Solution_> partSolverScope; partPhaseIndex++;

public PartPhaseRunner(Solution_ part, DefaultSolverScope<Solution_> solverScope) {
this.part = part;
phaseList = new ArrayList<>(phaseConfigList.size());
partBestSolutionRecaller = new BestSolutionRecallerConfig()
.buildBestSolutionRecaller(configPolicy.getEnvironmentMode());
// partBestSolutionRecaller.setSolverEventSupport(this); // TODO
partTermination = termination.createChildThreadTermination(solverScope, ChildThreadType.PART_THREAD);
int phaseIndex = 0;
for (PhaseConfig phaseConfig : phaseConfigList) {
Phase phase = phaseConfig.buildPhase(phaseIndex, configPolicy, partBestSolutionRecaller, partTermination);
phase.setSolverPhaseLifecycleSupport(partPhaseLifecycleSupport);
phaseList.add(phase);
phaseIndex++;
}
partSolverScope = solverScope.createChildThreadSolverScope(ChildThreadType.PART_THREAD);
} }

DefaultSolverScope<Solution_> partSolverScope = solverScope.createChildThreadSolverScope(ChildThreadType.PART_THREAD);
@Override return new PartSolver<>(partTermination, bestSolutionRecaller, phaseList, partSolverScope);
public void run() {
partSolverScope.setBestSolution(part);
partSolverScope.setWorkingSolutionFromBestSolution();
partBestSolutionRecaller.solvingStarted(partSolverScope);
phaseLifecycleSupport.fireSolvingStarted(partSolverScope);
for (Phase<Solution_> phase : phaseList) {
phase.solvingStarted(partSolverScope);
}
for (Phase<Solution_> phase : phaseList) {
phase.solve(partSolverScope);
}
for (Phase<Solution_> phase : phaseList) {
phase.solvingEnded(partSolverScope);
}
phaseLifecycleSupport.fireSolvingEnded(partSolverScope);
partBestSolutionRecaller.solvingEnded(partSolverScope);
partSolverScope.endingNow();
partSolverScope.getScoreDirector().dispose();
// TODO log?
}

} }


// private void doStep(PartitionedSearchStepScope<Solution_> stepScope) { // private void doStep(PartitionedSearchStepScope<Solution_> stepScope) {
// Move nextStep = stepScope.getStep(); // Move nextStep = stepScope.getStep();
// nextStep.doMove(stepScope.getScoreDirector()); // nextStep.doMove(stepScope.getScoreDirector());
// predictWorkingStepScore(stepScope, nextStep); // predictWorkingStepScore(stepScope, nextStep);
Expand Down
@@ -0,0 +1,137 @@
/*
* Copyright 2016 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.partitionedsearch;

import java.util.List;

import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.score.Score;
import org.optaplanner.core.impl.phase.Phase;
import org.optaplanner.core.impl.score.director.ScoreDirectorFactory;
import org.optaplanner.core.impl.solver.AbstractSolver;
import org.optaplanner.core.impl.solver.ProblemFactChange;
import org.optaplanner.core.impl.solver.recaller.BestSolutionRecaller;
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope;
import org.optaplanner.core.impl.solver.termination.Termination;

/**
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/
public class PartSolver<Solution_> extends AbstractSolver<Solution_> {

protected final Termination termination;
protected final BestSolutionRecaller<Solution_> bestSolutionRecaller;
protected final List<Phase<Solution_>> phaseList;

protected final DefaultSolverScope<Solution_> solverScope;

// ************************************************************************
// Constructors and simple getters/setters
// ************************************************************************

public PartSolver(Termination termination, BestSolutionRecaller<Solution_> bestSolutionRecaller,
List<Phase<Solution_>> phaseList, DefaultSolverScope<Solution_> solverScope) {
this.termination = termination;
this.bestSolutionRecaller = bestSolutionRecaller;
bestSolutionRecaller.setSolverEventSupport(solverEventSupport);
this.phaseList = phaseList;
for (Phase<Solution_> phase : phaseList) {
phase.setSolverPhaseLifecycleSupport(phaseLifecycleSupport);
}
this.solverScope = solverScope;
}

@Override
public Solution_ solve(Solution_ part) {
solverScope.setBestSolution(part);
solverScope.setWorkingSolutionFromBestSolution();
solvingStarted(solverScope);
for (Phase<Solution_> phase : phaseList) {
phase.solve(solverScope);
}
solvingEnded(solverScope);
solverScope.endingNow();
solverScope.getScoreDirector().dispose();
// TODO log?
return solverScope.getBestSolution();
}

public void solvingStarted(DefaultSolverScope<Solution_> solverScope) {
bestSolutionRecaller.solvingStarted(solverScope);
phaseLifecycleSupport.fireSolvingStarted(solverScope);
for (Phase<Solution_> phase : phaseList) {
phase.solvingStarted(solverScope);
}
}

public void solvingEnded(DefaultSolverScope<Solution_> solverScope) {
for (Phase<Solution_> phase : phaseList) {
phase.solvingEnded(solverScope);
}
phaseLifecycleSupport.fireSolvingEnded(solverScope);
bestSolutionRecaller.solvingEnded(solverScope);
}



// TODO remove these

@Override
public Solution_ getBestSolution() {
throw new UnsupportedOperationException();
}

@Override
public Score getBestScore() {
throw new UnsupportedOperationException();
}

@Override
public long getTimeMillisSpent() {
throw new UnsupportedOperationException();
}

@Override
public boolean isSolving() {
throw new UnsupportedOperationException();
}

@Override
public boolean terminateEarly() {
throw new UnsupportedOperationException();
}

@Override
public boolean isTerminateEarly() {
throw new UnsupportedOperationException();
}

@Override
public boolean addProblemFactChange(ProblemFactChange<Solution_> problemFactChange) {
throw new UnsupportedOperationException();
}

@Override
public boolean isEveryProblemFactChangeProcessed() {
throw new UnsupportedOperationException();
}

@Override
public ScoreDirectorFactory<Solution_> getScoreDirectorFactory() {
throw new UnsupportedOperationException();
}
}
Expand Up @@ -60,7 +60,7 @@ public abstract class AbstractScoreDirector<Solution_, Factory_ extends Abstract
protected final transient Logger logger = LoggerFactory.getLogger(getClass()); protected final transient Logger logger = LoggerFactory.getLogger(getClass());


protected final Factory_ scoreDirectorFactory; protected final Factory_ scoreDirectorFactory;
protected final boolean constraintMatchEnabledPreference; protected boolean constraintMatchEnabledPreference;
protected final VariableListenerSupport<Solution_> variableListenerSupport; protected final VariableListenerSupport<Solution_> variableListenerSupport;


protected Solution_ workingSolution; protected Solution_ workingSolution;
Expand Down Expand Up @@ -93,6 +93,15 @@ public ScoreDefinition getScoreDefinition() {
return scoreDirectorFactory.getScoreDefinition(); return scoreDirectorFactory.getScoreDefinition();
} }


public boolean isConstraintMatchEnabledPreference() {
return constraintMatchEnabledPreference;
}

@Override
public void overwriteConstraintMatchEnabledPreference(boolean constraintMatchEnabledPreference) {
this.constraintMatchEnabledPreference = constraintMatchEnabledPreference;
}

@Override @Override
public Solution_ getWorkingSolution() { public Solution_ getWorkingSolution() {
return workingSolution; return workingSolution;
Expand Down
Expand Up @@ -20,6 +20,7 @@


import org.optaplanner.core.api.domain.solution.PlanningSolution; import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.score.Score; import org.optaplanner.core.api.score.Score;
import org.optaplanner.core.api.score.constraint.ConstraintMatch;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor; import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.domain.variable.listener.VariableListener; import org.optaplanner.core.impl.domain.variable.listener.VariableListener;
import org.optaplanner.core.impl.domain.variable.supply.SupplyManager; import org.optaplanner.core.impl.domain.variable.supply.SupplyManager;
Expand All @@ -31,6 +32,12 @@
*/ */
public interface InnerScoreDirector<Solution_> extends ScoreDirector<Solution_> { public interface InnerScoreDirector<Solution_> extends ScoreDirector<Solution_> {


/**
* @param constraintMatchEnabledPreference false if a {@link ScoreDirector} implementation
* should not do {@link ConstraintMatch} tracking even if it supports it.
*/
void overwriteConstraintMatchEnabledPreference(boolean constraintMatchEnabledPreference);

/** /**
* @return used to check {@link #isWorkingEntityListDirty(long)} later on * @return used to check {@link #isWorkingEntityListDirty(long)} later on
*/ */
Expand Down

0 comments on commit 118fe4a

Please sign in to comment.