FRAMES NO FRAMES

Large Neighborhoods
PREVIOUS NEXT
Description
Local Search versus Large Neighborhood Search
Scheduler Large Neighborhood
Extending the Library of Large Neighborhoods
Description

The main idea behind Large Neighborhood Search is to focus the search on a sub-part of the decision variables of the problem. In this approach, we start from a known solution (current solution) and try to improve it by restoring (freezing) the values subset of some decision variables stored in this solution, while leaving other decision variables free. A goal is then used to try fix these remaining decision variables.

The set of decision variables is partitioned into two sets: the set of selected decision variables, and the other variables. Different restore policies are then applied, depending if the variable belongs or not to the set of selected decision variables.

To define the set of selected decision variables, use the pure virtual member function defineSelected of the class IloLargeNHoodI. Its signature is:

IloSolution IloLargeNHoodI::defineSelected(IloSolver solver, IloInt index)

The instance of class IloSolution returned must contain the set of selected decision variables corresponding to the neighbor with index index.

Local Search versus Large Neighborhood Search

At first glance, a large neighborhood and a neighborhood look very similar. But there are also some differences:

  • In a local search approach, the neighborhoods are enumerated explicitly (method define). The size of the neighborhood is the number of neighbors. A goal may be used to validate the neighbor: to check for an instance where all the constraints are fulfilled.
  • In a large neighborhood approach, the size of the neighborhood is the number of sub-problems to solve. Each sub-problem may contain a potentially a large number of neighbors, and thus is a combinatorial problem itself. To exhibit a neighbor a search algorithm must be used.

Suppose there is a scheduling problem made of three activities requiring a unary resource. Further, we have found a first solution and wish to iteratively select an activity and try to relocate it on the resource. In a local search approach, the size of the neighborhood is nine: each activity can be either first, last or in the middle on the resource. A sub-goal may be used to ensure that all other constraints are fulfilled (side constraints). In a large neighborhood search approach, the number of sub-problems to solve is the number of activities (three in this case). For each sub-problem, we can use, for instance, the goal IloRankForward to find a neighbor.

Scheduler Large Neighborhood

Large neighborhoods dedicated to scheduling problems derive from the class IloSchedulerLargeNHoodI.

Two predefined neighborhoods dedicated to scheduling problems are available:

  • IloRelocateActivityNHood which iteratively selects an activity and tries to reschedule it somewhere else.
  • IloTimeWindowNHood which iteratively selects a time window and tries to reschedule the activities within the time window.

The class IloSchedulerLargeNHoodI provides specialization of functionalities available in class IloLargeNHoodI for activities and resource constraints. For instance, in class IloLargeNHood, there is a member function isSelected used to check if an extractable is selected or not:

IloBool isSelected(IloExtractable extr) const;

A similar member function exists on class IloLargeNHood to check if activities and resource constraints are selected:

IloBool isSelected(IloActivity activity) const;

IloBool isSelected(IloResourceConstraint rc) const;

In addition to the above functionalities, the main difference between class IloSchedulerLargeNHoodI and class IloLargeNHoodI is that class IloSchedulerLargeNHoodI overloads the method finalizeDelta. To ease the definition of neighborhoods dedicated to scheduling problems, this method removes from any resource constraint that is not selected the next, the previous, the direct successors and the direct predecessors, in case these are selected. These are then replaced by new appropriate direct predecessors and direct successors.

As an example, suppose a set of activities are scheduled on a unary resource, and a time window neighborhood is applied. In the following equation, R is the resource, [Ai] represents an activity and | represents the time window boundary.

R: [A0][A1]..[Ai-1] | A[i]..[Aj] | [Aj+1]..[Ak]

Here, the time window contains all activities from Ai to Aj. In the current solution Ai is the next (and also the direct successor) of Ai-1. Similarly in the current solution, activity Aj is the previous (and also the direct predecessors) of activity Aj+1. Both the next of Ai-1, the successor of Ai-1, the previous of Aj+1 and the direct predecessors of Aj+1 are removed. Also, Aj+1 is set as the successor of Ai-1, and Ai-1 is set as the predecessor of Aj+1.

Extending the Library of Large Neighborhoods

There are two ways to extend the library with new large neighborhoods:

  • by combining existing large neighborhoods.
  • by deriving from class IloLargeNHoodI.
Combining Large Neighborhoods

There are two ways to combine large neighborhoods:

  • Union of two neighborhoods
  • Intersection of two neighborhoods
Union of Neighborhoods

The size of the resulting neighborhood is the product of the sizes of the two neighborhoods: size(n1)*size(n2).

The set of selected extractables of the union for index i is the union of the sets of selected extractables for index i1 for neighborhood n1, and for index i2 for neighborhood n2 such that i = i1*size(n2) + i2.

For any extractable, if it belongs to the union of the selected sets, it will be restored only if both neighborhoods specify that it must be restored. In case it does not belong to the union of the selected sets, then it is restored if at least one of the two neighborhoods specifies that it must be restored.

For example, a neighborhood that relocates two activities at once can be defined as the union of two relocate activity neighborhoods:

   IloSchedulerLargeNHood n1 = IloRelocateActivityNHood(env);
   IloSchedulerLargeNHood n2 = IloRelocateActivityNHood(env);
   IloLargeNHood nhood = IloUnionNHood(env, n1, n2);
Intersection of Neighborhoods

As for the union, the size of the resulting neighborhood is the product of the sizes of the two neighborhoods: size(n1)*size(n2).

The set of selected extractables of the intersection for index i is the intersection of the sets of selected extractables for index i1 for neighborhood n1 and for index i2 for neighborhood n2 such that i = i1*size(n2) + i2.

For any extractable, if it belongs to the intersection of the selected sets, it will be restored only if both neighborhoods specify that it must be restored. In case it does not belong to the intersection of the selected sets, then it is restored if at least one of the two neighborhoods specify that it must be restored.

For instance, a neighborhood that relocates activities within a time window with a window size 20 and a window step 10 can be defined as:

  IloSchedulerLargeNHood n1 = IloRelocateActivityNHood(env);
  IloSchedulerLargeNHood n2 = IloTimeWindowNHood(env, 20, 10);
  IloLargeNHood nhood = IloIntersectNHood(env, n1, n2);
Implementing a New Large Neighborhood

When deriving from class IloLargeNHoodI (or IloSchedulerLargeNHoodI), you have to overload at least the three following virtual member functions:

      IloSolution defineSelected(IloSolver solver, IloInt index);
      void start(IloSolver solver, IloSolution);
      IlcInt getSize(IloSolver solver, IloInt index);

The method defineSelected is a pure virtual member function and must be overloaded to define the selected extractables corresponding to a neighbor with index.

Methods start and getSize are pure virtual member functions of class IloNHoodI and must be overloaded as for any neighborhood.

Other virtual member functions on class IloLargeNHoodI are:

      void defineRestoreInfo(IloSolver solver, IloSolution delta);
      void finalizeDelta(IloSolver solver, IloSolution delta);

Method defineRestoreInfo is used to specify for each extractable the restore information; that is, either the restore fields for activities and resource constraints, or the restore status for other extractables. If this information differs from the current solution, then this information is added to the delta solution provided as argument. Default implementation uses the predicates.

Method finalizeDelta can be used to complete the definition delta. By default it doesn't do anything.

Note that class IloLargeHNoodI derives from class IloNHoodI and redefines the virtual method IloNHoodI::define . It calls method defineSelected, and then if not empty, calls method defineRestoreInfo and finalizeDelta. So when deriving class IloLargeHNoodI, you do not need to overload virtual method define.

PREVIOUS NEXT