pub struct BalanceConstraint<S, A, K, E, F, KF, Sc>where
Sc: Score,{ /* private fields */ }Expand description
Zero-erasure balance constraint that penalizes uneven load distribution.
This constraint:
- Groups entities by key (e.g., employee_id)
- Counts how many entities belong to each group
- Computes population standard deviation across all group counts
- Multiplies the base score by std_dev to produce the final score
The key difference from GroupedUniConstraint is that balance computes
a GLOBAL statistic, not per-group scores.
§Type Parameters
S- Solution typeA- Entity typeK- Group key typeE- Extractor function for entitiesF- Filter typeKF- Key functionSc- Score type
§Example
use solverforge_scoring::constraint::balance::BalanceConstraint;
use solverforge_scoring::api::constraint_set::IncrementalConstraint;
use solverforge_scoring::stream::filter::TrueFilter;
use solverforge_core::{ConstraintRef, ImpactType, HardSoftDecimalScore};
#[derive(Clone)]
struct Shift { employee_id: Option<usize> }
#[derive(Clone)]
struct Solution { shifts: Vec<Shift> }
// Base score of 1000 soft per unit of std_dev
let constraint = BalanceConstraint::new(
ConstraintRef::new("", "Balance workload"),
ImpactType::Penalty,
|s: &Solution| &s.shifts,
TrueFilter,
|shift: &Shift| shift.employee_id,
HardSoftDecimalScore::of_soft(1), // 1 soft per unit std_dev (scaled internally)
false,
);
let solution = Solution {
shifts: vec![
Shift { employee_id: Some(0) },
Shift { employee_id: Some(0) },
Shift { employee_id: Some(0) },
Shift { employee_id: Some(1) },
Shift { employee_id: None }, // Unassigned, filtered out
],
};
// Employee 0: 3 shifts, Employee 1: 1 shift
// Mean = 2, Variance = ((3-2)² + (1-2)²) / 2 = 1
// StdDev = 1.0, Score = -1 soft (base_score * std_dev, negated for penalty)
let score = constraint.evaluate(&solution);
assert_eq!(score, HardSoftDecimalScore::of_soft(-1));Implementations§
Source§impl<S, A, K, E, F, KF, Sc> BalanceConstraint<S, A, K, E, F, KF, Sc>
impl<S, A, K, E, F, KF, Sc> BalanceConstraint<S, A, K, E, F, KF, Sc>
Sourcepub fn new(
constraint_ref: ConstraintRef,
impact_type: ImpactType,
extractor: E,
filter: F,
key_fn: KF,
base_score: Sc,
is_hard: bool,
) -> Self
pub fn new( constraint_ref: ConstraintRef, impact_type: ImpactType, extractor: E, filter: F, key_fn: KF, base_score: Sc, is_hard: bool, ) -> Self
Creates a new zero-erasure balance constraint.
§Arguments
constraint_ref- Identifier for this constraintimpact_type- Whether to penalize or rewardextractor- Function to get entity slice from solutionfilter- Filter to select which entities to considerkey_fn- Function to extract group key (returns None to skip entity)base_score- Score per unit of standard deviationis_hard- Whether this is a hard constraint
Trait Implementations§
Source§impl<S, A, K, E, F, KF, Sc> Debug for BalanceConstraint<S, A, K, E, F, KF, Sc>where
Sc: Score,
impl<S, A, K, E, F, KF, Sc> Debug for BalanceConstraint<S, A, K, E, F, KF, Sc>where
Sc: Score,
Source§impl<S, A, K, E, F, KF, Sc> IncrementalConstraint<S, Sc> for BalanceConstraint<S, A, K, E, F, KF, Sc>
impl<S, A, K, E, F, KF, Sc> IncrementalConstraint<S, Sc> for BalanceConstraint<S, A, K, E, F, KF, Sc>
Source§fn match_count(&self, solution: &S) -> usize
fn match_count(&self, solution: &S) -> usize
Returns the number of matches for this constraint.
Source§fn initialize(&mut self, solution: &S) -> Sc
fn initialize(&mut self, solution: &S) -> Sc
Initializes internal state by inserting all entities. Read more
Source§fn on_insert(&mut self, solution: &S, entity_index: usize) -> Sc
fn on_insert(&mut self, solution: &S, entity_index: usize) -> Sc
Called when an entity is inserted or its variable changes. Read more
Source§fn on_retract(&mut self, solution: &S, entity_index: usize) -> Sc
fn on_retract(&mut self, solution: &S, entity_index: usize) -> Sc
Called when an entity is retracted or before its variable changes. Read more
Source§fn constraint_ref(&self) -> ConstraintRef
fn constraint_ref(&self) -> ConstraintRef
Returns the constraint reference (package + name). Read more
Source§fn get_matches(&self, _solution: &S) -> Vec<DetailedConstraintMatch<Sc>>
fn get_matches(&self, _solution: &S) -> Vec<DetailedConstraintMatch<Sc>>
Returns detailed matches with entity justifications. Read more
Auto Trait Implementations§
impl<S, A, K, E, F, KF, Sc> Freeze for BalanceConstraint<S, A, K, E, F, KF, Sc>
impl<S, A, K, E, F, KF, Sc> RefUnwindSafe for BalanceConstraint<S, A, K, E, F, KF, Sc>where
E: RefUnwindSafe,
F: RefUnwindSafe,
KF: RefUnwindSafe,
Sc: RefUnwindSafe,
S: RefUnwindSafe,
A: RefUnwindSafe,
K: RefUnwindSafe,
impl<S, A, K, E, F, KF, Sc> Send for BalanceConstraint<S, A, K, E, F, KF, Sc>
impl<S, A, K, E, F, KF, Sc> Sync for BalanceConstraint<S, A, K, E, F, KF, Sc>
impl<S, A, K, E, F, KF, Sc> Unpin for BalanceConstraint<S, A, K, E, F, KF, Sc>
impl<S, A, K, E, F, KF, Sc> UnwindSafe for BalanceConstraint<S, A, K, E, F, KF, Sc>where
E: UnwindSafe,
F: UnwindSafe,
KF: UnwindSafe,
Sc: UnwindSafe,
K: UnwindSafe,
S: UnwindSafe,
A: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more