File size: 3,223 Bytes
b7e7f16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8aa703c
 
b7e7f16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use chrono::Timelike;
use solverforge::{ConstraintSet, SolverEvent, SolverManager};
use std::collections::BTreeMap;

use super::{generate, DemoData};
use crate::domain::Plan;

// Slow end-to-end acceptance test for the published benchmark instance.

fn schedule() -> Plan {
    generate(DemoData::Large)
}

#[test]
#[ignore = "slow acceptance test for the canonical quickstart dataset"]
fn large_demo_solves_to_feasible_terminal_state() {
    static MANAGER: SolverManager<Plan> = SolverManager::new();

    let schedule = schedule();
    let (job_id, mut receiver) = MANAGER.solve(schedule).expect("job should start");
    let mut completed_score = None;

    while let Some(event) = receiver.blocking_recv() {
        match event {
            SolverEvent::Completed { solution, .. } => {
                completed_score = solution.score;
                if let Some(score) = solution.score {
                    if score.hard_score() != solverforge::HardSoftDecimalScore::ZERO {
                        let mut mismatches = BTreeMap::<(String, u32, String), usize>::new();
                        for shift in &solution.shifts {
                            let Some(employee_idx) = shift.employee_idx else {
                                continue;
                            };
                            let employee = &solution.employees[employee_idx];
                            if !employee.skills.contains(&shift.required_skill) {
                                *mismatches
                                    .entry((
                                        shift.location.clone(),
                                        shift.start.time().hour(),
                                        shift.required_skill.clone(),
                                    ))
                                    .or_default() += 1;
                            }
                        }
                        eprintln!("large demo skill mismatches: {mismatches:?}");

                        let constraints = crate::constraints::create_constraints();
                        let analyses = constraints.evaluate_detailed(&solution);
                        let hard_breakdown: Vec<_> = analyses
                            .into_iter()
                            .filter(|analysis| {
                                analysis.score.hard_score()
                                    != solverforge::HardSoftDecimalScore::ZERO
                            })
                            .map(|analysis| {
                                format!("{}={}", analysis.constraint_ref.name, analysis.score)
                            })
                            .collect();
                        eprintln!("large demo hard breakdown: {}", hard_breakdown.join(", "));
                    }
                }
                break;
            }
            SolverEvent::Failed { error, .. } => {
                panic!("large demo solve failed unexpectedly: {error}");
            }
            _ => {}
        }
    }

    let score = completed_score.expect("expected a completed score");
    assert_eq!(score.hard_score(), solverforge::HardSoftDecimalScore::ZERO);

    MANAGER.delete(job_id).expect("delete completed job");
}