[Canonical page](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/execution/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Execution

Selects how [`Project::evaluate_with`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#evaluate_with>) runs the candidates: one after another on the calling thread, or on parallel worker threads. The variant changes only how the work is spread. Results are identical either way, in input order. The default is `Execution::Sequential`, which is what [`Project::evaluate`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#evaluate>) uses.

## Declaration

```rust
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Execution {
    #[default]
    Sequential,
    Parallel {
        threads: Option<std::num::NonZeroUsize>,
    },
}
```

## Variants

| Variant | Description |
|---|---|
| `Sequential` | Default. Scores the candidates one by one on the calling thread. |
| `Parallel { threads }` | Scores the candidates on scoped worker threads. `threads: Some(n)` sets the number of workers; `None` uses `std::thread::available_parallelism()`. Each worker holds one candidate copy at a time. |

:::note
`Execution` compiles on every target. For the targets `evaluate_with` is available on, see [`Project::evaluate_with`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#evaluate_with>).
:::

## Example

```rust
use std::num::NonZeroUsize;
use plen_core::{
    Candidate, DateTime, EvaluateOptions, Execution, Patch, Project, ProjectData,
    ProjectOptions, SchedulingChanges, SolveOptions, TaskData, TaskUpdate,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let monday = DateTime::from_ymd_hm(2026, 8, 10, 8, 0).ok_or("invalid date")?;
    let mut task = TaskData::new(1);
    task.start_date = Some(monday);
    task.duration = Some(2.0);
    let data = ProjectData {
        tasks: vec![task],
        ..Default::default()
    };

    let mut project = Project::new(ProjectOptions::default());
    project.solve(Some(data), &SolveOptions::default())?;

    let candidates: Vec<Candidate> = [3.0, 4.0]
        .into_iter()
        .enumerate()
        .map(|(i, duration)| {
            let mut update = TaskUpdate::new(1);
            update.duration = Patch::Value(duration);
            let mut changes = SchedulingChanges::default();
            changes.tasks.update.push(update);
            Candidate { id: (i as i32).into(), changes }
        })
        .collect();

    let execution = Execution::Parallel {
        threads: NonZeroUsize::new(2),
    };
    let result = project.evaluate_with(&candidates, &EvaluateOptions::default(), execution)?;

    for candidate in &result.candidates {
        println!("candidate {}: cost {:?}", candidate.id, candidate.cost);
    }
    Ok(())
}
```

## Related

- [`Project::evaluate_with`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/project/project.md#evaluate_with>) - the call that takes it
- [`EvaluateResult`](<https://docs.dhtmlx.com/planning-engine/rust/api/core/evaluate/evaluate-result.md>) - the result, identical for every variant
- [Evaluating Alternatives](<https://docs.dhtmlx.com/planning-engine/rust/resources-and-assignment/evaluating-alternatives.md#parallel-evaluation>) - running candidates on worker threads
