Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/solverforge-scoring/WIREFRAME.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ pub use api::analysis::{
// Fluent Stream API
pub use stream::{
fixed_weight, hard_weight, BiConstraintBuilder, BiConstraintStream, ConstraintFactory,
FixedWeight, GroupedConstraintBuilder, GroupedConstraintStream, HardWeight,
ConstraintWeight, FixedWeight, GroupedConstraintBuilder, GroupedConstraintStream, HardWeight,
Projection, ProjectionSink, UniConstraintBuilder, UniConstraintStream,
};

Expand Down Expand Up @@ -738,6 +738,8 @@ Construct with `new(entities)` or `with_description(entities, description)`.

**`ConstraintAnalysis<'a, Sc: Score>`** — `{ constraint_ref: &'a ConstraintRef, weight_multiplier: u32, score: Sc, matches: Vec<DetailedConstraintMatch<'a, Sc>>, is_hard: bool }`; `new(constraint_ref, weight_multiplier, score, matches, is_hard)`

**`ConstraintWeight<Args, Sc>`** — The weight bound of every `penalize`/`reward`: `score(&self, args) -> Sc` scores one match from its borrowed row tuple (`(&A,)` through `(&A, &B, &C, &D, &E)`, or key and result for grouped streams), and `is_hard(&self) -> bool` decides the constraint's hardness. Implemented for the built-in score types (fixed per match, hard when the first level is non-zero), `FixedWeight`, closures over the matched rows (soft), and `HardWeight` (hard); implement it for a custom weight type.

**`FixedWeight<Sc>` / `fixed_weight(score)`** — Public zero-erasure wrapper for custom fixed score weights. Use `penalize(fixed_weight(custom_score))` or `reward(fixed_weight(custom_score))` when `Sc` is user-defined.

**`HardWeight<W>` / `hard_weight(weight)`** — Public zero-erasure wrapper that forces hard constraint metadata while delegating scoring to the wrapped fixed or dynamic weight.
Expand Down
3 changes: 3 additions & 0 deletions crates/solverforge-scoring/public-api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@ trait director::score_director::WeightMode: pub trait WeightMode: Mode + Send +
trait director::shadow_aware::SolvableSolution: pub trait SolvableSolution: PlanningSolution { // Required methods fn descriptor() -> SolutionDescriptor; fn entity_count(solution: &Self, descriptor_index: usize) -> usize; }
director::shadow_aware::SolvableSolution: fn descriptor() -> SolutionDescriptor
director::shadow_aware::SolvableSolution: fn entity_count(solution: &Self, descriptor_index: usize) -> usize
trait stream::ConstraintWeight: pub trait ConstraintWeight<Args, Sc: Score>: Send + Sync { // Required methods fn score(&self, args: Args) -> Sc; fn is_hard(&self) -> bool; }
stream::ConstraintWeight: fn score(&self, args: Args) -> Sc
stream::ConstraintWeight: fn is_hard(&self) -> bool
trait stream::ExistenceTarget: pub trait ExistenceTarget<S, A, EA, FA, Sc: Score> where EA: CollectionExtract<S, Item = A>, FA: UniFilter<S, A>,{ type Output; // Required method fn apply( self, mode: ExistenceMode, extractor_a: EA, filter_a: FA, ) -> Self::Output; }
stream::ExistenceTarget: type Output
stream::ExistenceTarget: fn apply( self, mode: ExistenceMode, extractor_a: EA, filter_a: FA, ) -> Self::Output
Expand Down
4 changes: 2 additions & 2 deletions crates/solverforge-scoring/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ Fluent Constraint Stream API

pub use stream::{
fixed_weight, hard_weight, BiConstraintBuilder, BiConstraintStream, ConstraintFactory,
FixedWeight, GroupedConstraintBuilder, GroupedConstraintStream, HardWeight, Projection,
ProjectionSink, UniConstraintBuilder, UniConstraintStream,
ConstraintWeight, FixedWeight, GroupedConstraintBuilder, GroupedConstraintStream, HardWeight,
Projection, ProjectionSink, UniConstraintBuilder, UniConstraintStream,
};
4 changes: 1 addition & 3 deletions crates/solverforge-scoring/src/stream/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,4 @@ pub use tri_stream::{TriConstraintBuilder, TriConstraintStream};
#[doc(hidden)]
pub use unassigned::UnassignedEntity;
pub use uni_stream::{UniConstraintBuilder, UniConstraintStream};
#[doc(hidden)]
pub use weighting_support::ConstraintWeight;
pub use weighting_support::{fixed_weight, hard_weight, FixedWeight, HardWeight};
pub use weighting_support::{fixed_weight, hard_weight, ConstraintWeight, FixedWeight, HardWeight};
64 changes: 63 additions & 1 deletion crates/solverforge-scoring/src/stream/weighting_support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,72 @@ pub const fn hard_weight<W>(weight: W) -> HardWeight<W> {
HardWeight { weight }
}

#[doc(hidden)]
/// The weight a `penalize` or `reward` call scores each match with.
///
/// `Args` is the tuple of borrowed rows one match carries, `(&A,)` for a uni
/// stream up to `(&A, &B, &C, &D, &E)` for a penta stream; grouped streams
/// pass the key and the collector result. Implemented for:
///
/// - every built-in score type (`SoftScore`, `HardSoftScore`,
/// `HardMediumSoftScore`, `HardSoftDecimalScore`, `BendableScore`), scoring
/// each match with that fixed value;
/// - [`FixedWeight`], wrapping any other fixed score;
/// - closures `Fn(&A, ...) -> Sc` over the matched rows;
/// - [`HardWeight`], wrapping another weight and marking it hard.
///
/// Implement it for a custom weight type to compute a match's score from
/// state of your own.
///
/// # Examples
///
/// ```
/// use solverforge_core::score::SoftScore;
/// use solverforge_scoring::api::constraint_set::IncrementalConstraint;
/// use solverforge_scoring::stream::{
/// source, ChangeSource, ConstraintFactory, ConstraintWeight,
/// };
///
/// /// Scores each late minute past a deadline.
/// struct MinutesLate {
/// deadline: i64,
/// }
///
/// impl<'a> ConstraintWeight<(&'a i64,), SoftScore> for MinutesLate {
/// fn score(&self, (end,): (&'a i64,)) -> SoftScore {
/// SoftScore::of((*end - self.deadline).max(0))
/// }
///
/// fn is_hard(&self) -> bool {
/// false
/// }
/// }
///
/// #[derive(Clone)]
/// struct Plan {
/// ends: Vec<i64>,
/// }
///
/// fn ends(plan: &Plan) -> &[i64] {
/// plan.ends.as_slice()
/// }
///
/// let constraint = ConstraintFactory::<Plan, SoftScore>::new()
/// .for_each(source(ends as fn(&Plan) -> &[i64], ChangeSource::Descriptor(0)))
/// .penalize(MinutesLate { deadline: 10 })
/// .named("late");
///
/// let plan = Plan { ends: vec![8, 13, 15] };
/// assert_eq!(constraint.evaluate(&plan), SoftScore::of(-8));
/// assert!(!constraint.is_hard());
/// ```
pub trait ConstraintWeight<Args, Sc: Score>: Send + Sync {
/// The score one match with rows `args` contributes, before the
/// constraint's penalty or reward sign is applied.
fn score(&self, args: Args) -> Sc;

/// Whether the constraint this weight scores is hard. Built-in fixed
/// scores are hard when their first (hard) level is non-zero; closures
/// are soft unless wrapped in [`HardWeight`].
fn is_hard(&self) -> bool;
}

Expand Down
1 change: 1 addition & 0 deletions crates/solverforge/WIREFRAME.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Each module groups re-exports from one layer crate, so every type a facade signa
- `hard_weight`
- `FixedWeight`
- `HardWeight`
- `ConstraintWeight` (trait: the weight bound of `penalize`/`reward`)
- `ConstraintSet` (trait)
- `ConstraintMetadata<'a>` (borrowed constraint identity view), `ConstraintResult`
- `SharedNodeDiagnostics`, `SharedNodeId`, `SharedNodeOperation`
Expand Down
8 changes: 4 additions & 4 deletions crates/solverforge/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,10 @@ Constraint API
*/

pub use solverforge_scoring::{
fixed_weight, hard_weight, ConstraintMetadata, ConstraintResult, ConstraintSet, FixedWeight,
HardWeight, IncrementalConstraint, IncrementalUniConstraint, ListPrecedenceMakespanConstraint,
Projection, ProjectionSink, SharedNodeDiagnostics, SharedNodeId, SharedNodeOperation,
SingleConstraint,
fixed_weight, hard_weight, ConstraintMetadata, ConstraintResult, ConstraintSet,
ConstraintWeight, FixedWeight, HardWeight, IncrementalConstraint, IncrementalUniConstraint,
ListPrecedenceMakespanConstraint, Projection, ProjectionSink, SharedNodeDiagnostics,
SharedNodeId, SharedNodeOperation, SingleConstraint,
};

/* ============================================================================
Expand Down
Loading