solverforge-macros 0.19.4

Derive macros for SolverForge constraint solver
Documentation
use solverforge::prelude::*;

struct Entry {
    bucket: usize,
    delta: i64,
}

struct Employee {
    id: usize,
}

struct WorkEntryProjection;

impl Projection<(usize, i64)> for WorkEntryProjection {
    type Out = Entry;
    const MAX_EMITS: usize = 1;

    fn project<Sink>(&self, work: &(usize, i64), out: &mut Sink)
    where
        Sink: ProjectionSink<Self::Out>,
    {
        out.emit(Entry {
            bucket: work.0,
            delta: work.1,
        });
    }
}

struct Plan {
    work: Vec<(usize, i64)>,
    employees: Vec<Employee>,
}

fn work(plan: &Plan) -> &[(usize, i64)] {
    plan.work.as_slice()
}

fn employees(plan: &Plan) -> &[Employee] {
    plan.employees.as_slice()
}

#[solverforge_constraints]
fn constraints() -> impl ConstraintSet<Plan, SoftScore> {
    let g = ConstraintFactory::<Plan, SoftScore>::new();
    let demand_by_bucket = g
        .for_each(work as fn(&Plan) -> &[(usize, i64)])
        .project(WorkEntryProjection)
        .group_by(|entry: &Entry| entry.bucket, sum(|entry: &Entry| entry.delta))
        .complement(
            employees as fn(&Plan) -> &[Employee],
            |employee: &Employee| employee.id,
            |_employee: &Employee| 0i64,
        );

    (
        demand_by_bucket
            .penalize(|_bucket: &usize, demand: &i64| SoftScore::of(*demand))
            .named("linear demand"),
        demand_by_bucket
            .penalize(|_bucket: &usize, demand: &i64| SoftScore::of(*demand * *demand))
            .named("squared demand"),
    )
}

fn main() {
    let mut constraints = constraints();
    let plan = Plan {
        work: vec![(0, 2), (0, 3)],
        employees: vec![Employee { id: 0 }, Employee { id: 1 }],
    };

    assert_eq!(constraints.initialize_all(&plan), SoftScore::of(-30));
}