Search code examples
spring-bootoptaplanner

Set a ListMoveChange filter when using OptaPlanner Spring Boot starter


We are using the OptaPlanner Spring Boot starter to create a vehicle routing problem solver based on the example in the OptaPlanner quickstarts:

https://github.com/kiegroup/optaplanner-quickstarts/tree/stable/use-cases/vehicle-routing

So we do not have an solveConfig.xml file. We would like to define a filter for ListChangeMoves but it's not clear how we would register this without using an XML file. We have tried using a solverConfig.xml e.g.

<localSearch>
  <unionMoveSelector>
    <listChangeMoveSelector>
      <filterClass>my.filter.Class</filterClass>
    </listChangeMoveSelector>
  </unionMoveSelector>
</localSearch>

But this is not working. Is there an example of setting up a filter for list moves?


Solution

  • This is a XML solver config using a a swap move selector and a change move selector with move filtering:

      <constructionHeuristic/>
      <localSearch>
        <unionMoveSelector>
          <changeMoveSelector>
            <filterClass>org.acme.vehiclerouting.solver.ChangeMoveSelectorFilter</filterClass>
          </changeMoveSelector>
          <swapMoveSelector/>
        </unionMoveSelector>
      </localSearch>
    

    If you don't want to use swap moves, then you don't need the union move selector and the configuration can be simplified to:

      <constructionHeuristic/>
      <localSearch>
        <changeMoveSelector>
          <filterClass>org.acme.vehiclerouting.solver.ChangeMoveSelectorFilter</filterClass>
        </changeMoveSelector>
      </localSearch>
    

    A few comments:

    • I'm including the CH phase because it is necessary in a typical case. See OptaPlanner terminates immediately if I add constructionHeuristic config for an explanation.
    • The ChangeMoveSelector is automatically configured to produce ListChangeMoves if the planning entity has a @PlanningListVariable. There is no <listChangeMoveSelector> config element.
    • More information including how to implement the move selection filter can be found in the documentation.

    UPDATE: No XML configuration

    It's possible to inject SolverConfig, modify it and then use it to create other objects, for example Solver, SolverManager, and ScoreManager.

    The code would look something like this:

    
    @Component
    class MyService {
    
        // Don't inject these.
        private final SolverManager<VrpSolution, Long> solverManager;
        private final ScoreManager<VrpSolution, HardSoftScore> scoreManager;
    
        // But inject the SolverConfig.
        public MyService(SolverConfig solverConfig) {
            // And instantiate SolverManager and ScoreManager manually.
            this.solverManager = SolverManager.<VrpSolution, Long>create(
                solverConfig.withPhaseList(Arrays.asList(
                    new ConstructionHeuristicPhaseConfig(),
                    new LocalSearchPhaseConfig().withMoveSelectorConfig(
                            new ChangeMoveSelectorConfig()
                                    .withFilterClass(MyFilter.class)))));
            this.scoreManager = ScoreManager.create(SolverFactory.create(solverConfig));
        }
    }
    

    Pros:

    • SolverConfig will be initialized by OptaPlannerAutoConfiguration (from optaplanner-spring-boot-starter) before it's injected into your component. That means:
    • The solution and entity classes will be auto-discovered and you don't have to specify them (which you have to in solverConfig.xml).
    • You can use application.properties to do minor solver config tweaks, for example set time-spent termination.

    Cons:

    • You have to create Solver,SolverManager, and ScoreManager instances manually. Specifically, you can't have a @Bean method producing an instance of one of the types above because that would deactivate OptaPlannerAutoConfiguration, which creates the SolverConfig bean.