Heuristics
Heuristics in JuLS control how the optimization process explores the solution space. They are divided into three main categories: initialization, neighbourhood exploration, and move selection.
Types of Heuristics
1. Initialization Heuristics
These determine the starting solution for optimization.
SimpleInitialization
Random initialization within variable domains:
init = SimpleInitialization()
GreedyInitialization
Problem-specific greedy construction:
init = GreedyInitialization()
ChristofidesInitialization
Specialized for TSP problems using the Christofides algorithm:
init = ChristofidesInitialization()
2. Neighbourhood Heuristics
These define how to explore the solution space by generating moves.
RandomNeighbourhood
Randomly selects variables and generates moves:
neigh = RandomNeighbourhood(n_vars, n_to_move) # n_vars total, n_to_move selected
BinaryRandomNeighbourhood
Specialized for binary problems:
neigh = BinaryRandomNeighbourhood(n_moves, n_vars) # n_moves generated, n_vars flipped each
SwapNeighbourhood
Generates swap moves between variables:
neigh = SwapNeighbourhood() # No parameters needed
KOptNeighbourhood
k-opt moves for permutation problems:
neigh = KOptNeighbourhood(n_moves, k) # n_moves generated, k variables each
3. Move Selection Heuristics
These decide which moves to accept during optimization.
GreedyMoveSelection
Always accepts the best improving move:
pick = GreedyMoveSelection()
SimulatedAnnealing
Temperature-based acceptance with cooling:
pick = SimulatedAnnealing(T0=100.0, α=0.95)
Metropolis
Metropolis criterion with fixed temperature:
pick = Metropolis(T=10.0)
Custom Heuristics
You can create custom heuristics by implementing the appropriate interfaces.
Custom Initialization
struct MyInitialization <: InitializationHeuristic
# Custom parameters
end
function (init::MyInitialization)(experiment::MyExperiment)
# Return initial solution vector
return initial_solution
end
Custom Neighbourhood
struct MyNeighbourhood <: NeighbourhoodHeuristic
# Custom parameters
end
function get_neighbourhood(neigh::MyNeighbourhood, model::Model)
# Return vector of possible moves
return moves
end
Custom Move Selection
struct MyMoveSelection <: MoveSelectionHeuristic
# Custom parameters
end
function pick_a_move(picker::MyMoveSelection, evaluated_moves::Vector{<:MoveEvaluatorOutput})
# Return selected move or nothing
return selected_move
end
Heuristic Combinations
Different heuristic combinations work better for different problem types:
For Binary Problems
model = init_model(
experiment;
init = GreedyInitialization(),
neigh = BinaryRandomNeighbourhood(10, 2),
pick = GreedyMoveSelection()
)
For Permutation Problems
model = init_model(
experiment;
init = ChristofidesInitialization(), # TSP-specific
neigh = KOptNeighbourhood(5, 2), # 5 moves, 2-opt
pick = SimulatedAnnealing(100.0, 0.95) # T0=100.0, α=0.95
)
For General Integer Problems
model = init_model(
experiment;
init = SimpleInitialization(),
neigh = RandomNeighbourhood(10, 2), # 10 variables, 2 to move
pick = Metropolis(5.0) # T=5.0
)
Next Steps
- See Examples for heuristic usage in practice
- Learn about Constraint Programming integration
- Explore the API Reference for detailed documentation