JBoss.org Community Documentation

- Prev
- Next

## Chapter 7. `Move` and neighborhood selection

## 7.1. `Move` and neighborhood introduction

### 7.1.1. What is a `Move`?

A `Move` is a change (or set of changes) from a solution A to a solution B. For example, the move below changes queen `C` from row `0` to row `2`:

The new solution is called a neighbor of the original solution, because it can be reached in a single `Move`. Although a single move can change multiple queens, the neighbors of a solution should always be a very small subset of all possible solutions. For example, on that original solution, these are all possible `changeMove`'s:

If we ignore the 4 `changeMove`'s that have not impact and are therefore not doable, we can see that number of moves is `n * (n - 1) = 12`. This is far less than the number of possible solutions, which is `n ^ n = 256`. As the problem scales out, the number of possible moves increases far less than the number of possible solutions.

Yet, in 4 `changeMove`'s or less we can reach any solution. For example we can reach a very different solution in 3 `changeMove`'s:

## Note

There are many other types of moves besides `changeMove`'s. Many move types are included out-of-the-box, but you can also implement custom moves.

A `Move` can affect multiple entities or even create/delete entities. But it must not change the problem facts.

All optimization algorithms use `Move`'s to transition from one solution to a neighbor solution. Therefor, all the optimization algorithms are confronted with `Move` selection: the craft of creating and iterating moves efficiently and the art of finding the most promising subset of random moves to evaluate first.

### 7.1.2. What is a `MoveSelector`?

A `MoveSelector`'s main function is to create `Iterator<Move>` when needed. An optimization algorithm will iterate through a subset of those moves.

Here's an example how to configure a `changeMoveSelector` for the optimization algorithm Local Search:

      <localSearch>

        <changeMoveSelector/>

        ...

      </localSearch>

Out of the box, this works and all properties of the `changeMoveSelector` are defaulted sensibly (unless that fails fast due to ambiguity). On the other hand, the configuration can be customized significantly for specific use cases. For example: you want want to configure a filter to discard pointless moves.

### 7.1.3. Subselecting of entities, values and other moves

To create a `Move`, we need to select 1 or more planning entities and/or planning values to move. Just like `MoveSelector`s, `EntitySelector`s and `ValueSelector`s need to support a similar feature set (such as scalable just-in-time selection). Therefor, they implement a common interface `Selector` and they are configured similarly.

A MoveSelector is often composed out of `EntitySelector`s, `ValueSelector`s or even other `MoveSelector`s, which can be configured individually if desired:

        <unionMoveSelector>

          <changeMoveSelector>

            <entitySelector>

              ...

            </entitySelector>

            <valueSelector>

              ...

            </valueSelector>

            ...

          </changeMoveSelector>

          ...

        </unionMoveSelector>

Together, this structure forms a `Selector` tree:

The root of this tree is a `MoveSelector` which is injected into the optimization algorithm implementation to be (partially) iterated in every step.

## 7.2. General `Selector` features

### 7.2.1. `CacheType`: Create moves in advance or Just In Time

A `Selector`'s `cacheType` determines when a selection (such as a `Move`, an entity, a value, ...) is created and how long it lives.

Almost every `Selector` supports setting a `cacheType`:

        <changeMoveSelector>

          <cacheType>PHASE</cacheType>

          ...

        </changeMoveSelector>

The following `cacheType`s are supported:

- `JUST_IN_TIME` (default): Not cached. Construct each selection (`Move`, ...) just before it's used. This scales up well in memory footprint.

- `STEP`: Cached. Create each selection (`Move`, ...) at the beginning of a step and cache them in a list for the remainder of the step. This scales up badly in memory footprint.

- `PHASE`: Cached. Create each selection (`Move`, ...) at the beginning of a `SolverPhase` and cache them in a list for the remainder of the `SolverPhase`. Some selections cannot be phase cached because the list changes every step. This scales up badly in memory footprint, but has a slight performance gain.

- `SOLVER`: Cached. Create each selection (`Move`, ...) at the beginning of a `Solver` and cache them in a list for the remainder of the `Solver`. Some selections cannot be solver cached because the list changes every step. This scales up badly in memory footprint, but has a slight performance gain.

A `cacheType` can be set on composite selectors too:

        <unionMoveSelector>

          <cacheType>PHASE</cacheType>

          <changeMoveSelector/>

          <swapMoveSelector/>

          ...

        </unionMoveSelector>

Nested selectors of a cached selector cannot be configured to be cached themselves, unless it's a higher `cacheType`. For example: a `STEP` cached `unionMoveSelector` can hold a `PHASE` cached `changeMoveSelector`, but not a `STEP` cached `changeMoveSelector`.

### 7.2.2. SelectionOrder: original, random or shuffled selection

A `Selector`'s `selectionOrder` determines the order in which the selections (such as `Move`s, entities, values, ...) are iterated. An optimization algorithm will usually only iterate through a subset of its `MoveSelector`'s selections, starting from the start, so the `selectionOrder` is critical to decide which `Move`s are evaluated.

Almost every `Selector` supports setting a `selectionOrder`:

        <changeMoveSelector>

          ...

          <selectionOrder>ORIGINAL</selectionOrder>

          ...

        </changeMoveSelector>

The following `selectionOrder`s are supported:

- `ORIGINAL`: Select the selections (`Move`s, ...) in default order. Each selection will be selected only once.

  - For example: A0, A1, A2, A3, ..., B0, B1, B2, B3, ...

- RANDOM (default): Select the selections (`Move`s, ...) in non-shuffled random order. A selection might be selected multiple times. This scales up well in performance.

  - For example: C2, A3, C2, B1, A0, C0, ...

- SHUFFLED: Select the selections (`Move`s, ...) in shuffled random order. Each selection will be selected only once. Requires `cacheType >= STEP`. This scales up badly in performance because a random number is generated for each element, even if it's not selected (which is the grand majority when scaling up).

  - For example: C2, A3, B1, A0, C0, ...

A `selectionOrder` can be set on composite selectors too.

## Note

When a `Selector` is cached, all of its nested `Selector`s will naturally default to `selectionOrder` `ORIGINAL`. Avoid overwriting the `selectionOrder` of those nested `Selector`s.

### 7.2.3. Recommended combinations of `CacheType` and `SelectionOrder`

#### 7.2.3.1. Just in time random selection (default)

This combination is great for big use cases (10 000 entities or more), as it scales up well in memory footprint and performance. Other combinations are often not even viable on such sizes. It works for smaller use cases too, so it's a good way to start out. It's the default, so this explicit configuration of `cacheType` and `selectionOrder` is actually obsolete:

        <unionMoveSelector>

          <cacheType>JUST_IN_TIME</cacheType>

          <selectionOrder>RANDOM</selectionOrder>

          <changeMoveSelector/>

          <swapMoveSelector/>

        </unionMoveSelector>

Here's how it works. When `Iterator<Move>.next()` is called, a child `MoveSelector` is randomly selected (1), which creates a random `Move` is created (2, 3, 4) and is then returned (5):

Notice that it never creates a list of `Move`s and it generates random numbers only for `Move`s that are actually selected.

#### 7.2.3.2. Cached shuffled selection

This combination often wins for small and medium use cases (5000 entities or less). Beyond that size, it scales up badly in memory footprint and performance.

        <unionMoveSelector>

          <cacheType>PHASE</cacheType>

          <selectionOrder>SHUFFLED</selectionOrder>

          <changeMoveSelector/>

          <swapMoveSelector/>

        </unionMoveSelector>

Here's how it works. At the start of the phase (or step depending on the `cacheType`), all moves are created (1) and cached (2). When `MoveSelector.iterator()` is called, the moves are shuffled (3). When `Iterator<Move>.next()` is called, the next element in the shuffled list is returned (4):

Notice that each `Move` will only be selected once, even though they are selected in random order.

Use cacheType PHASE if none of the (possibly nested) Selectors require `STEP`. Otherwise, do something like this:

        <unionMoveSelector>

          <cacheType>STEP</cacheType>

          <selectionOrder>SHUFFLED</selectionOrder>

          <changeMoveSelector>

            <cacheType>PHASE</cacheType>

          </changeMoveSelector>

          <swapMoveSelector/>

            <cacheType>PHASE</cacheType>

          </swapMoveSelector>

          <pillarSwapMoveSelector/><!-- Does not support cacheType PHASE -->

        </unionMoveSelector>

#### 7.2.3.3. Cached random selection

This combination is often a worthy competitor for medium and even big use cases (if you have a lot of memory at your disposal). It scales up well in performance, but badly in memory.

        <unionMoveSelector>

          <cacheType>PHASE</cacheType>

          <selectionOrder>RANDOM</selectionOrder>

          <changeMoveSelector/>

          <swapMoveSelector/>

        </unionMoveSelector>

### 7.2.4. Filtered selection

There are certain moves that you don't want to select, because:

- The move is pointless and would only waste CPU time. For example, swapping 2 lectures of the same course will result in the same score and the same schedule because all lectures of 1 course are interchangeable (same teacher, same students, same topic).

- Doing the move would break a build-in hard constraint, so the solution would be infeasible but the score function doesn't check build-in hard constraints (for performance gain). For example, don't change a gym lecture to a room which is not a gym room.

  - Note that any build-in hard constraint must usually be filtered on every move type. For example, don't swap the room of a gym lecture with another lecture if the other lecture's original room isn't a gym room.

Filtered selection can happen on any Selector in the selector tree, including any `MoveSelector`, `EntitySelector` or `ValueSelector`:

Filtering us the interface `SelectionFilter`:

    public interface SelectionFilter<T> {

        boolean accept(ScoreDirector scoreDirector, T selection);

    }

Implement the method `accept` to return `false` on a discarded `selection`. Unaccepted moves will not be selected and will therefore never have their method `doMove` called.

    public class DifferentCourseSwapMoveFilter implements SelectionFilter<SwapMove> {

        public boolean accept(ScoreDirector scoreDirector, SwapMove move) {

            Lecture leftLecture = (Lecture) move.getLeftEntity();

            Lecture rightLecture = (Lecture) move.getRightEntity();

            return !leftLecture.getCourse().equals(rightLecture.getCourse());

        }

    }

Apply the filter on the lowest level possible. In most cases, you 'll need to know both the entity and the value involved and you'll have to apply a `moveFilterClass` on the `moveSelector`:

        <swapMoveSelector>

          <moveFilterClass>org.drools.planner.examples.curriculumcourse.solver.move.DifferentCourseSwapMoveFilter</moveFilterClass>

        </swapMoveSelector>

But if possible apply it on a lower levels, such as an `entityFilterClass` on the `entitySelector` or a `valueFilterClass` on the `valueSelector`:

        <changeMoveSelector>

          <entitySelector>

            <entityFilterClass>...EntityFilter</entityFilterClass>

          </entitySelector>

        </changeMoveSelector>

Filtered selection works with any kind of `cacheType` and `selectionOrder`. You can configure multiple `*FilterClass` elements on a single selector.

### 7.2.5. Sorted selection

TODO Not yet implemented.

### 7.2.6. Probability selection

TODO

## 7.3. Generic MoveSelectors

### 7.3.1. `changeMoveSelector`

For 1 planning variable, the `ChangeMove` selects 1 planning entity and 1 planning value and assigns the entity's variable to that value.

Simplest configuration:

        <changeMoveSelector/>

Advanced configuration:

        <changeMoveSelector>

          ... <!-- Normal moveSelector properties -->

          <entitySelector>

            ...

          </entitySelector>

          <valueSelector>

            ...

          </valueSelector>

        </changeMoveSelector>

A `ChangeMove` is the finest grained move.

## Important

Almost every `moveSelector` configuration injected into a metaheuristic algorithm should include a changeMoveSelector or a custom implementation. This guarantees that every possible `Solution` can be reached through applying a number of moves in sequence (not taking score traps into account). Of course, normally it is unioned with other, more course grained moves.

### 7.3.2. swapMoveSelector

The `SwapMove` selects 2 different planning entities and swaps the planning values of all their planning variables.

Simplest configuration:

        <swapMoveSelector/>

Advanced configuration:

        <swapMoveSelector>

          ... <!-- Normal moveSelector properties -->

          <entitySelector>

            ...

          </entitySelector>

          <secondaryEntitySelector>

            ...

          </secondaryEntitySelector>

        </swapMoveSelector>

The `secondaryEntitySelector` is rarely needed: if it is not specified, entities from the same `entitySelector` are swapped.

Although a `SwapMove` on a single variable is essentially just 2 `ChangeMove`s, it's often the winning step where the first of the 2 `ChangeMove`s would not be the winning step because it leave the solution in a state with broken hard constraints. For example: swapping the room of 2 lectures doesn't bring the solution in a intermediate state where both lectures are in the same room which breaks a hard constraint.

### 7.3.3. pillarSwapMoveSelector

A pillar is a set of planning entities which have the same planning value(s) for each of their planning variables. The `pillarSwapMove` selects 2 different entity pillars and swaps the values of all their variables for all their entities.

Simplest configuration:

        <pillarSwapMoveSelector/>

Advanced configuration:

        <pillarSwapMoveSelector>

          ... <!-- Normal moveSelector properties -->

          <pillarSelector>

            <entitySelector>

              ...

            </entitySelector>

          </pillarSelector>

          <secondaryPillarSelector>

            <entitySelector>

              ...

            </entitySelector>

          </secondaryPillarSelector>

        </pillarSwapMoveSelector>

The `secondaryPillarSelector` is rarely needed: if it is not specified, entities from the same `pillarSelector` are swapped.

### 7.3.4. subChainChangeMoveSelector

TODO

### 7.3.5. subChainSwapMoveSelector

TODO

## 7.4. Combining multiple `MoveSelector`s

### 7.4.1. unionMoveSelector

TODO

### 7.4.2. cartesianProductMoveSelector

TODO Not yet implemented.

## 7.5. EntitySelector

### 7.5.1. TODO

TODO

## 7.6. ValueSelector

### 7.6.1. TODO

TODO

## 7.7. Custom moves

### 7.7.1. Custom moves introduction

Instead of reusing the generic `Move`s (such as `ChangeMove`) you can also implement your own `Move`s. Generic and custom `MoveSelector`s can be combined as wanted.

A custom `Move` can be tailored to work to the advantage of your constraints. For example, in examination scheduling, changing the period of an exam A also changes te period of all the exams that need to coincide with exam A.

A custom `Move` is also slightly faster than a generic `Move`. However, it's far more work to implement and much harder to avoid bugs. After implementing a custom `Move`, make sure to turn on `environmentMode` `TRACE` to check for score corruptions.

### 7.7.2. The interface `Move`

Your custom moves must implement the `Move` interface:

    public interface Move {

        boolean isMoveDoable(ScoreDirector scoreDirector);

        Move createUndoMove(ScoreDirector scoreDirector);

        void doMove(ScoreDirector scoreDirector);

        Collection<? extends Object> getPlanningEntities();

        Collection<? extends Object> getPlanningValues();

    }

Let's take a look at the `Move` implementation for 4 queens which moves a queen to a different row:

    public class RowChangeMove implements Move {

        private Queen queen;

        private Row toRow;

        public RowChangeMove(Queen queen, Row toRow) {

            this.queen = queen;

            this.toRow = toRow;

        }

        // ... see below

    }

An instance of `RowChangeMove` moves a queen from its current row to a different row.

Planner calls the `doMove(ScoreDirector)` method to do a move. The `Move` implementation must notify the `ScoreDirector` of any changes it make to the planning entities's variables:

        public void doMove(ScoreDirector scoreDirector) {

            scoreDirector.beforeVariableChanged(queen, "row"); // before changes are made to the queen.row

            queen.setRow(toRow);

            scoreDirector.afterVariableChanged(queen, "row"); // after changes are made to the queen.row

        }

You need to call the methods `scoreDirector.beforeVariableChanged(Object, String)` and `scoreDirector.afterVariableChanged(Object, String)` directly before and after modifying the entity. Alternatively, you can also call the methods `scoreDirector.beforeAllVariablesChanged(Object)` and `scoreDirector.afterAllVariablesChanged(Object)`.

## Note

You can alter multiple entities in a single move and effectively create a big move (also known as a coarse-grained move).

## Warning

A `Move` can only change/add/remove planning entities, it must not change any of the problem facts.

Planner automatically filters out non doable moves by calling the `isDoable(ScoreDirector)` method on a move. A non doable move is:

- A move that changes nothing on the current solution. For example, moving queen B0 to row 0 is not doable, because it is already there.

- A move that is impossible to do on the current solution. For example, moving queen B0 to row 10 is not doable because it would move it outside the board limits.

In the n queens example, a move which moves the queen from its current row to the same row isn't doable:

        public boolean isMoveDoable(ScoreDirector scoreDirector) {

            return !ObjectUtils.equals(queen.getRow(), toRow);

        }

Because we won't generate a move which can move a queen outside the board limits, we don't need to check it. A move that is currently not doable could become doable on the working `Solution` of a later step.

Each move has an undo move: a move (normally of the same type) which does the exact opposite. In the example above the undo move of C0 to C2 would be the move C2 to C0. An undo move is created from a `Move`, before the `Move` has been done on the current solution.

        public Move createUndoMove(ScoreDirector scoreDirector) {

            return new RowChangeMove(queen, queen.getRow());

        }

Notice that if C0 would have already been moved to C2, the undo move would create the move C2 to C2, instead of the move C2 to C0.

A solver phase might do and undo the same `Move` more than once. In fact, many solver phases will iteratively do an undo a number of moves to evaluate them, before selecting one of those and doing that move again (without undoing it this time).

A `Move` must implement the `getPlanningEntities()` and `getPlanningValues()` methods. They are used by entity tabu and value tabu respectively. When they are called, the `Move` has already been done.

        public List<? extends Object> getPlanningEntities() {

            return Collections.singletonList(queen);

        }

        public Collection<? extends Object> getPlanningValues() {

            return Collections.singletonList(toRow);

        }

If your `Move` changes multiple planning entities, return all of them in `getPlanningEntities()` and return all their values (to which they are changing) in `getPlanningValues()`.

        public Collection<? extends Object> getPlanningEntities() {

            return Arrays.asList(leftCloudProcess, rightCloudProcess);

        }

        public Collection<? extends Object> getPlanningValues() {

            return Arrays.asList(leftCloudProcess.getComputer(), rightCloudProcess.getComputer());

        }

A `Move` must implement the `equals()` and `hashCode()` methods. 2 moves which make the same change on a solution, should be equal.

        public boolean equals(Object o) {

            if (this == o) {

                return true;

            } else if (o instanceof RowChangeMove) {

                RowChangeMove other = (RowChangeMove) o;

                return new EqualsBuilder()

                        .append(queen, other.queen)

                        .append(toRow, other.toRow)

                        .isEquals();

            } else {

                return false;

            }

        }

        public int hashCode() {

            return new HashCodeBuilder()

                    .append(queen)

                    .append(toRow)

                    .toHashCode();

        }

Notice that it checks if the other move is an instance of the same move type. This `instanceof` check is important because a move will be compared to a move with another move type if you're using more then 1 move type.

It's also recommended to implement the `toString()` method as it allows you to read Planner's logging more easily:

        public String toString() {

            return queen + " => " + toRow;

        }

Now that we can implement a single custom `Move`, let's take a look at generating such custom moves.

### 7.7.3. `MoveListFactory`: the easy way to generate custom moves

The easiest way to generate custom moves is by implementing the interface `MoveListFactory`:

    public interface MoveListFactory {

        List<Move> createMoveList(Solution solution);

    }

For example:

    public class RowChangeMoveFactory implements MoveListFactory {

        public List<Move> createMoveList(Solution solution) {

            NQueens nQueens = (NQueens) solution;

            List<Move> moveList = new ArrayList<Move>();

            for (Queen queen : nQueens.getQueenList()) {

                for (Row toRow : nQueens.getRowList()) {

                    moveList.add(new RowChangeMove(queen, toRow));

                }

            }

            return moveList;

        }

    }

Simple configuration (which can be nested in a `unionMoveSelector` just like any other `MoveSelector`):

        <moveListFactory>

          <moveListFactoryClass>org.drools.planner.examples.nqueens.solver.move.factory.RowChangeMoveFactory</moveListFactoryClass>

        </moveListFactory>

Advanced configuration:

        <moveListFactory>

          ... <!-- Normal moveSelector properties -->

          <moveListFactoryClass>org.drools.planner.examples.nqueens.solver.move.factory.RowChangeMoveFactory</moveListFactoryClass>

        </moveListFactory>

Because the `MoveListFactory` generates all moves at once in a List<Move>, it does not support `cacheType` `JUST_IN_TIME`. Therefore, `moveListFactory` uses `cacheType` `STEP` by default and it scales badly in memory footprint.

### 7.7.4. `MoveIteratorFactory`: generate custom moves just in time

Use this advanced form to generate custom moves by implementing the interface `MoveIteratorFactory`:

    public interface MoveIteratorFactory {

        long getSize(ScoreDirector scoreDirector);

        Iterator<Move> createOriginalMoveIterator(ScoreDirector scoreDirector);

        Iterator<Move> createRandomMoveIterator(ScoreDirector scoreDirector, Random workingRandom);

    }

The method `getSize()` must give an estimation of the size. It doesn't need to be correct. The method `createOriginalMoveIterator` is called if the `selectionOrder is` `ORIGINAL` or if it is cached. The method `createRandomMoveIterator` is called for `selectionOrder` `RANDOM` combined with cacheType `JUST_IN_TIME`.

## Important

Don't create a collection (list, array, map, set) of `Move`s when creating the `Iterator<Move>`: the whole purpose of `MoveIteratorFactory` over `MoveListFactory` is giving you the ability to create a `Move` just in time in the `Iterator`'s method `next()`.

Simple configuration (which can be nested in a `unionMoveSelector` just like any other `MoveSelector`):

        <moveIteratorFactory>

          <moveIteratorFactoryClass>...</moveIteratorFactoryClass>

        </moveIteratorFactory>

Advanced configuration:

        <moveIteratorFactory>

          ... <!-- Normal moveSelector properties -->

          <moveIteratorFactoryClass>...</moveIteratorFactoryClass>

        </moveIteratorFactory>

### 7.7.5. Move generation through DRL

Not yet supported.

- PrevChapter 6. Optimization algorithms
- Top of page
- Front page
- NextChapter 8. Construction heuristics