Timefold Solver Documentation

repository·main·Indexed 23 days ago

https://github.com/timefoldai/timefold-solver

Documentation for Timefold Solver, including API stability guidelines, build instructions using Maven, and nullability management with JSpecify. It covers the differences between Community and Enterprise Editions, detailing advanced features like Score Analysis, Constraint Profiling, and Multistage moves, as well as instructions for license management, installation of enterprise artifacts, and quickstart examples for Java, Spring Boot, and Quarkus.

Tokens
117.9K
Snippets
258
Records
417
Agent score
81%

What's inside Timefold Solver

  1. Integrate Timefold Solver with Java frameworks

    main

    Timefold Solver uses plain old JavaBeans (POJOs) for input and output data (the planning problem and the best solution), making it compatible with most Java technologies.

    For streamlined development, use the first-class framework integrations:

    • Quarkus
    • Spring Boot

    For other technologies, you can use standard Java annotations on your domain POJOs to handle data loading and exposure.

  2. Compare Timefold Community vs Enterprise Edition features

    main

    Timefold Solver Enterprise is a commercial, non-open-source product that includes advanced features for scaling to large datasets and improving user trust. Key Enterprise-only features include:

    • Score Analysis & Constraint Profiling: Deep insights into how constraints affect the score.
    • Advanced Optimization Algorithms: Nearby selection, Multistage moves, and Multithreaded solving.
    • Scalability & Performance: Partitioned search and general performance improvements.
    • API Extensions: Recommendation API and Throttling best solution events.
  3. What is a MoveSelector?

    main

    A MoveSelector is a component whose primary function is to create an Iterator<Move> when requested. Optimization algorithms iterate through a subset of these moves to explore the solution space.

    Note: Move Selectors are not considered a public API. While they are supported in Timefold Solver 2.0, they will only receive critical bug fixes and no new features. Users are encouraged to migrate to the Neighborhoods API.

    <localSearch>
      <changeMoveSelector/>
      ...
    </localSearch>
  4. Introduction to Constraint Streams

    main

    Constraint Streams are a functional programming API for incremental score calculation in Timefold Solver. Unlike the standard Java Streams API, which must recalculate the entire stream from scratch when a single variable changes, Constraint Streams automatically detect changes and only recalculate the minimum necessary portion of the problem. This provides significant performance benefits for large-scale optimization problems.

    An example of a constraint using Constraint Streams:

    private Constraint doNotAssignAnn(ConstraintFactory factory) {
        return factory.forEach(Shift.class)
                .filter(Shift::isEmployeeAnn)
                .penalize(HardSoftScore.ONE_SOFT)
                .asConstraint("Don't assign Ann");
    }
        private Constraint doNotAssignAnn(ConstraintFactory factory) {
            return factory.forEach(Shift.class)
                    .filter(Shift::isEmployeeAnn)
                    .penalize(HardSoftScore.ONE_SOFT)
                    .asConstraint("Don't assign Ann");
        }
  5. What is model enrichment?

    main

    Model enrichment is the process of adding extra information to the SolverModel before the solving process begins. When a planning problem is submitted via the REST API, the raw model might be incomplete. Enrichers allow you to augment this model with derived fields, external lookups, or pre-computed values that are necessary for constraints to function correctly.

    Common use cases include:

    • External Lookups: Fetching data from databases or external services (e.g., travel times, holiday calendars, price lists).
    • Pinning Entities: Marking entities that represent work already completed or committed.
    • Pre-computing Values: Calculating expensive derived values (e.g., distance matrices, compatibility scores) once, so that constraints can access them instantly during solving.
  6. What is a Move in Timefold Solver?

    main

    A Move is a change (or set of changes) that transitions a solution from state A to state B. The resulting solution B is called a neighbor of solution A.

    Key characteristics of a Move:

    • It must not change the problem facts.
    • It must not add or remove entities.
    • It can affect multiple entities simultaneously.
    • All optimization algorithms use Moves to navigate the search space.

    Effective optimization relies on Move selection: the ability to efficiently create moves and identify the most promising subset of moves to evaluate.

  7. Overview of Construction Heuristics

    main

    A construction heuristic is used to build a reasonably good initial solution in a finite amount of time. While the solution might not always be feasible, it provides a fast starting point for metaheuristics (like Local Search) to refine.

    Construction heuristics terminate automatically, so you typically do not need to configure a specific Termination for this phase.

  8. How Value Range Providers work in Timefold Solver

    main

    In Timefold Solver, a Value Range Provider defines the set of possible values that can be assigned to a planning variable.

    To connect a planning variable to its available values, you must:

    1. Annotate the planning variable field (e.g., timeslot) with @PlanningVariable.
    2. Annotate the field containing the collection of possible values (e.g., timeslots) with @ValueRangeProvider.

    The solver matches the two by ensuring the type of the planning variable matches the type of the elements returned by the value range provider. For example, if a Lesson has a timeslot field of type Timeslot, the timeslots field must be a collection of Timeslot instances.

    Common examples include fields for rooms or timeslots in scheduling problems.

  9. Understand the structured solving request format

    main

    Solving requests use a fixed envelope containing a modelInput object and a config object.

    • modelInput: The JSON representation of your ModelInput class.
    • config: Contains run and model settings.
      • run:
        • name: The run name (auto-generated if empty).
        • maxThreadCount: Max threads for solving (defaults to 1).
        • tags: Optional tags for the run.
        • termination:
          • spentLimit: Max duration (ISO 8601 Duration).
          • unimprovedSpentLimit: Max duration without score improvement (ISO 8601 Duration). If omitted, diminished returns termination is used.
      • model: Model-specific configuration overrides.
    {
        "config": {
          "run": {
            "name": "dataset name",
            "termination": {
              "spentLimit": "PT5M",
              "unimprovedSpentLimit": "PT10S"
            },
            "maxThreadCount": 1,
            "tags": []
          },
          "model": {
            "overrides": "<model-specific configurations>"
          }
        },
        "modelInput" : "<ModelInput class as JSON>"
    }
  10. Understand Timefold Solver API Stability and Package Structure

    main

    Timefold Solver follows strict rules regarding package stability and API design to ensure backward compatibility. When building on top of the library, you should rely on specific package patterns:

    • *.api.* packages: 100% backwards compatible; breaking changes only occur in major versions.
    • *.config.* packages: 100% backwards compatible; breaking changes only occur in major versions.
    • All other packages: No stability guarantees provided.

    API Design Patterns:

    • Public APIs expose interfaces, not implementations.
    • Object creation in public APIs uses the factory pattern.
    • Public API methods return interface types rather than implementation types.
    • Implementation constructors are kept package-private to force the use of factories or builders.
    // ✅ Correct API Pattern
    package ai.timefold.solver.core.api.solver;
    public interface SolverFactory<Solution_> {
        static <Solution_> SolverFactory<Solution_> create(SolverConfig config) {
            return new DefaultSolverFactory<>(config);
        }
        Solver<Solution_> buildSolver();
    }
    
    public final class DefaultSolver<Solution_> implements Solver<Solution_> {
        DefaultSolver(SolverScope<Solution_> solverScope) { // package-private
            this.solverScope = Objects.requireNonNull(solverScope);
        }
        @Override
        public final Solution_ solve(Solution_ problem) { ... }
    }