solverforge-scoring 0.11.1

Incremental constraint scoring for SolverForge
Documentation
use super::support::*;

#[test]
fn projected_retracts_previous_outputs_before_update() {
    let mut constraint = ConstraintFactory::<Plan, SoftScore>::new()
        .for_each(source(
            work as fn(&Plan) -> &[Work],
            ChangeSource::Descriptor(0),
        ))
        .project(WorkEntryProjection)
        .group_by(
            |entry: &Entry| entry.bucket,
            sum(|entry: &Entry| entry.delta),
        )
        .penalize_with(|delta: &i64| SoftScore::of((*delta).max(0)))
        .named("demand");

    let mut plan = Plan {
        work: vec![Work {
            bucket: 0,
            demand: 5,
            enabled: true,
        }],
        capacity: Vec::new(),
    };

    let mut total = constraint.initialize(&plan);
    assert_eq!(total, SoftScore::of(-5));
    total = total + constraint.on_retract(&plan, 0, 0);
    plan.work[0].demand = 2;
    total = total + constraint.on_insert(&plan, 0, 0);

    assert_eq!(total, SoftScore::of(-2));
    assert_eq!(total, constraint.evaluate(&plan));
}

#[test]
fn projected_updates_only_the_changed_descriptor_entity() {
    PROJECTION_CALLS.store(0, Ordering::SeqCst);
    let mut constraint = ConstraintFactory::<Plan, SoftScore>::new()
        .for_each(source(
            work as fn(&Plan) -> &[Work],
            ChangeSource::Descriptor(0),
        ))
        .project(CountedProjection)
        .penalize_with(|entry: &Entry| SoftScore::of(entry.delta))
        .named("counted");

    let mut plan = Plan {
        work: vec![
            Work {
                bucket: 0,
                demand: 1,
                enabled: true,
            },
            Work {
                bucket: 0,
                demand: 2,
                enabled: true,
            },
            Work {
                bucket: 0,
                demand: 3,
                enabled: true,
            },
        ],
        capacity: vec![Capacity {
            bucket: 0,
            capacity: 1,
        }],
    };

    let mut total = constraint.initialize(&plan);
    assert_eq!(PROJECTION_CALLS.load(Ordering::SeqCst), 3);

    total = total + constraint.on_retract(&plan, 0, 1);
    plan.capacity[0].capacity = 2;
    total = total + constraint.on_insert(&plan, 0, 1);
    assert_eq!(PROJECTION_CALLS.load(Ordering::SeqCst), 3);
    assert_eq!(total, constraint.evaluate(&plan));
    PROJECTION_CALLS.store(3, Ordering::SeqCst);

    total = total + constraint.on_retract(&plan, 1, 0);
    plan.work[1].demand = 20;
    total = total + constraint.on_insert(&plan, 1, 0);
    assert_eq!(PROJECTION_CALLS.load(Ordering::SeqCst), 4);
    assert_eq!(total, constraint.evaluate(&plan));
}