Docs
  • Solver
  • Models
    • Field Service Routing
    • Employee Shift Scheduling
    • Pick-up and Delivery Routing
  • Platform
Try models
  • Timefold Solver SNAPSHOT
  • Running the Solver
  • Service Reference (Preview)
  • Model Enrichment
  • Edit this Page

Timefold Solver SNAPSHOT

    • Introduction
    • PlanningAI concepts
    • Getting started
      • Overview
      • Building as a service
      • Embed as a library
        • Hello World Guide
        • Quarkus Guide
        • Spring Boot Guide
    • Example use cases
      • Vehicle Routing (Guide)
      • More examples on GitHub
    • Building your model
      • Modeling planning problems
      • Domain modeling guide
      • Time patterns
    • Constraints and score
      • Score calculation
      • Understanding the score
      • Adjusting constraints at runtime
      • Load balancing and fairness
      • Performance tips and tricks
    • Running the Solver
      • Service Reference (Preview)
        • REST API
        • Model Enrichment
        • Constraint weights
        • Demo data (optional)
        • Exposing metrics (optional)
      • Use as a Library
        • Configuring Timefold Solver
        • Constraint weights
        • Quarkus integration
        • Spring Boot integration
        • Persistent storage
    • Deployment
      • Cloud architecture patterns
      • Infrastructure requirements
    • Diagnosing the Solver
      • Benchmarking
      • Solver diagnostics
    • Optimization algorithms
      • Construction heuristics
      • Local search
      • Exhaustive search
      • Custom moves
        • Neighborhoods API
        • Move Selector reference
    • Responding to change
    • FAQ
    • New and noteworthy
    • Upgrading Timefold Solver
      • Upgrading Timefold Solver: Overview
      • Upgrade from Timefold Solver 1.x to 2.x
      • Upgrading from OptaPlanner
      • Backwards compatibility
      • Migration Guides
        • Variable Listeners to Custom Shadow Variables
        • Chained planning variable to planning list variable
    • Plus/Enterprise Editions
      • Installation
      • Performance improvements
      • Score analysis
      • Recommendation API
      • Nearby selection
      • Multithreaded solving
      • Partitioned search
      • Constraint profiling
      • Multistage moves
      • Throttling best solution events

Model Enrichment

This page describes features which are only relevant when running Timefold Solver as a service.
The information on these pages may describe functionality which may be changed or even removed in a future release.

1. SolverModel enrichment

In some situations, the SolverModel must be enriched with additional information. This could be external information such as map data or smaller enhancements such as pinning entities which occurred in the past.

Enrichers usually pre-calculate fields which would otherwise be calculated in a ConstraintStream. Especially when the field depends on external information or is difficult to compute, pre-calculating can lead to much faster results.

In this example, we enrich the Timetable PlanningSolution described above by filling in the "isHoliday" field for all Timeslot objects.

Timeslot class for the School Timetabling example
  • Java

  • Kotlin

public class Timeslot {

    private LocalDateTime startTime;
    private LocalDateTime endTime;

    private boolean isHoliday;

    public void setHoliday(boolean isHoliday) {
        this.isHoliday = isHoliday;
    }

    // other Getters/Setters/Constructors excluded
}
data class Timeslot(
    val startTime: LocalDateTime? = null,
    val endTime: LocalDateTime? = null
) {
    var isHoliday: Boolean = false
}

Enrichment of the SolverModel is possible by implementing a SolverModelEnricher.

Timeslot Enricher for the School Timetabling example
  • Java

  • Kotlin

@ApplicationScoped
public class TimeslotHolidayEnricher implements SolverModelEnricher<Timetable> {

    @Override
    public Timetable enrich(Timetable solverModel) {
        for (Timeslot timeslot : solverModel.getTimeslots()) {
            boolean isHoliday = overlapsWithKnownHoliday(timeslot.getStartTime(), timeslot.getEndTime());
            timeslot.setHoliday(isHoliday);
        }

        return solverModel;
    }

    private boolean overlapsWithKnownHoliday(LocalDateTime start, LocalDateTime end) {
        // Implementation excluded; potentially call external service / database.
        return false;
    }
}
@ApplicationScoped
class TimeslotHolidayEnricher : SolverModelEnricher<Timetable> {

    override fun enrich(solverModel: Timetable): Timetable {
        for (timeslot in solverModel.timeslots) {
            timeslot.isHoliday = overlapsWithKnownHoliday(timeslot.startTime, timeslot.endTime)
        }

        return solverModel
    }

    private fun overlapsWithKnownHoliday(start: LocalDateTime?, end: LocalDateTime?): Boolean {
        // Implementation excluded; potentially call external service / database.
        return false
    }
}

Next, register a SolverModelEnrichmentDirector implementation. This class allows you to determine the order in which enrichers are executed. This might be important when 1 of your custom enrichers depends on an enricher provided by Timefold Solver.

Timetable Enrichment Director for the School Timetabling example
  • Java

  • Kotlin

@ApplicationScoped
public class TimetableEnrichmentDirector implements SolverModelEnrichmentDirector<Timetable> {

    private final TimeslotHolidayEnricher timeslotEnricher;

    @Inject
    public TimetableEnrichmentDirector(TimeslotHolidayEnricher timeslotEnricher) {
        this.timeslotEnricher = timeslotEnricher;
    }

    @Override
    public Timetable enrich(Timetable solverModel) {
        var enrichedModel = timeslotEnricher.enrich(solverModel);
        // Additional enrichers.
        return enrichedModel;
    }
}
@ApplicationScoped
class TimetableEnrichmentDirector @Inject constructor(
    private val timeslotEnricher: TimeslotHolidayEnricher
) : SolverModelEnrichmentDirector<Timetable> {

    override fun enrich(solverModel: Timetable): Timetable {
        val enrichedModel = timeslotEnricher.enrich(solverModel)
        // Additional enrichers.
        return enrichedModel
    }
}
  • © 2026 Timefold BV
  • Timefold.ai
  • Documentation
  • Changelog
  • Send feedback
  • Privacy
  • Legal
    • Light mode
    • Dark mode
    • System default