IBM ILOG Dispatcher User's Manual > Developing Dispatcher Applications > Developing Your Own Neighborhoods > Neighborhood Description > Defining the Neighborhood

You begin by defining a neighborhood which exchanges the visit here with each one of a set of predefined visits there. You also declare other fields which are used internally: rsolution holds the current solution and size holds the size of the neighborhood (the number of neighbors). The fields prevHere and nextHere are used to hold the previous and next visits of here in the current solution. The field vehHere holds the vehicle performing the visit here and the field perfHere returns whether the visit here is performed or not.

class LocalizedExchangeI : public IloNHoodI {
private:
  IloVisit           _here;
  IloVisitArray      _there;
  IloRoutingSolution _rsolution;
  IloInt             _size;

  IloVisit           _prevHere;
  IloVisit           _nextHere;
  IloVehicle         _vehHere;
  IloBool            _perfHere;

You then declare the methods for the new neighborhood class. You use a constructor which takes a visit to swap and an array of visits with which this "focus" visit can be swapped. Three virtual member functions of IloNHoodI are also redefined: start, define, and getSize. The latter is implemented inline.

public:
  LocalizedExchangeI(IloEnv env, IloVisit visit, IloVisitArray arr);
  void start(IloSolver, IloSolution);
  IloSolution define(IloSolver, IloInt);
  IloInt getSize(IloSolver) const { return _size; }
};

The constructor of the subneighborhood builds the base class and installs the passed parameters.

LocalizedExchangeI::LocalizedExchangeI(IloEnv env,
                                       IloVisit visit,
                                       IloVisitArray arr)
  : IloNHoodI(env), _here(visit), _there(arr) { }

The start method is called by local search goals such as IloSingleMove to indicate the current solution. You keep a reference to the current solution, which is cast into an IloRoutingSolution for later use. (Casting the solution to an IloRoutingSolution allows you to use API suitable for routing problems on the solution. Note that the solution itself is not altered in any way by this cast; it simply allows the use of a routing-based API on the solution.) The start method also performs operations that need only to be done once. For example, you retrieve the following from the solution: the preceding and following visits of here, the vehicle performing the visit here, and whether or not the visit is performed. (If here is not performed, the values of the other three pieces of information are later ignored.) You also calculate the size of the neighborhood, which is the size of the there set. However, in order to be robust, you must set the size of the neighborhood to 0 if here is not in the current solution (in which case it does not make sense to involve it in a move), or if the visit is not extracted (in which case attempting to move it will cause Solver to throw an exception later.)

void LocalizedExchangeI::start(IloSolver solver,
                               IloSolution solution) {
  _rsolution = IloRoutingSolution(solution);
  if (solver.isExtracted(_here) && solution.contains(_here)) {
    _size = _there.getSize();
    _prevHere = _rsolution.getPrev(_here);
    _nextHere = _rsolution.getNext(_here);
    _vehHere = _rsolution.getVehicle(_here);
    _perfHere = _rsolution.isPerformed(_here);
  }
  else
    _size = 0;
}

The define method is usually the main workhorse of any neighborhood definition. It takes an index and converts it to a solution delta. (The solution delta is a solution holding only the part of the current solution which needs to be changed, together with its new value.) This delta needs to be of type IloSolution, not of type IloRoutingSolution, although it can be cast to an IloRoutingSolution to perform routing operations on it. The first thing you do in define is to determine what the index signifies. (In other words, you determine to which swap the index corresponds.) This is easily done as here is swapped with there[index]. Then you perform various checks which allow you to ignore this neighbor in certain circumstances. Specifically, when the solution does not contain the visit there[index], or when this visit is not extracted, you can ignore the neighbor. Likewise, when both visits are unperformed (a swap is useless), or when both visits are served by the same vehicle (the move you define is only inter-route, but could be generalized), you ignore the neighbor. The neighbor is ignored by returning an empty handle (here you use return 0 to perform this action).

IloSolution LocalizedExchangeI::define(IloSolver solver, IloInt index) {
  IloVisit v = _there[index];
  if (!_rsolution.getSolution().contains(v)) return 0;
  if (!solver.isExtracted(v)) return 0;
  IloBool perfV = _rsolution.isPerformed(v);
  if (!_perfHere && !perfV) return 0;
  if (_perfHere && perfV && _vehHere == _rsolution.getVehicle(v)) return 0;