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 a9d2eae
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ public class LateAcceptanceAcceptor<Solution_> extends AbstractAcceptor<Solution

protected Double moveCountLimitPercentage;
protected Double moveReconfigurationRatio;
protected long lateAcceptanceReconfigurationRatioCount;
// current number of accepted inferior solutions
protected long currentReconfigurationRationCount = 1;
// max number of inferior solutions that can be accepted
protected long maxReconfigurationRatioCount;
// 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 Down Expand Up @@ -50,12 +57,13 @@ 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;
maxReconfigurationRatioCount = (long) (lateAcceptanceSize * moveReconfigurationRatio / 100);
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, maxReconfigurationRatioCount);
}

private void validate() {
Expand Down Expand Up @@ -85,20 +93,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 +122,35 @@ 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 <= maxReconfigurationRatioCount
&& 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 to ensure stepEnded won't replace it, and it will 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
Original file line number Diff line number Diff line change
Expand Up @@ -280,24 +280,59 @@ 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();
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.applyReconfiguration(stepScope0);
assertThat(acceptor.isAccepted(moveScope2)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope2)).isFalse();
acceptor.stepEnded(stepScope0);
acceptor.applyReconfiguration(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);
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);
assertThat(acceptor.isAccepted(moveScope3)).isTrue();
acceptor.stepEnded(stepScope0);
assertThat(acceptor.isAccepted(moveScope3)).isFalse();
acceptor.stepEnded(stepScope0);
}
}

0 comments on commit a9d2eae

Please sign in to comment.