Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ enum OptimizationStrategyType {
case HILL_CLIMBING | GLOBAL_HILL_CLIMBING =>
locallySearchable(params)
case TABU_SEARCH =>
!optimizee.evaluateByComparison
!optimizee.evaluateByComparison && !hasContinuousParameter(params)
case MIN_CONFLICTS =>
!optimizee.evaluateByComparison && params.supportsLocalMoves
case _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.barrybecker4.optimization.strategy.tabu

import com.barrybecker4.math.MathUtil
import com.barrybecker4.optimization.optimizee.{EvaluationBudgetExceededException, Optimizee}
import com.barrybecker4.optimization.parameter.{ParameterArray, ParameterArrayWithFitness}
import com.barrybecker4.optimization.parameter.{NumericParameterArray, ParameterArray, ParameterArrayWithFitness}
import com.barrybecker4.optimization.strategy.OptimizationStrategy
import com.barrybecker4.optimization.strategy.statespace.StateSpaceSearchStrategy

Expand All @@ -22,7 +22,7 @@ import scala.util.Random
* scores random neighbors instead and forbids returning to a solution it has just left; that works but evaluates a
* whole solution per candidate, so it is slow.
*
* Works with absolute fitness only. When the optimizee is wrapped with an evaluation budget the best solution found
* Works with absolute fitness and discrete parameters only. When the optimizee is wrapped with an evaluation budget the best solution found
* so far is returned when the budget is used up.
*
* @param optimizee the thing to be optimized.
Expand All @@ -41,6 +41,7 @@ class TabuSearchStrategy(optimizee: Optimizee, rnd: Random = MathUtil.RANDOM) ex

override def doOptimization(params: ParameterArray, fitnessRange: Double): ParameterArrayWithFitness = {
require(!optimizee.evaluateByComparison, "Tabu search needs absolute fitness, not comparison")
require(!hasContinuousParameter(params), "Tabu search needs discrete parameters, but this array has a continuous one")
var best = ParameterArrayWithFitness(params, Double.PositiveInfinity)
try best = search(params)
catch { case _: EvaluationBudgetExceededException => best = bestSoFar.getOrElse(best) }
Expand All @@ -64,6 +65,11 @@ class TabuSearchStrategy(optimizee: Optimizee, rnd: Random = MathUtil.RANDOM) ex
best
}

private def hasContinuousParameter(params: ParameterArray): Boolean = params match {
case numeric: NumericParameterArray => !numeric.supportsLocalMoves
case _ => false
}

private def shouldRestart(stagnantIterations: Int): Boolean =
config.restartAfterStagnantIterations > 0 && stagnantIterations >= config.restartAfterStagnantIterations

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ class StrategyScoreboardSuite extends AnyFunSuite with TimeLimitedTests {
case (VARIABLE_NEIGHBORHOOD_SEARCH, "ackley") => Reach(3.0)
case (VARIABLE_NEIGHBORHOOD_SEARCH, "rastrigin") => Reach(2.0)

case (TABU_SEARCH, _) if continuous => Refuse("discrete")
case (MIN_CONFLICTS, _) if continuous => Refuse("local moves")
case (TABU_SEARCH | MIN_CONFLICTS, _) => Reach(Exact, Some(500))

case _ =>
throw new IllegalStateException(s"No scoreboard bar for $strategy on $problem")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ class OptimizationStrategyTypeSuite extends AnyFunSuite {
"DIFFERENTIAL_EVOLUTION",
"PARTICLE_SWARM",
"ITERATED_LOCAL_SEARCH",
"VARIABLE_NEIGHBORHOOD_SEARCH",
"TABU_SEARCH"
"VARIABLE_NEIGHBORHOOD_SEARCH"
))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ class TabuSearchStrategySuite extends AnyFunSuite {
intercept[IllegalArgumentException](strategyFor(problem, TabuSearchConfig()).doOptimization(problem.getInitialGuess, 1.0))
}

test("an array with a real number is refused") {
val problem = new com.barrybecker4.optimization.optimizee.optimizees.SphereProblem(2)
intercept[IllegalArgumentException](strategyFor(problem, TabuSearchConfig()).doOptimization(problem.getInitialGuess, 1.0))
}

test("the configuration refuses values that make no sense") {
intercept[IllegalArgumentException](TabuSearchConfig(candidateMoves = 0))
intercept[IllegalArgumentException](TabuSearchConfig(tenureMin = 5, tenureMax = 4))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class OptimizerEvalStatusSuite extends AnyFunSuite {
"DIFFERENTIAL_EVOLUTION",
"PARTICLE_SWARM",
"ITERATED_LOCAL_SEARCH",
"VARIABLE_NEIGHBORHOOD_SEARCH"
"VARIABLE_NEIGHBORHOOD_SEARCH",
"TABU_SEARCH",
"MIN_CONFLICTS"
)
}
Loading