CandidateResult#

The score of one Candidate. Project#evaluate and Project#evaluateWith return one per candidate in EvaluateResult.candidates, in input order.

Declaration#

package org.dhtmlx.plen;

public class CandidateResult extends ValueRecord implements DeepCloneable<CandidateResult>

A result holds either an error or metrics, never both. Check error first.

Constructors#

CandidateResult()#

public CandidateResult()

Creates an instance with every field set to null. The library fills the fields on every instance it returns.

Fields#

Field Type Description
id Id The candidate's id, copied unchanged.
error RejectedOperation Set when the candidate failed: the first rejected operation of its change set, or an input error from its solve or level run. path is "" when the error has no input path. When set, scheduled, leveled and cost are null. null otherwise.
scheduled Metrics Metrics of the scheduled stage. Set on every candidate without error.
leveled LeveledMetrics Metrics of the leveled stage. Set only when EvaluateOptions.level is set and the scheduled stage has no cycles; null otherwise.
cost Double The cost of the last stage present: leveled.metrics.cost when leveled is set, else scheduled.cost. Lower is better. null when error is set.

For how cost is computed, see Metrics.cost.

Methods#

withId, withError, withScheduled, withLeveled, withCost#

public CandidateResult withId(Id value)
public CandidateResult withError(RejectedOperation value)
public CandidateResult withScheduled(Metrics value)
public CandidateResult withLeveled(LeveledMetrics value)
public CandidateResult withCost(Double value)

Each setter assigns its field and returns this instance.

Returns: this CandidateResult.

deepClone#

public CandidateResult deepClone()

Returns a deep copy: error, scheduled and leveled are copied recursively, so no list or nested object is shared with the original. null fields stay null.

Returns: a new CandidateResult.

copy#

public CandidateResult copy()

Returns a shallow copy: the new result holds the same id, error, scheduled and leveled objects.

Returns: a new CandidateResult.

equals, hashCode, toString#

public boolean equals(Object other)
public int hashCode()
public String toString()

Inherited from ValueRecord. Two results are equal when every field is equal, nested metrics and lists included. cost compares with Double.equals. toString() prints all fields, for logging.

Example#

Rank the scored candidates by cost and list the rejected ones separately.

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

public class CandidateResultExample {
  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.num(1)).withStartDate(monday).withDuration(2.0)))
        .withResources(List.of(
            new ResourceData().withId(Id.str("dev1")),
            new ResourceData().withId(Id.str("dev2"))));

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

    List<Candidate> candidates = new ArrayList<>();
    for (String dev : List.of("dev1", "dev2")) {
      SchedulingChanges changes = new SchedulingChanges();
      changes.assignments.add.add(new AssignmentData()
          .withId(Id.str("a-" + dev))
          .withTask(Id.num(1))
          .withResource(Id.str(dev))
          .withValue(1.0));
      candidates.add(new Candidate().withId(Id.str(dev)).withChanges(changes));
    }

    // Rejected: "dev9" isn't in the project.
    SchedulingChanges bad = new SchedulingChanges();
    bad.resources.remove.add(Id.str("dev9"));
    candidates.add(new Candidate().withId(Id.str("drop-dev9")).withChanges(bad));

    EvaluateResult result = project.evaluate(candidates, new EvaluateOptions());

    result.candidates.stream()
        .filter(c -> c.error == null)
        .sorted(Comparator.comparing((CandidateResult c) -> c.cost))
        .forEach(c -> System.out.println(
            c.id + ": cost " + c.cost + ", conflicts " + c.scheduled.conflicts.value));

    result.candidates.stream()
        .filter(c -> c.error != null)
        .forEach(c -> System.out.println(
            c.id + " rejected at " + c.error.path + ": " + c.error.message));
  }
}