Skip to content

Commit

Permalink
chore: improve approach
Browse files Browse the repository at this point in the history
  • Loading branch information
zepfred committed Nov 27, 2024
1 parent 49b9ffd commit e8fb143
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 53 deletions.
2 changes: 1 addition & 1 deletion benchmark/src/main/resources/benchmark.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@
<xs:element minOccurs="0" name="noStopFlatLineDetectionRatio" type="xs:double"/>


<xs:element minOccurs="0" name="minimalExecutionTimeSeconds" type="xs:long"/>
<xs:element minOccurs="0" name="delayFlatLineSecondsSpentLimit" type="xs:long"/>


<xs:element maxOccurs="unbounded" minOccurs="0" name="termination" type="tns:terminationConfig"/>
Expand Down
2 changes: 1 addition & 1 deletion core/src/build/revapi-differences.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
"annotationType": "jakarta.xml.bind.annotation.XmlType",
"attribute": "propOrder",
"oldValue": "{\"terminationClass\", \"terminationCompositionStyle\", \"spentLimit\", \"millisecondsSpentLimit\", \"secondsSpentLimit\", \"minutesSpentLimit\", \"hoursSpentLimit\", \"daysSpentLimit\", \"unimprovedSpentLimit\", \"unimprovedMillisecondsSpentLimit\", \"unimprovedSecondsSpentLimit\", \"unimprovedMinutesSpentLimit\", \"unimprovedHoursSpentLimit\", \"unimprovedDaysSpentLimit\", \"unimprovedScoreDifferenceThreshold\", \"bestScoreLimit\", \"bestScoreFeasible\", \"stepCountLimit\", \"unimprovedStepCountLimit\", \"scoreCalculationCountLimit\", \"terminationConfigList\"}",
"newValue": "{\"terminationClass\", \"terminationCompositionStyle\", \"spentLimit\", \"millisecondsSpentLimit\", \"secondsSpentLimit\", \"minutesSpentLimit\", \"hoursSpentLimit\", \"daysSpentLimit\", \"unimprovedSpentLimit\", \"unimprovedMillisecondsSpentLimit\", \"unimprovedSecondsSpentLimit\", \"unimprovedMinutesSpentLimit\", \"unimprovedHoursSpentLimit\", \"unimprovedDaysSpentLimit\", \"unimprovedScoreDifferenceThreshold\", \"bestScoreLimit\", \"bestScoreFeasible\", \"stepCountLimit\", \"unimprovedStepCountLimit\", \"scoreCalculationCountLimit\", \"moveCountLimit\", \"stopFlatLineDetectionRatio\", \"noStopFlatLineDetectionRatio\", \"minimalExecutionTimeSeconds\", \"terminationConfigList\"}",
"newValue": "{\"terminationClass\", \"terminationCompositionStyle\", \"spentLimit\", \"millisecondsSpentLimit\", \"secondsSpentLimit\", \"minutesSpentLimit\", \"hoursSpentLimit\", \"daysSpentLimit\", \"unimprovedSpentLimit\", \"unimprovedMillisecondsSpentLimit\", \"unimprovedSecondsSpentLimit\", \"unimprovedMinutesSpentLimit\", \"unimprovedHoursSpentLimit\", \"unimprovedDaysSpentLimit\", \"unimprovedScoreDifferenceThreshold\", \"bestScoreLimit\", \"bestScoreFeasible\", \"stepCountLimit\", \"unimprovedStepCountLimit\", \"scoreCalculationCountLimit\", \"moveCountLimit\", \"stopFlatLineDetectionRatio\", \"noStopFlatLineDetectionRatio\", \"delayFlatLineSecondsSpentLimit\", \"terminationConfigList\"}",
"justification": "Add new termination config"
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"moveCountLimit",
"stopFlatLineDetectionRatio",
"noStopFlatLineDetectionRatio",
"minimalExecutionTimeSeconds",
"delayFlatLineSecondsSpentLimit",
"terminationConfigList"
})
public class TerminationConfig extends AbstractConfig<TerminationConfig> {
Expand Down Expand Up @@ -83,7 +83,7 @@ public class TerminationConfig extends AbstractConfig<TerminationConfig> {

private Double stopFlatLineDetectionRatio = null;
private Double noStopFlatLineDetectionRatio = null;
private Long minimalExecutionTimeSeconds = null;
private Long delayFlatLineSecondsSpentLimit = null;

@XmlElement(name = "termination")
private List<TerminationConfig> terminationConfigList = null;
Expand Down Expand Up @@ -280,12 +280,12 @@ public void setNoStopFlatLineDetectionRatio(@Nullable Double noStopFlatLineDetec
this.noStopFlatLineDetectionRatio = noStopFlatLineDetectionRatio;
}

public @Nullable Long getMinimalExecutionTimeSeconds() {
return minimalExecutionTimeSeconds;
public @Nullable Long getDelayFlatLineSecondsSpentLimit() {
return delayFlatLineSecondsSpentLimit;
}

public void setMinimalExecutionTimeSeconds(@Nullable Long minimalExecutionTimeSeconds) {
this.minimalExecutionTimeSeconds = minimalExecutionTimeSeconds;
public void setDelayFlatLineSecondsSpentLimit(@Nullable Long delayFlatLineSecondsSpentLimit) {
this.delayFlatLineSecondsSpentLimit = delayFlatLineSecondsSpentLimit;
}

public @Nullable List<@NonNull TerminationConfig> getTerminationConfigList() {
Expand Down Expand Up @@ -421,8 +421,8 @@ public TerminationConfig withTerminationClass(Class<? extends Termination> termi
return this;
}

public @NonNull TerminationConfig withMinimalExecutionTimeSeconds(@NonNull Long minimalExecutionTimeSeconds) {
this.minimalExecutionTimeSeconds = minimalExecutionTimeSeconds;
public @NonNull TerminationConfig withDelayFlatLineSecondsSpentLimit(@NonNull Long delayFlatLineSecondsSpentLimit) {
this.delayFlatLineSecondsSpentLimit = delayFlatLineSecondsSpentLimit;
return this;
}

Expand Down Expand Up @@ -537,7 +537,7 @@ public boolean isConfigured() {
moveCountLimit != null ||
stopFlatLineDetectionRatio != null ||
noStopFlatLineDetectionRatio != null ||
minimalExecutionTimeSeconds != null ||
delayFlatLineSecondsSpentLimit != null ||
isTerminationListConfigured();
}

Expand Down Expand Up @@ -582,8 +582,8 @@ private boolean isTerminationListConfigured() {
inheritedConfig.getStopFlatLineDetectionRatio());
noStopFlatLineDetectionRatio = ConfigUtils.inheritOverwritableProperty(noStopFlatLineDetectionRatio,
inheritedConfig.getNoStopFlatLineDetectionRatio());
minimalExecutionTimeSeconds = ConfigUtils.inheritOverwritableProperty(minimalExecutionTimeSeconds,
inheritedConfig.getMinimalExecutionTimeSeconds());
delayFlatLineSecondsSpentLimit = ConfigUtils.inheritOverwritableProperty(delayFlatLineSecondsSpentLimit,
inheritedConfig.getDelayFlatLineSecondsSpentLimit());
terminationConfigList = ConfigUtils.inheritMergeableListConfig(
terminationConfigList, inheritedConfig.getTerminationConfigList());
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,10 @@ The termination with bestScoreFeasible (%s) can only be used with a score type \
}
if (terminationConfig.getStopFlatLineDetectionRatio() != null
|| terminationConfig.getNoStopFlatLineDetectionRatio() != null
|| terminationConfig.getMinimalExecutionTimeSeconds() != null) {
|| terminationConfig.getDelayFlatLineSecondsSpentLimit() != null) {
terminationList.add(new UnimprovedBestSolutionTermination<>(terminationConfig.getStopFlatLineDetectionRatio(),
terminationConfig.getNoStopFlatLineDetectionRatio(), terminationConfig.getMinimalExecutionTimeSeconds()));
terminationConfig.getNoStopFlatLineDetectionRatio(),
terminationConfig.getDelayFlatLineSecondsSpentLimit()));
}
terminationList.addAll(buildInnerTermination(configPolicy));
return buildTerminationFromList(terminationList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

public final class UnimprovedBestSolutionTermination<Solution_> extends AbstractTermination<Solution_> {

// Minimal interval of time to avoid early conclusions
private final long minimalExecutionTimeMillis;
// Evaluation delay to avoid early conclusions
private final long delayExecutionTimeMillis;
// This setting determines the amount of time
// that is allowed without any improvements since the last best solution was identified.
// For example, if the last solution was found at 10 seconds and the setting is configured to 0.5,
Expand All @@ -38,19 +38,20 @@ public final class UnimprovedBestSolutionTermination<Solution_> extends Abstract
protected Boolean terminate;

public UnimprovedBestSolutionTermination(Double stopFlatLineDetectionRatio,
Double noStopFlatLineDetectionRatio, Long minimalExecutionTimeSeconds) {
this(stopFlatLineDetectionRatio, noStopFlatLineDetectionRatio, minimalExecutionTimeSeconds, Clock.systemUTC());
Double noStopFlatLineDetectionRatio, Long delayFlatLineSecondsSpentLimit) {
this(stopFlatLineDetectionRatio, noStopFlatLineDetectionRatio, delayFlatLineSecondsSpentLimit, Clock.systemUTC());
}

public UnimprovedBestSolutionTermination(Double stopFlatLineDetectionRatio, Double noStopFlatLineDetectionRatio,
Long minimalExecutionTimeSeconds, Clock clock) {
Long delayFlatLineSecondsSpentLimit, Clock clock) {
this.stopFlatLineDetectionRatio = Objects.requireNonNull(stopFlatLineDetectionRatio,
"The field stopFlatLineDetectionRatio is required for the termination UnimprovedBestSolutionTermination");
this.noStopFlatLineDetectionRatio = Objects.requireNonNull(noStopFlatLineDetectionRatio,
"The field noStopFlatLineDetectionRatio is required for the termination UnimprovedBestSolutionTermination");
this.minimalExecutionTimeMillis = Objects.requireNonNull(minimalExecutionTimeSeconds,
"The field minimalExecutionTimeSeconds is required for the termination UnimprovedBestSolutionTermination")
* 1000L;
this.delayExecutionTimeMillis =
(Objects.requireNonNull(delayFlatLineSecondsSpentLimit,
"The field delayFlatLineSecondsSpentLimit is required for the termination UnimprovedBestSolutionTermination")
* 1000L);
this.clock = Objects.requireNonNull(clock);
if (stopFlatLineDetectionRatio < 0) {
throw new IllegalArgumentException(
Expand All @@ -65,14 +66,14 @@ public UnimprovedBestSolutionTermination(Double stopFlatLineDetectionRatio, Doub
"The noStopFlatLineDetectionRatio (%.2f) cannot be greater than stopFlatLineDetectionRatio (%.2f)."
.formatted(noStopFlatLineDetectionRatio, stopFlatLineDetectionRatio));
}
if (minimalExecutionTimeSeconds <= 0) {
if (delayFlatLineSecondsSpentLimit < 0) {
throw new IllegalArgumentException(
"The minimalExecutionTimeSeconds %d must be great than zero.".formatted(minimalExecutionTimeSeconds));
"The delayFlatLineSecondsSpentLimit (%d) cannot be negative.".formatted(delayFlatLineSecondsSpentLimit));
}
}

public long getMinimalExecutionTimeMillis() {
return minimalExecutionTimeMillis;
public long getDelayExecutionTimeMillis() {
return delayExecutionTimeMillis;
}

public double getStopFlatLineDetectionRatio() {
Expand Down Expand Up @@ -129,7 +130,7 @@ public boolean isPhaseTerminated(AbstractPhaseScope<Solution_> phaseScope) {
if (terminate != null) {
return terminate;
}
// Validate if there is a first best solution and the poll time
// Validate if there is a first best solution
if (waitForFirstBestScore) {
return false;
}
Expand All @@ -144,7 +145,7 @@ public boolean isPhaseTerminated(AbstractPhaseScope<Solution_> phaseScope) {
// as it would be the starting point for the new curve.
var minInterval = Math.floor(lastImprovementInterval * noStopFlatLineDetectionRatio);
var maxInterval = Math.floor(lastImprovementInterval * stopFlatLineDetectionRatio);
if (lastImprovementMillis > 0 && completeInterval >= minimalExecutionTimeMillis && newInterval > minInterval
if (lastImprovementMillis > 0 && completeInterval >= delayExecutionTimeMillis && newInterval >= minInterval
&& newInterval < maxInterval) {
initialCurvePointMillis = lastImprovementMillis;
previousBest = currentBest;
Expand All @@ -159,7 +160,7 @@ public boolean isPhaseTerminated(AbstractPhaseScope<Solution_> phaseScope) {
terminate = null;
return false;
} else {
if (completeInterval < minimalExecutionTimeMillis) {
if (completeInterval < delayExecutionTimeMillis) {
return false;
}
var maxInterval = Math.floor(lastImprovementInterval * stopFlatLineDetectionRatio);
Expand Down Expand Up @@ -198,12 +199,12 @@ public double calculatePhaseTimeGradient(AbstractPhaseScope<Solution_> phaseScop
public UnimprovedBestSolutionTermination<Solution_> createChildThreadTermination(SolverScope<Solution_> solverScope,
ChildThreadType childThreadType) {
return new UnimprovedBestSolutionTermination<>(stopFlatLineDetectionRatio, noStopFlatLineDetectionRatio,
minimalExecutionTimeMillis, clock);
delayExecutionTimeMillis / 1000, clock);
}

@Override
public String toString() {
return "UnimprovedBestSolutionTermination(%.2f, %.2f)".formatted(stopFlatLineDetectionRatio,
noStopFlatLineDetectionRatio);
return "UnimprovedBestSolutionTermination(%.2f, %.2f, %d)".formatted(stopFlatLineDetectionRatio,
noStopFlatLineDetectionRatio, delayExecutionTimeMillis / 1000);
}
}
2 changes: 1 addition & 1 deletion core/src/main/resources/solver.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@

<xs:element minOccurs="0" name="noStopFlatLineDetectionRatio" type="xs:double"/>

<xs:element minOccurs="0" name="minimalExecutionTimeSeconds" type="xs:long"/>
<xs:element minOccurs="0" name="delayFlatLineSecondsSpentLimit" type="xs:long"/>

<xs:element maxOccurs="unbounded" minOccurs="0" name="termination" type="tns:terminationConfig"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ void childWithUnimprovedPropertiesFromParent() {
TerminationConfig parent = new TerminationConfig()
.withStopFlatLineDetectionRatio(0.5)
.withNoStopFlatLineDetectionRatio(0.1)
.withMinimalExecutionTimeSeconds(10L);
.withDelayFlatLineSecondsSpentLimit(10L);
child.inherit(parent);
assertThat(child.getStopFlatLineDetectionRatio()).isEqualTo(0.5);
assertThat(child.getNoStopFlatLineDetectionRatio()).isEqualTo(0.1);
assertThat(child.getMinimalExecutionTimeSeconds()).isEqualTo(10L);
assertThat(child.getDelayFlatLineSecondsSpentLimit()).isEqualTo(10L);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ void buildUnimprovedBestScoreRatio() {
var terminationConfig = new TerminationConfig();
terminationConfig.setStopFlatLineDetectionRatio(0.5);
terminationConfig.setNoStopFlatLineDetectionRatio(0.1);
terminationConfig.setMinimalExecutionTimeSeconds(10L);
terminationConfig.setDelayFlatLineSecondsSpentLimit(10L);
var termination = TerminationFactory.create(terminationConfig)
.buildTermination(mock(HeuristicConfigPolicy.class));
assertThat(termination)
Expand All @@ -249,7 +249,7 @@ void buildUnimprovedBestScoreRatio() {
.isEqualTo(0.5);
assertThat(((UnimprovedBestSolutionTermination<?>) termination).getNoStopFlatLineDetectionRatio())
.isEqualTo(0.1);
assertThat(((UnimprovedBestSolutionTermination<?>) termination).getMinimalExecutionTimeMillis())
assertThat(((UnimprovedBestSolutionTermination<?>) termination).getDelayExecutionTimeMillis())
.isEqualTo(10000L);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ void testTermination() {
}

@Test
void testStartNewCurve() {
void testStartNewPoint() {
Clock clock = Mockito.mock(Clock.class);
var currentTime = Clock.systemUTC().millis();
var termination = new UnimprovedBestSolutionTermination<TestdataSolution>(0.5, 0.3, 10L, clock);
var termination = new UnimprovedBestSolutionTermination<TestdataSolution>(0.5, 0.4, 10L, clock);
var solverScope = Mockito.mock(SolverScope.class);
var phaseScope = Mockito.mock(LocalSearchPhaseScope.class);
when(phaseScope.getSolverScope()).thenReturn(solverScope);
Expand All @@ -60,15 +60,15 @@ void testStartNewCurve() {
termination.phaseStarted(phaseScope);
termination.waitForFirstBestScore = false;

// Adding a new curve
// New start point
termination.currentBest = SimpleScore.of(1);
termination.initialCurvePointMillis = currentTime;
termination.lastImprovementMillis = currentTime + 10_000;
when(clock.millis()).thenReturn(currentTime + 14_000);
assertThat(termination.isPhaseTerminated(phaseScope)).isFalse();
assertThat(termination.initialCurvePointMillis).isEqualTo(currentTime + 10_000);

// Not adding a new curve - flat line smaller than the minimum
// Don't change start point - flat line smaller than the minimum
termination.terminate = null;
termination.currentBest = SimpleScore.of(1);
termination.initialCurvePointMillis = currentTime;
Expand All @@ -77,7 +77,7 @@ void testStartNewCurve() {
assertThat(termination.isPhaseTerminated(phaseScope)).isFalse();
assertThat(termination.initialCurvePointMillis).isEqualTo(currentTime);

// Not adding a new curve - flat line larger than the minimum
// Don't change start point - flat line larger than the minimum
termination.terminate = null;
termination.currentBest = SimpleScore.of(1);
termination.initialCurvePointMillis = currentTime;
Expand All @@ -88,7 +88,7 @@ void testStartNewCurve() {
}

@Test
void testMinimalInterval() {
void testDelayInterval() {
Clock clock = Mockito.mock(Clock.class);
var currentTime = Clock.systemUTC().millis();
var termination = new UnimprovedBestSolutionTermination<TestdataSolution>(0.5, 0.4, 10L, clock);
Expand Down Expand Up @@ -119,15 +119,12 @@ void testMinimalInterval() {
@Test
void invalidTermination() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(-1.0, 0.0, 1L));
assertThatIllegalArgumentException()
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(0.0, -1.0, 1L));
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(-1.0, 0.0, 0L));
assertThatIllegalArgumentException()
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(0.0, 1.0, 1L));
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(0.0, -1.0, 0L));
assertThatIllegalArgumentException()
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(1.0, 1.0, 0L));
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(0.0, 1.0, 0L));
assertThatIllegalArgumentException()
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(0.1, 1.0, 1L)
.calculateSolverTimeGradient(null));
.isThrownBy(() -> new UnimprovedBestSolutionTermination<TestdataSolution>(1.0, 1.0, -1L));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ the solver improved the solution and how much time has passed without any improv
<termination>
<stopFlatLineDetectionRatio>0.5</stopFlatLineDetectionRatio>
<noStopFlatLineDetectionRatio>0.1</noStopFlatLineDetectionRatio>
<minimalExecutionTimeSeconds>120</minimalExecutionTimeSeconds>
<delayFlatLineSecondsSpentLimit>120</delayFlatLineSecondsSpentLimit>
</termination>
----

Expand All @@ -596,7 +596,7 @@ setting the `stopFlatLineDetectionRatio` to `1.0` will lead to termination after

[NOTE]
====
The `Termination` process requires a minimum execution time, specified by `minimalExecutionTimeSeconds`.
The `Termination` process requires a minimum execution time, specified by `delayFlatLineSecondsSpentLimit`.
====

The solving process may identify no improvement periods that are not significant enough to trigger the termination.
Expand Down

0 comments on commit e8fb143

Please sign in to comment.