Skip to content

Commit 9579709

Browse files
authored
Merge pull request #48 from NewBornRustacean/feat-skip-unrelated-insert-cross-bi
early return and test case to verify it
2 parents 25b4c6f + ea44bf3 commit 9579709

2 files changed

Lines changed: 87 additions & 2 deletions

File tree

crates/solverforge-scoring/src/constraint/cross_bi_incremental/incremental.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,14 @@ where
8888
let b_changed = self
8989
.b_source
9090
.assert_localizes(descriptor_index, &self.constraint_ref.name);
91+
let mut total = Sc::zero();
92+
93+
if !a_changed && !b_changed {
94+
return total;
95+
}
96+
9197
let entities_a = self.extractor_a.extract(solution);
9298
let entities_b = self.extractor_b.extract(solution);
93-
let mut total = Sc::zero();
9499
if a_changed {
95100
total = total + self.insert_a(solution, entities_a, entities_b, entity_index);
96101
}
@@ -108,6 +113,11 @@ where
108113
.b_source
109114
.assert_localizes(descriptor_index, &self.constraint_ref.name);
110115
let mut total = Sc::zero();
116+
117+
if !a_changed && !b_changed {
118+
return total;
119+
}
120+
111121
if a_changed {
112122
total = total + self.retract_a(entity_index);
113123
}

crates/solverforge-scoring/src/constraint/tests/cross_bi_incr.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
use std::sync::{
2+
atomic::{AtomicUsize, Ordering},
3+
Arc,
4+
};
5+
16
use crate::api::constraint_set::IncrementalConstraint;
27
use crate::constraint::IncrementalCrossBiConstraint;
3-
use crate::stream::collection_extract::{source, ChangeSource};
8+
use crate::stream::collection_extract::{source, ChangeSource, CollectionExtract};
49
use crate::stream::joiner::equal_bi;
510
use crate::stream::ConstraintFactory;
611
use solverforge_core::score::{Score, SoftScore};
@@ -24,6 +29,42 @@ struct Schedule {
2429
employees: Vec<Employee>,
2530
}
2631

32+
#[derive(Clone)]
33+
struct CountingShiftExtract {
34+
calls: Arc<AtomicUsize>,
35+
}
36+
37+
impl CollectionExtract<Schedule> for CountingShiftExtract {
38+
type Item = Shift;
39+
40+
fn extract<'s>(&self, schedule: &'s Schedule) -> &'s [Self::Item] {
41+
self.calls.fetch_add(1, Ordering::Relaxed);
42+
schedule.shifts.as_slice()
43+
}
44+
45+
fn change_source(&self) -> ChangeSource {
46+
ChangeSource::Descriptor(0)
47+
}
48+
}
49+
50+
#[derive(Clone)]
51+
struct CountingEmployeeExtract {
52+
calls: Arc<AtomicUsize>,
53+
}
54+
55+
impl CollectionExtract<Schedule> for CountingEmployeeExtract {
56+
type Item = Employee;
57+
58+
fn extract<'s>(&self, schedule: &'s Schedule) -> &'s [Self::Item] {
59+
self.calls.fetch_add(1, Ordering::Relaxed);
60+
schedule.employees.as_slice()
61+
}
62+
63+
fn change_source(&self) -> ChangeSource {
64+
ChangeSource::Descriptor(1)
65+
}
66+
}
67+
2768
fn create_unavailable_employee_constraint() -> impl IncrementalConstraint<Schedule, SoftScore> {
2869
IncrementalCrossBiConstraint::new(
2970
ConstraintRef::new("", "Unavailable employee"),
@@ -65,6 +106,40 @@ fn sample_schedule() -> Schedule {
65106
}
66107
}
67108

109+
#[test]
110+
fn cross_bi_unrelated_insert_skips_extractors() {
111+
let shift_extract_calls = Arc::new(AtomicUsize::new(0));
112+
let employee_extract_calls = Arc::new(AtomicUsize::new(0));
113+
let mut constraint = IncrementalCrossBiConstraint::new(
114+
ConstraintRef::new("", "Unavailable employee"),
115+
ImpactType::Penalty,
116+
CountingShiftExtract {
117+
calls: Arc::clone(&shift_extract_calls),
118+
},
119+
CountingEmployeeExtract {
120+
calls: Arc::clone(&employee_extract_calls),
121+
},
122+
|shift: &Shift| shift.employee_id,
123+
|employee: &Employee| Some(employee.id),
124+
|_schedule: &Schedule, shift: &Shift, employee: &Employee| {
125+
shift.employee_id.is_some() && employee.unavailable_days.contains(&shift.day)
126+
},
127+
|_schedule: &Schedule, _shift_idx: usize, _employee_idx: usize| SoftScore::of(1),
128+
false,
129+
);
130+
let schedule = sample_schedule();
131+
132+
assert_eq!(constraint.initialize(&schedule), SoftScore::of(-1));
133+
shift_extract_calls.store(0, Ordering::Relaxed);
134+
employee_extract_calls.store(0, Ordering::Relaxed);
135+
136+
let delta = constraint.on_insert(&schedule, 0, 2);
137+
138+
assert_eq!(delta, SoftScore::zero());
139+
assert_eq!(shift_extract_calls.load(Ordering::Relaxed), 0);
140+
assert_eq!(employee_extract_calls.load(Ordering::Relaxed), 0);
141+
}
142+
68143
#[test]
69144
fn test_cross_bi_evaluate_works_without_initialize() {
70145
let constraint = create_unavailable_employee_constraint();

0 commit comments

Comments
 (0)