Project#evaluate#

Scores alternative change sets against the solved baseline of a Project. Doesn't modify the project. Use it to compare options, such as which resource should take a task, before committing one with apply. Needs a solved project.

Signature#

public EvaluateResult evaluate(Vec<Candidate> candidates, EvaluateOptions options)
public EvaluateResult evaluate(Collection<Candidate> candidates, EvaluateOptions options)

Synchronous and CPU-only. Read-only, but not synchronized: serialize calls on one instance. The Collection overload copies the candidates into a Vec, so List.of(...) works.

Parameters#

Name Type Description
candidates Vec<Candidate> The change sets to score. Must not be null
options EvaluateOptions Baseline check, leveling stage, focus tasks and clearFirst. Must not be null

Behavior#

Each candidate runs on a disposable copy of the reference state (the baseline, or its cleared copy with options.clearFirst):

  1. apply the candidate's changes.
  2. solve(null, …) and measure the scheduled stage.
  3. With options.level set, level with those options and measure the leveled stage.

Every candidate starts from the reference state, never from the previous candidate. A failure inside one candidate goes into that candidate's error and doesn't fail the call. The result has scores, not dates.

Returns#

An EvaluateResult with the baseline metrics and one result per candidate, in input order.

Errors#

All exceptions are unchecked.

Error When
ProjectException with code == ProjectError.NO_BASELINE The project is unsolved, including after apply
ProjectException with code == ProjectError.STALE_BASELINE options.baselineId or any candidate's changes.baselineId doesn't name the current baseline
InputException An unknown task in focus (path focus[i]); options.level is invalid for the baseline
NullPointerException candidates or options is null

evaluateWith#

public EvaluateResult evaluateWith(
    Vec<Candidate> candidates, EvaluateOptions options, Execution execution)
public EvaluateResult evaluateWith(
    Collection<Candidate> candidates, EvaluateOptions options, Execution execution)

Same as evaluate, with a choice of Execution. Execution.sequential() is what evaluate uses. Execution.parallel(threads) scores candidates on a private fixed thread pool of min(threads, candidates.size()) workers (null threads means availableProcessors()). With one worker or fewer, candidates run on the calling thread. The pool shuts down before the call returns, and the caller blocks until every candidate is done. Peak memory is one candidate copy per worker.

The results are identical to evaluate and keep input order. A call-level error is the first one in input order, and later candidates may already have run. Errors are the same as for evaluate, plus NullPointerException for a null execution.

Example#

Compare leaving the plan as it is with moving task B to an idle resource.

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

public class EvaluateExample {
  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),
            new TaskData(Id.str("B")).withStartDate(monday).withDuration(2.0)))
        .withResources(List.of(
            new ResourceData().withId(Id.str("dev1")),
            new ResourceData().withId(Id.str("dev2"))))
        .withAssignments(List.of(
            new AssignmentData().withId(Id.str("aA")).withTask(Id.str("A"))
                .withResource(Id.str("dev1")).withValue(1.0),
            new AssignmentData().withId(Id.str("aB")).withTask(Id.str("B"))
                .withResource(Id.str("dev1")).withValue(1.0)));

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

    SchedulingChanges toDev2 = new SchedulingChanges();
    toDev2.assignments.update.add(
        new AssignmentUpdate(Id.str("aB")).withResource(Patch.of(Id.str("dev2"))));

    List<Candidate> candidates = List.of(
        new Candidate().withId(Id.str("stay")).withChanges(new SchedulingChanges()),
        new Candidate().withId(Id.str("dev2")).withChanges(toDev2));
    EvaluateResult result = project.evaluate(candidates, new EvaluateOptions());

    for (CandidateResult candidate : result.candidates) {
      System.out.println(candidate.id + ": cost " + candidate.cost); // stay: cost 2.0, dev2: cost 0.0
    }
  }
}