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.