Skip to content

Commit

Permalink
PLANNER-476 CalculateCountTermination
Browse files Browse the repository at this point in the history
  • Loading branch information
ge0ffrey committed Nov 5, 2015
1 parent ee52197 commit 24f599c
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 26 deletions.
Expand Up @@ -31,6 +31,7 @@
import org.optaplanner.core.impl.solver.termination.AndCompositeTermination;
import org.optaplanner.core.impl.solver.termination.BestScoreFeasibleTermination;
import org.optaplanner.core.impl.solver.termination.BestScoreTermination;
import org.optaplanner.core.impl.solver.termination.CalculateCountTermination;
import org.optaplanner.core.impl.solver.termination.OrCompositeTermination;
import org.optaplanner.core.impl.solver.termination.StepCountTermination;
import org.optaplanner.core.impl.solver.termination.Termination;
Expand Down Expand Up @@ -62,6 +63,8 @@ public class TerminationConfig extends AbstractConfig<TerminationConfig> {
private Integer stepCountLimit = null;
private Integer unimprovedStepCountLimit = null;

private Long calculateCountLimit = null;

@XStreamImplicit(itemFieldName = "termination")
private List<TerminationConfig> terminationConfigList = null;

Expand Down Expand Up @@ -193,6 +196,14 @@ public void setUnimprovedStepCountLimit(Integer unimprovedStepCountLimit) {
this.unimprovedStepCountLimit = unimprovedStepCountLimit;
}

public Long getCalculateCountLimit() {
return calculateCountLimit;
}

public void setCalculateCountLimit(Long calculateCountLimit) {
this.calculateCountLimit = calculateCountLimit;
}

public List<TerminationConfig> getTerminationConfigList() {
return terminationConfigList;
}
Expand Down Expand Up @@ -264,6 +275,9 @@ public Termination buildTermination(HeuristicConfigPolicy configPolicy) {
if (stepCountLimit != null) {
terminationList.add(new StepCountTermination(stepCountLimit));
}
if (calculateCountLimit != null) {
terminationList.add(new CalculateCountTermination(calculateCountLimit));
}
if (unimprovedStepCountLimit != null) {
terminationList.add(new UnimprovedStepCountTermination(unimprovedStepCountLimit));
}
Expand Down Expand Up @@ -425,6 +439,8 @@ public void inherit(TerminationConfig inheritedConfig) {
inheritedConfig.getStepCountLimit());
unimprovedStepCountLimit = ConfigUtils.inheritOverwritableProperty(unimprovedStepCountLimit,
inheritedConfig.getUnimprovedStepCountLimit());
calculateCountLimit = ConfigUtils.inheritOverwritableProperty(calculateCountLimit,
inheritedConfig.getCalculateCountLimit());
terminationConfigList = ConfigUtils.inheritMergeableListConfig(
terminationConfigList, inheritedConfig.getTerminationConfigList());
}
Expand Down
@@ -0,0 +1,70 @@
/*
* Copyright 2015 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.optaplanner.core.impl.solver.termination;

import org.optaplanner.core.impl.phase.scope.AbstractPhaseScope;
import org.optaplanner.core.impl.score.director.InnerScoreDirector;
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope;

public class CalculateCountTermination extends AbstractTermination {

private final long calculateCountLimit;

public CalculateCountTermination(long calculateCountLimit) {
this.calculateCountLimit = calculateCountLimit;
if (calculateCountLimit < 0L) {
throw new IllegalArgumentException("The calculateCountLimit (" + calculateCountLimit
+ ") cannot be negative.");
}
}

// ************************************************************************
// Terminated methods
// ************************************************************************

public boolean isSolverTerminated(DefaultSolverScope solverScope) {
return isTerminated(solverScope.getScoreDirector());
}

public boolean isPhaseTerminated(AbstractPhaseScope phaseScope) {
return isTerminated(phaseScope.getScoreDirector());
}

protected boolean isTerminated(InnerScoreDirector scoreDirector) {
long calculateCount = scoreDirector.getCalculateCount();
return calculateCount >= calculateCountLimit;
}

// ************************************************************************
// Time gradient methods
// ************************************************************************

public double calculateSolverTimeGradient(DefaultSolverScope solverScope) {
return calculateTimeGradient(solverScope.getScoreDirector());
}

public double calculatePhaseTimeGradient(AbstractPhaseScope phaseScope) {
return calculateTimeGradient(phaseScope.getScoreDirector());
}

protected double calculateTimeGradient(InnerScoreDirector scoreDirector) {
long calculateCount = scoreDirector.getCalculateCount();
double timeGradient = ((double) calculateCount) / ((double) calculateCountLimit);
return Math.min(timeGradient, 1.0);
}

}
@@ -0,0 +1,83 @@
/*
* Copyright 2015 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.optaplanner.core.impl.solver.termination;

import org.junit.Test;
import org.optaplanner.core.impl.phase.scope.AbstractPhaseScope;
import org.optaplanner.core.impl.score.director.InnerScoreDirector;
import org.optaplanner.core.impl.solver.scope.DefaultSolverScope;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

public class CalculateCountTerminationTest {

@Test
public void solveTermination() {
Termination termination = new CalculateCountTermination(1000L);
DefaultSolverScope solverScope = mock(DefaultSolverScope.class);
InnerScoreDirector scoreDirector = mock(InnerScoreDirector.class);
when(solverScope.getScoreDirector()).thenReturn(scoreDirector);

when(scoreDirector.getCalculateCount()).thenReturn(0L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.0, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(100L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.1, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(500L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.5, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(700L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.7, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(1000L);
assertEquals(true, termination.isSolverTerminated(solverScope));
assertEquals(1.0, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(1200L);
assertEquals(true, termination.isSolverTerminated(solverScope));
assertEquals(1.0, termination.calculateSolverTimeGradient(solverScope), 0.0);
}

@Test
public void phaseTermination() {
Termination termination = new CalculateCountTermination(1000L);
AbstractPhaseScope phaseScope = mock(AbstractPhaseScope.class);
InnerScoreDirector scoreDirector = mock(InnerScoreDirector.class);
when(phaseScope.getScoreDirector()).thenReturn(scoreDirector);

when(scoreDirector.getCalculateCount()).thenReturn(0L);
assertEquals(false, termination.isPhaseTerminated(phaseScope));
assertEquals(0.0, termination.calculatePhaseTimeGradient(phaseScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(100L);
assertEquals(false, termination.isPhaseTerminated(phaseScope));
assertEquals(0.1, termination.calculatePhaseTimeGradient(phaseScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(500L);
assertEquals(false, termination.isPhaseTerminated(phaseScope));
assertEquals(0.5, termination.calculatePhaseTimeGradient(phaseScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(700L);
assertEquals(false, termination.isPhaseTerminated(phaseScope));
assertEquals(0.7, termination.calculatePhaseTimeGradient(phaseScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(1000L);
assertEquals(true, termination.isPhaseTerminated(phaseScope));
assertEquals(1.0, termination.calculatePhaseTimeGradient(phaseScope), 0.0);
when(scoreDirector.getCalculateCount()).thenReturn(1200L);
assertEquals(true, termination.isPhaseTerminated(phaseScope));
assertEquals(1.0, termination.calculatePhaseTimeGradient(phaseScope), 0.0);
}

}
Expand Up @@ -28,26 +28,26 @@ public class TimeMillisSpentTerminationTest {
@Test
public void solveTermination() {
Termination termination = new TimeMillisSpentTermination(1000L);
DefaultSolverScope phaseScope = mock(DefaultSolverScope.class);
DefaultSolverScope solverScope = mock(DefaultSolverScope.class);

when(phaseScope.calculateTimeMillisSpent()).thenReturn(0L);
assertEquals(false, termination.isSolverTerminated(phaseScope));
assertEquals(0.0, termination.calculateSolverTimeGradient(phaseScope), 0.0);
when(phaseScope.calculateTimeMillisSpent()).thenReturn(100L);
assertEquals(false, termination.isSolverTerminated(phaseScope));
assertEquals(0.1, termination.calculateSolverTimeGradient(phaseScope), 0.0);
when(phaseScope.calculateTimeMillisSpent()).thenReturn(500L);
assertEquals(false, termination.isSolverTerminated(phaseScope));
assertEquals(0.5, termination.calculateSolverTimeGradient(phaseScope), 0.0);
when(phaseScope.calculateTimeMillisSpent()).thenReturn(700L);
assertEquals(false, termination.isSolverTerminated(phaseScope));
assertEquals(0.7, termination.calculateSolverTimeGradient(phaseScope), 0.0);
when(phaseScope.calculateTimeMillisSpent()).thenReturn(1000L);
assertEquals(true, termination.isSolverTerminated(phaseScope));
assertEquals(1.0, termination.calculateSolverTimeGradient(phaseScope), 0.0);
when(phaseScope.calculateTimeMillisSpent()).thenReturn(1200L);
assertEquals(true, termination.isSolverTerminated(phaseScope));
assertEquals(1.0, termination.calculateSolverTimeGradient(phaseScope), 0.0);
when(solverScope.calculateTimeMillisSpent()).thenReturn(0L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.0, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(solverScope.calculateTimeMillisSpent()).thenReturn(100L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.1, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(solverScope.calculateTimeMillisSpent()).thenReturn(500L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.5, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(solverScope.calculateTimeMillisSpent()).thenReturn(700L);
assertEquals(false, termination.isSolverTerminated(solverScope));
assertEquals(0.7, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(solverScope.calculateTimeMillisSpent()).thenReturn(1000L);
assertEquals(true, termination.isSolverTerminated(solverScope));
assertEquals(1.0, termination.calculateSolverTimeGradient(solverScope), 0.0);
when(solverScope.calculateTimeMillisSpent()).thenReturn(1200L);
assertEquals(true, termination.isSolverTerminated(solverScope));
assertEquals(1.0, termination.calculateSolverTimeGradient(solverScope), 0.0);
}

@Test
Expand Down
Expand Up @@ -716,7 +716,7 @@
<section xml:id="timeMillisSpentTermination">
<title>TimeMillisSpentTermination</title>

<para>Terminates when an amount of time has been used:</para>
<para>Terminates when an amount of time has been used.</para>

<programlisting language="xml"> &lt;termination&gt;
&lt;millisecondsSpentLimit&gt;500&lt;/millisecondsSpentLimit&gt;
Expand Down Expand Up @@ -774,7 +774,7 @@
<section xml:id="unimprovedTimeMillisSpentTermination">
<title>UnimprovedTimeMillisSpentTermination</title>

<para>Terminates when the best score hasn't improved in an amount of time:</para>
<para>Terminates when the best score hasn't improved in an amount of time.</para>

<programlisting language="xml"> &lt;localSearch&gt;
&lt;termination&gt;
Expand Down Expand Up @@ -832,9 +832,8 @@
<section xml:id="bestScoreTermination">
<title>BestScoreTermination</title>

<para>Terminates when a certain score has been reached. You can use this <literal>Termination</literal> if you
know the perfect score, for example for 4 queens (which uses a <link
linkend="simpleScore">SimpleScore</link>):</para>
<para>Terminates when a certain score has been reached. Use this <literal>Termination</literal> if you know the
perfect score, for example for 4 queens (which uses a <link linkend="simpleScore">SimpleScore</link>):</para>

<programlisting language="xml"> &lt;termination&gt;
&lt;bestScoreLimit&gt;0&lt;/bestScoreLimit&gt;
Expand Down Expand Up @@ -875,7 +874,8 @@
<section xml:id="stepCountTermination">
<title>StepCountTermination</title>

<para>Terminates when an amount of steps has been reached:</para>
<para>Terminates when a number of steps has been reached. This is useful for hardware performance independent
runs.</para>

<programlisting language="xml"> &lt;localSearch&gt;
&lt;termination&gt;
Expand All @@ -890,7 +890,8 @@
<section xml:id="unimprovedStepCountTermination">
<title>UnimprovedStepCountTermination</title>

<para>Terminates when the best score hasn't improved in a number of steps:</para>
<para>Terminates when the best score hasn't improved in a number of steps. This is useful for hardware performance
independent runs.</para>

<programlisting language="xml"> &lt;localSearch&gt;
&lt;termination&gt;
Expand All @@ -906,6 +907,20 @@
<literal>&lt;localSearch&gt;</literal>), not for the <literal>Solver</literal> itself.</para>
</section>

<section xml:id="calculateCountTermination">
<title>CalculateCountTermination</title>

<para>Terminates when a number of score calculations (which is usually the sum of the number of moves and the
number of steps) have been reached. This is useful for benchmarking.</para>

<programlisting language="xml"> &lt;termination&gt;
&lt;calculateCountLimit&gt;100000&lt;/calculateCountLimit&gt;
&lt;/termination&gt;</programlisting>

<para>Switching <link linkend="environmentMode">EnvironmentMode</link> can heavily impact when this termination
ends.</para>
</section>

<section xml:id="combiningMultipleTerminations">
<title>Combining Multiple Terminations</title>

Expand Down

0 comments on commit 24f599c

Please sign in to comment.