Skip to content

Commit

Permalink
feat: add incremental approach
Browse files Browse the repository at this point in the history
  • Loading branch information
zepfred committed Sep 30, 2024
1 parent 77bc341 commit c0e5e54
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 48 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 @@ -2447,7 +2447,7 @@
<xs:element minOccurs="0" name="moveCountLimitPercentage" type="xs:double"/>


<xs:element minOccurs="0" name="reconfigurationRatio" type="xs:double"/>
<xs:element minOccurs="0" name="lateAcceptanceReconfigurationSize" type="xs:long"/>


</xs:sequence>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

@XmlType(propOrder = {
"moveCountLimitPercentage",
"reconfigurationRatio"
"lateAcceptanceReconfigurationSize"
})
public class ReconfigurationConfig extends AbstractConfig<ReconfigurationConfig> {

private Double moveCountLimitPercentage = null;
private Double reconfigurationRatio = null;
private Long lateAcceptanceReconfigurationSize = null;

public Double getMoveCountLimitPercentage() {
return moveCountLimitPercentage;
Expand All @@ -23,12 +23,12 @@ public void setMoveCountLimitPercentage(Double moveCountLimitPercentage) {
this.moveCountLimitPercentage = moveCountLimitPercentage;
}

public Double getReconfigurationRatio() {
return reconfigurationRatio;
public Long getLateAcceptanceReconfigurationSize() {
return lateAcceptanceReconfigurationSize;
}

public void setReconfigurationRatio(Double reconfigurationRatio) {
this.reconfigurationRatio = reconfigurationRatio;
public void setLateAcceptanceReconfigurationSize(Long lateAcceptanceReconfigurationSize) {
this.lateAcceptanceReconfigurationSize = lateAcceptanceReconfigurationSize;
}

// ************************************************************************
Expand All @@ -40,15 +40,15 @@ public ReconfigurationConfig withMoveCountLimitPercentage(Double moveCountLimitP
return this;
}

public ReconfigurationConfig withReconfigurationRatio(Double reconfigurationRatio) {
this.reconfigurationRatio = reconfigurationRatio;
public ReconfigurationConfig withReconfigurationRatio(Long reconfigurationRatio) {
this.lateAcceptanceReconfigurationSize = reconfigurationRatio;
return this;
}

@Override
public ReconfigurationConfig inherit(ReconfigurationConfig inheritedConfig) {
moveCountLimitPercentage = inheritedConfig.getMoveCountLimitPercentage();
reconfigurationRatio = inheritedConfig.getReconfigurationRatio();
lateAcceptanceReconfigurationSize = inheritedConfig.getLateAcceptanceReconfigurationSize();
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,9 @@ private Optional<LateAcceptanceAcceptor<Solution_>> buildLateAcceptanceAcceptor(
: 100.0;
acceptor.setMoveCountLimitPercentage(moveCountLimitPercentage);
var reconfigurationRatio = acceptorConfig.getReconfigurationConfig() != null
? acceptorConfig.getReconfigurationConfig().getReconfigurationRatio()
: 10.0;
acceptor.setMoveReconfigurationRatio(reconfigurationRatio);
? acceptorConfig.getReconfigurationConfig().getLateAcceptanceReconfigurationSize()
: 1;
acceptor.setLateAcceptanceReconfigurationSize(reconfigurationRatio);
return Optional.of(acceptor);
}
return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ public class LateAcceptanceAcceptor<Solution_> extends AbstractAcceptor<Solution
protected int lateScoreIndex = -1;

protected Double moveCountLimitPercentage;
protected Double moveReconfigurationRatio;
protected long lateAcceptanceReconfigurationRatioCount;
// max number of inferior solutions that can be accepted
protected long lateAcceptanceReconfigurationSize;
// current number of accepted inferior solutions
protected long currentReconfigurationRationCount = 1;
// max number of moves evaluated before triggering the reconfiguration
protected long maxReconfigurationMoveCount;
protected Score<?> lastAcceptedScore = null;
// move termination that triggers the reconfiguration when it is terminated
protected MoveCountTermination<Solution_> moveCountTermination;

public void setLateAcceptanceSize(int lateAcceptanceSize) {
Expand All @@ -34,8 +40,8 @@ public void setMoveCountLimitPercentage(Double moveCountLimitPercentage) {
this.moveCountLimitPercentage = moveCountLimitPercentage;
}

public void setMoveReconfigurationRatio(Double moveReconfigurationRatio) {
this.moveReconfigurationRatio = moveReconfigurationRatio;
public void setLateAcceptanceReconfigurationSize(long lateAcceptanceReconfigurationSize) {
this.lateAcceptanceReconfigurationSize = lateAcceptanceReconfigurationSize;
}

// ************************************************************************
Expand All @@ -50,12 +56,12 @@ public void phaseStarted(LocalSearchPhaseScope<Solution_> phaseScope) {
var initialScore = phaseScope.getBestScore();
Arrays.fill(previousScores, initialScore);
lateScoreIndex = 0;
lateAcceptanceReconfigurationRatioCount = (long) (lateAcceptanceSize * moveReconfigurationRatio / 100);
var lateAcceptanceReconfigurationMoveCount = (long) (phaseScope.getMoveSelectorSize() * moveCountLimitPercentage / 100);
moveCountTermination = new MoveCountTermination<>(lateAcceptanceReconfigurationMoveCount, true);
currentReconfigurationRationCount = 1;
maxReconfigurationMoveCount = (long) (phaseScope.getMoveSelectorSize() * moveCountLimitPercentage / 100);
moveCountTermination = new MoveCountTermination<>(maxReconfigurationMoveCount, true);
moveCountTermination.phaseStarted(phaseScope);
logger.info("Late Acceptance reconfiguration move count({}), late elements count({}) ",
lateAcceptanceReconfigurationMoveCount, lateAcceptanceReconfigurationRatioCount);
logger.info("Late Acceptance reconfiguration move count({}), max inferior elements count({}) ",
maxReconfigurationMoveCount, lateAcceptanceReconfigurationSize);
}

private void validate() {
Expand Down Expand Up @@ -85,20 +91,26 @@ public boolean isAccepted(LocalSearchMoveScope<Solution_> moveScope) {
return false;
}

@SuppressWarnings({"rawtypes", "unchecked"})
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void stepEnded(LocalSearchStepScope<Solution_> stepScope) {
super.stepEnded(stepScope);
Score stepScore = stepScope.getScore();
var lateScoreCmp = stepScore.compareTo(previousScores[lateScoreIndex]);
var lastStepScore = stepScope.getPhaseScope().getLastCompletedStepScope().getScore();
var lastStepScoreCmp = stepScore.compareTo(lastStepScore);
var lateScore = previousScores[lateScoreIndex];
previousScores[lateScoreIndex] = stepScope.getScore();
lateScoreIndex = (lateScoreIndex + 1) % lateAcceptanceSize;
if (lateScoreCmp > 0 || lastStepScoreCmp > 0) {
// The terminator is only updated when a superior solution is found.
if (maxReconfigurationMoveCount > 0) {
// The termination is only updated when a superior solution is found.
// Otherwise, we continue incrementing the moves until the reconfiguration is triggered
moveCountTermination.stepEnded(stepScope);
var lateScoreCmp = lateScore != null && stepScore.compareTo(lateScore) > 0;
var lastStepScoreCmp = lastAcceptedScore != null && stepScore.compareTo(lastAcceptedScore) > 0;
if (lateScore == null || lateScoreCmp || lastStepScoreCmp) {
moveCountTermination.stepEnded(stepScope);
if (lastStepScoreCmp) {
// Reset the current number of accepted inferior solutions
currentReconfigurationRationCount = 1;
}
}
}
}

Expand All @@ -108,24 +120,36 @@ public void phaseEnded(LocalSearchPhaseScope<Solution_> phaseScope) {
previousScores = null;
moveCountTermination = null;
lateScoreIndex = -1;
currentReconfigurationRationCount = 1;
lastAcceptedScore = null;
}

@Override
public boolean needReconfiguration(LocalSearchStepScope<Solution_> stepScope) {
return moveCountTermination.isSolverTerminated(stepScope.getPhaseScope().getSolverScope());
return maxReconfigurationMoveCount > 0
&& currentReconfigurationRationCount <= lateAcceptanceReconfigurationSize
&& moveCountTermination.isSolverTerminated(stepScope.getPhaseScope().getSolverScope());
}

@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void applyReconfiguration(LocalSearchStepScope<Solution_> stepScope) {
var idx = lateScoreIndex;
if (previousScores[idx] == null) {
// The method still has null values from the last reconfiguration,
// and we don't need to apply any reconfiguration
return;
}
for (var i = 0; i < lateAcceptanceReconfigurationRatioCount; i++) {
previousScores[idx] = null;
for (var i = 0; i < currentReconfigurationRationCount; i++) {
// We first increment idx to ensure stepEnded logic won't rewrite it,
// so it can be used in the next iteration
idx = (idx + 1) % lateAcceptanceSize;
previousScores[idx] = null;
}
Score currentBestScore = stepScope.getPhaseScope().getSolverScope().getBestScore();
if (lastAcceptedScore == null || currentBestScore.compareTo(lastAcceptedScore) > 0) {
lastAcceptedScore = currentBestScore;
}
logger.info("Reconfiguration applied to inferior elements count ({}), best current score ({}).",
currentReconfigurationRationCount, lastAcceptedScore);
currentReconfigurationRationCount++;
// Ensure that after accepting an inferior solution,
// the LS evaluates maxReconfigurationMoveCount neighbors again
moveCountTermination.stepEnded(stepScope);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public void phaseStarted(AbstractPhaseScope<Solution_> phaseScope) {
}

@Override
public void stepStarted(AbstractStepScope<Solution_> stepScope) {
super.stepStarted(stepScope);
public void stepEnded(AbstractStepScope<Solution_> stepScope) {
super.stepEnded(stepScope);
if (updateMoveCountPerStep) {
lastMoveCount = stepScope.getPhaseScope().getSolverScope().getMoveEvaluationCount();
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/resources/solver.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -1437,7 +1437,7 @@

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

<xs:element minOccurs="0" name="reconfigurationRatio" type="xs:double"/>
<xs:element minOccurs="0" name="lateAcceptanceReconfigurationSize" type="xs:long"/>

</xs:sequence>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void lateAcceptanceSize() {
acceptor.setLateAcceptanceSize(3);
acceptor.setHillClimbingEnabled(false);
acceptor.setMoveCountLimitPercentage(1.0);
acceptor.setMoveReconfigurationRatio(1.0);
acceptor.setLateAcceptanceReconfigurationSize(1L);

var solverScope = new SolverScope<>();
solverScope.setBestScore(SimpleScore.of(-1000));
Expand Down Expand Up @@ -134,7 +134,7 @@ void hillClimbingEnabled() {
acceptor.setLateAcceptanceSize(2);
acceptor.setHillClimbingEnabled(true);
acceptor.setMoveCountLimitPercentage(1.0);
acceptor.setMoveReconfigurationRatio(1.0);
acceptor.setLateAcceptanceReconfigurationSize(1L);

var solverScope = new SolverScope<>();
solverScope.setBestScore(SimpleScore.of(-1000));
Expand Down Expand Up @@ -260,10 +260,10 @@ void negativeLateAcceptanceSize() {
@Test
void applyReconfiguration() {
var acceptor = new LateAcceptanceAcceptor<>();
acceptor.setLateAcceptanceSize(2);
acceptor.setLateAcceptanceSize(3);
acceptor.setHillClimbingEnabled(true);
acceptor.setMoveCountLimitPercentage(20.0);
acceptor.setMoveReconfigurationRatio(20.0);
acceptor.setLateAcceptanceReconfigurationSize(20L);

var solverScope = new SolverScope<>();
solverScope.setBestScore(SimpleScore.of(-1000));
Expand All @@ -280,24 +280,63 @@ void applyReconfiguration() {
assertThat(acceptor.isAccepted(moveScope0)).isFalse();
assertThat(acceptor.needReconfiguration(stepScope0)).isFalse();

// Reconfiguration
// Test reconfiguration
solverScope.addMoveEvaluationCount(1);
assertThat(acceptor.needReconfiguration(stepScope0)).isFalse();
solverScope.addMoveEvaluationCount(1);
assertThat(acceptor.needReconfiguration(stepScope0)).isTrue();
assertThat(acceptor.isAccepted(moveScope0)).isFalse();

// Apply reconfiguration
// One iteration
acceptor.phaseStarted(phaseScope);
stepScope0.setScore(SimpleScore.of(-3000));
var moveScope1 = buildMoveScope(stepScope0, -3000);
acceptor.applyReconfiguration(stepScope0);
assertThat(acceptor.isAccepted(moveScope1)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope1)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope1)).isFalse();
acceptor.stepEnded(stepScope0);

// Two iterations
acceptor.phaseStarted(phaseScope);
var moveScope2 = buildMoveScope(stepScope0, -4000);
acceptor.stepStarted(stepScope0);
assertThat(acceptor.needReconfiguration(stepScope0)).isFalse();
acceptor.applyReconfiguration(stepScope0);
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope2)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope2)).isFalse();
acceptor.stepEnded(stepScope0);
acceptor.applyReconfiguration(stepScope0);
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope2)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope2)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope2)).isFalse();
acceptor.stepEnded(stepScope0);

// Reset
acceptor.phaseStarted(phaseScope);
var moveScope3 = buildMoveScope(stepScope0, -4000);
acceptor.applyReconfiguration(stepScope0);
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope3)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope3)).isFalse();
acceptor.stepEnded(stepScope0);
// This step will reset currentReconfigurationRationCount
var moveScope4 = buildMoveScope(stepScope0, -900);
stepScope0.setScore(SimpleScore.of(-900));
assertThat(acceptor.isAccepted(moveScope4)).isTrue();
acceptor.stepEnded(stepScope0);
// Rerun the reconfiguration
acceptor.applyReconfiguration(stepScope0);
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope3)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope3)).isFalse();
acceptor.stepEnded(stepScope0);
}
}

0 comments on commit c0e5e54

Please sign in to comment.