By

What happens when a single application has to address a problem using distinct configurations? How can you solve two different planning problems in the same application, potentially in sequence? With two SolverManager instances, one for each problem. In the article, we’ll show you how to configure and inject two SolverManager instances in a single application.

An application may require different configurations to solve a problem or solve problem A to acquire input for solving problem B. That’s why using multiple solvers within one application becomes necessary. The Timefold Solver integrates with Quarkus and Spring Boot, making it easy to manage various solver configurations. It provides all the tools for the hassle-free configuration and utilization of distinct solvers.

In this article, we will discuss how Timefold allows us to configure multiple solver settings and explain how to use the different SolverManager instances in the application.

Distinct Solver Configurations Problem

Let’s consider a situation where we need to solve two distinct school timetabling problems:

  1. Initially, a specific group of teachers is designated to teach the available lessons.

  2. The next step involves assigning lessons to the rooms.

The first step will need a different domain model compared to the second. The file teachersConfig.xml shows the XML structure of the first step:

<solver xmlns="https://timefold.ai/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="https://timefold.ai/xsd/solver https://timefold.ai/xsd/solver/solver.xsd">
  <solutionClass>org.acme.schooltimetabling.domain.TeacherToLessonSchedule</solutionClass>
  <entityClass>org.acme.schooltimetabling.domain.Teacher</entityClass>
</solver>

The following XML file, roomsConfig.xml, describes the model of the second optimization stage:

<solver xmlns="https://timefold.ai/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="https://timefold.ai/xsd/solver https://timefold.ai/xsd/solver/solver.xsd">
  <solutionClass>org.acme.schooltimetabling.domain.Timetable</solutionClass>
  <entityClass>org.acme.schooltimetabling.domain.Lesson</entityClass>
</solver>

While we can manually create factories that instantiate managed resources for each problem, doing so requires boilerplate and error-prone code.

Injecting Multiple Instances of SolverManager

The Timefold Quarkus and Spring integrations provide built-in managed instances of the SolverManager resource for each problem definition identified during the configuration process. Therefore, there is no need for custom logic to instantiate managed resources when configuring multiple solver settings. Instead, named properties should be defined for each desired configuration. Let’s examine a simple example where we set the maximum amount of time spent on optimizing a single solver configuration in both Quarkus and Spring Boot:

        
# The Quarkus configuration limits the optimization time to 5 seconds
quarkus.timefold.solver.termination.spent-limit=5s
        
      
        
# The Spring Boot configuration limits the optimization time to 5 seconds
timefold.solver.termination.spent-limit=5s
        
      

Let’s add two configurations to optimize with distinct optimization time spent. One configuration runs for 5 seconds, and the other runs for 60 seconds.

        
# The Quarkus configuration solver1 limits optimization to 5 seconds.
# solver2 runs for 60 seconds.
quarkus.timefold.solver."solver1".termination.spent-limit=5s
quarkus.timefold.solver."solver2".termination.spent-limit=60s
        
      
        
# The Spring Boot configuration solver1 limits optimization to 5 seconds.
# solver2 runs for 60 seconds.
timefold.solver.solver1.termination.spent-limit=5s
timefold.solver.solver2.termination.spent-limit=60s
        
      

We can configure multiple solvers using the namespace *.timefold.solver.<solverName> and specify the named property <solverName> for each solver. It is important to note that Quarkus configuration requires properties to have a prefix quarkus and named properties enclosed in double quotes.

Solver Configuration Properties

The following properties are supported:

[quarkus].timefold.solver.<solverName>.solver-config-xml
A classpath resource to read the solver configuration XML.

[quarkus].timefold.solver.<solverName>.environment-mode
Enable runtime assertions to detect common bugs in your implementation during development.
Defaults to REPRODUCIBLE.

[quarkus].timefold.solver.<solverName>.daemon
Enable daemon mode. Defaults to false.

[quarkus].timefold.solver.<solverName>.move-thread-count
Enable multi-threaded solving for a single problem. Defaults to NONE.

[quarkus].timefold.solver.<solverName>.domain-access-type
How Timefold Solver should access the domain model. Defaults to REFLECTION.

[quarkus].timefold.solver.<solverName>.termination.spent-limit
How long the solver can run.

[quarkus].timefold.solver.<solverName>.termination.unimproved-spent-limit
How long the solver can run without finding a new best solution after finding a new best solution.

[quarkus].timefold.solver.<solverName>.termination.best-score-limit
Terminates the solver when a specific score (or better) has been reached.

Working with Multiple Solvers

When revisiting the timetabling problem discussed in Section 2, it is necessary to fill out the properties file for both Quarkus and Spring Boot for the two distinct optimization problems in the following way:

        
# Quarkus timetabling solver configurations

quarkus.timefold.solver."teacherSolver".solver-config-xml=teachersConfig.xml
quarkus.timefold.solver."roomSolver".solver-config-xml=roomsConfig.xml
        
      
        
# Spring Boot timetabling solver configurations

timefold.solver.teacherSolver.solver-config-xml=teachersConfig.xml
timefold.solver.roomSolver.solver-config-xml=roomsConfig.xml
        
      

Use the following code to inject the resources.

@Path("/path")
public class Resource {

    @Named("teacherSolver")
    SolverManager<TeacherToLessonSchedule, String> teacherToLessonScheduleSolverManager;

    @Named("roomSolver")
    SolverManager<Timetable, String> lessonToRoomTimeslotSolverManager;

    ...
}
@RestController
@RequestMapping("/path")
public class Resource {

    @Autowired
    @Qualifier("teacherSolver")
    SolverManager<TeacherToLessonSchedule, String> teacherToLessonScheduleSolverManager;

    @Autowired
    @Qualifier("roomSolver")
    SolverManager<Timetable, String> lessonToRoomTimeslotSolverManager;

    ...
}

Conclusion

We evaluated the approach necessary to set up and use multiple solver configurations within the same application. Timefold Solver offers a simple solution for defining and utilizing different solver configurations without requiring custom logic to create and inject related managed resources.

Continue reading

  • How to speed up Timefold Solver Startup Time by 20x with native images

    Discover how to build a Spring native image and the benefits from doing so.

  • Red Hat: OptaPlanner End Of Life Notice (EOL)

    Timefold, led by former core OptaPlanner engineers, offers a seamless transition with extended support and accelerated innovation.

  • Newsletter 4: A big Speed Upgrade for Timefold - Our First Customer Story and AMA!

    Unlock unprecedented speed with Timefold's latest update! Essential for OptaPlanner and pre-1.8.0 users – upgrade now for instant gains

  • How fast is Java 22?

    Explore the performance of Java 22 against Java 21, this time also with GraalVM.

  • Continuous Planning Optimization with Pinning

    Discover how to make non-disruptive, feasible adjustments to your already in-progress plans with Timefold, ensuring real-time adaptability to unexpected changes.

  • Fast Planning Optimization with the Recommended Fit API

    Discover how Timefold's Recommended Fit API offers swift, feasible adjustments to your plans, ensuring real-time adaptability to unexpected changes.

Sign up for our newsletter

And stay up to date with announcements, the latest news, events, roadmap progress & product updates from Timefold!

We care about the protection of your data. Read our Privacy Policy.