The OpenTS Tabu Search expects that our objective function be able
to either evaluate an entire solution from scratch or evaluate
a possible solution that would result from the application of a Move object.
We determine what the Tabu Search expects in the evaluate(...)
object by testing for whether or not the Move object is null.
Either way, you must return the complete value,
not the incremental, value of the solution.
import org.coinor.opents.*;
public class MyObjectiveFunction implements ObjectiveFunction
{
...
public double[] evaluate( Solution solution, Move move )
{
int[] tour = ((MySolution)solution).tour;
int len = tour.length;
// If move is null, calculate distance from scratch
if( move == null )
{
double dist = 0;
for( int i = 0; i < len; i++ )
dist += matrix[ tour[i] ][ i+1 >= len ? 0 : tour[i+1] ];
return new double[]{ dist };
} // end if: move == null
// Else calculate incrementally
else
{
MySwapMove mv = (MySwapMove)move;
int pos1 = -1;
int pos2 = -1;
// Find positions
for( int i = 0; i < tour.length; i++ )
if( tour[i] == mv.customer ) {
pos1 = i;
break;
}
pos2 = pos1 + mv.movement;
// Logic below requires pos1 < pos2
if( pos1 > pos2 )
{
int temp = pos2;
pos2 = pos1;
pos1 = temp;
} // end if
// Prior objective value
double dist = solution.getObjectiveValue()[0];
// Treat a pair swap move differently
if( pos1 + 1 == pos2 )
{
...
return new double[]{ dist };
} // end if: pair swap
// Else the swap is separated by at least one customer
else
{
...
return new double[]{ dist };
} // end else: not a pair swap
} // end else: calculate incremental
} // end evaluate
...
} // end class MyObjectiveFunction
So just how do we evaluate the incremental change?
We'll take a look at that next.
If we didn't know any better, or if our objective function
was more complex, we could always manipulate the solution,
evaluate from scratch, and then un-manipulate the solution
to put it back to the way it was.