SuggestResult#

The proposal returned by Project::suggest_assignments, scored against the accepted state. It holds the net change set, the steps that built it, the slots it couldn't fill or that are out of their allowed list, and the scores before and after. Commit the proposal with Project::apply.

Declaration#

#[derive(Clone, Debug)]
pub struct SuggestResult {
    pub baseline_id: String,
    pub changes: SchedulingChanges,
    pub steps: Vec<SuggestStep>,
    pub unfilled: Vec<Id>,
    pub invalid: Vec<Id>,
    pub baseline: AssignTotal,
    pub proposed: SuggestProposal,
}

With the serde feature, SuggestResult implements Serialize only. changes is written with unset fields and empty buckets left out, so the output decodes and applies as is.

Fields#

Field Type Description
baseline_id String Baseline token of the accepted state the proposal was scored against.
changes SchedulingChanges Net difference between the accepted state and the proposal. See changes below.
steps Vec<SuggestStep> Decisions that changed the working state, in the order they were made. Empty when nothing moved.
unfilled Vec<Id> Ids of targeted empty slots left empty, in visit order. Empty slots that weren't targeted aren't listed.
invalid Vec<Id> Ids of slots whose final resource isn't in their allowed list, in visit order. Includes fixed slots and is filled whatever the targets.
baseline AssignTotal Score of the reference state on the scoring stage (leveled when SuggestOptions::level is Some).
proposed SuggestProposal Score and metrics of the final working state.

With SuggestOptions::clear_first, baseline and proposed describe the cleared state, while baseline_id still names the accepted state.

changes#

  • changes.baseline_id is Some(baseline_id).
  • A filled slot becomes one assignment add {id: slot id, task, resource, value}. The new assignment takes the slot's id.
  • A repaired or rebalanced assignment becomes one update {id, resource}, however many passes moved it. An assignment that ends on its original resource produces no entry.
  • A slot filled and then moved in a later pass is one add with its final resource.
  • With clear_first, one task update {id, leveling_delay: 0, pauses: []} per cleared task comes before the assignment edits. These clears are present whenever something was cleared, also when no slot moved.
  • No leveling delays are included.

With no steps and no clears, changes carries only baseline_id.

Applying changes reproduces proposed under GapBehavior::Compress. For the commit flow and the preserve-mode caveat, see Suggesting assignments.

Example#

use plen_core::{Error, Project, SuggestResult};

// Report the proposal, then commit it.
fn commit(project: &mut Project, result: &SuggestResult) -> Result<(), Error> {
    for step in &result.steps {
        println!("{} -> {} (pass {})", step.slot, step.to_resource, step.pass);
    }
    if !result.unfilled.is_empty() {
        println!("unfilled: {:?}", result.unfilled);
    }
    if !result.invalid.is_empty() {
        println!("invalid: {:?}", result.invalid);
    }
    println!("cost {} -> {}", result.baseline.cost, result.proposed.cost);

    project.apply(&result.changes)?;
    Ok(())
}