Published in Blog
Use Multiple Solvers in the Same Application
Learn how to configure and inject multiple SolverManager instances in one application using Timefold Solver, tackling distinct planning problems seamlessly.
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:
-
Initially, a specific group of teachers is designated to teach the available lessons.
-
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.