[Canonical page](<https://docs.dhtmlx.com/planning-engine/java/api/project-solve/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Project#solve

Schedules a [`Project`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project.md>) and accepts the scheduled dates as its baseline. Pass a [`ProjectData`](<https://docs.dhtmlx.com/planning-engine/java/api/data/project-data.md>) to load a project, or `null` to reschedule after [`apply`](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply.md>). The other read-only calls ([`preview`](<https://docs.dhtmlx.com/planning-engine/java/api/project-preview.md>), [`evaluate`](<https://docs.dhtmlx.com/planning-engine/java/api/project-evaluate.md>), [`suggestAssignments`](<https://docs.dhtmlx.com/planning-engine/java/api/project-suggest-assignments.md>) and the rest) need a solved project.

For a one-shot run that keeps no state, use [`Engine.schedule`](<https://docs.dhtmlx.com/planning-engine/java/api/engine-schedule.md>).

## Signature

```java
public ScheduleResult solve(ProjectData data, SolveOptions options)
```

Synchronous and CPU-only. Changes the project's state. Not synchronized: serialize calls on one instance.

## Parameters

| Name | Type | Description |
|---|---|---|
| `data` | [`ProjectData`](<https://docs.dhtmlx.com/planning-engine/java/api/data/project-data.md>) | Replacement data, or `null` to schedule the current state |
| `options` | [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/java/api/project/solve-options.md>) | Subtree, analytics and reporting switches. Must not be `null` |

## Behavior

- A non-null `data` is deep-copied, validated, installed, scheduled and accepted. The previous data is discarded. Later changes to the caller's `ProjectData` object don't reach the project. [`ProjectOptions.validation`](<https://docs.dhtmlx.com/planning-engine/java/api/project/project-options.md>) decides whether invalid data is rejected or repaired.
- `null` schedules the current state when it has unsolved edits. When the project is already solved with equal `options`, `solve` returns the same cached result object without scheduling again. The cache key is the whole `SolveOptions` value, compared by value.

Tasks touched by successful [`apply`](<https://docs.dhtmlx.com/planning-engine/java/api/project-apply.md>) calls since the last solve appear in `updates` as direct updates, even when the planner leaves them where the edit put them. A cyclic project returns unchanged dates and keeps those tasks pending.

The result's `baselineId` names the accepted baseline. It changes only when the data or the dates change, so solving an unchanged state keeps it.

## Returns

The [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/java/api/results/schedule-result.md>). The project keeps it as its cached result and shares it with later calls and copies. Treat it as read-only, or call `deepClone()` before changing it.

## Errors

All exceptions are unchecked.

| Error | When |
|---|---|
| [`InputException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/input-exception.md>) | A non-null `data` fails validation. The previous state and cached result are kept. |
| [`ProjectException`](<https://docs.dhtmlx.com/planning-engine/java/api/errors/project-exception.md>) with `code == ProjectError.NEEDS_RESYNC` | `data` is `null` after an `apply` rejected an operation and no `solve(data, …)` followed |
| `NullPointerException` | `options` is `null` |

## Example

Load a project, commit an edit and reschedule it.

```java
import java.util.List;
import org.dhtmlx.plen.*;

public class SolveExample {
  public static void main(String[] args) throws Exception {
    Moment monday = Moment.fromYmdHm(2026, 8, 10, 8, 0);
    ProjectData data = new ProjectData()
        .withTasks(List.of(new TaskData(Id.str("a")).withStartDate(monday).withDuration(2.0)));

    SolveOptions options = new SolveOptions();
    Project project = new Project();
    ScheduleResult loaded = project.solve(data, options);

    // Nothing changed: the cached result comes back with the same baseline id.
    ScheduleResult again = project.solve(null, options);
    System.out.println(again.baselineId.equals(loaded.baselineId)); // true

    SchedulingChanges changes = new SchedulingChanges();
    changes.tasks.update.add(new TaskUpdate(Id.str("a")).withDuration(Patch.of(3.0)));
    project.apply(changes);

    ScheduleResult result = project.solve(null, options);
    for (Mutation update : result.updates) {
      System.out.println("task " + update.id + ": " + update.startDate + " .. " + update.endDate);
      // task a: 2026-08-10 08:00 .. 2026-08-13 08:00
    }
  }
}
```

## Related

- [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/java/api/project/solve-options.md>) - subtree, analytics and reporting switches
- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/java/api/results/schedule-result.md>) - the accepted dates it returns
- [`ProjectData`](<https://docs.dhtmlx.com/planning-engine/java/api/data/project-data.md>) - the snapshot it loads
- [`Engine.schedule`](<https://docs.dhtmlx.com/planning-engine/java/api/engine-schedule.md>) - the stateless one-shot alternative
- [Scheduling a project](<https://docs.dhtmlx.com/planning-engine/java/scheduling/scheduling-a-project.md#solving-a-project>) - loading, rescheduling and the cached result
