3333import static ai .timefold .solver .core .api .score .stream .ConstraintCollectors .countBi ;
3434import static ai .timefold .solver .core .api .score .stream .ConstraintCollectors .max ;
3535import static ai .timefold .solver .core .api .score .stream .ConstraintCollectors .min ;
36+ import static ai .timefold .solver .core .api .score .stream .Joiners .containedIn ;
37+ import static ai .timefold .solver .core .api .score .stream .Joiners .containing ;
3638import static ai .timefold .solver .core .api .score .stream .Joiners .equal ;
3739import static ai .timefold .solver .core .api .score .stream .Joiners .filtering ;
3840import static ai .timefold .solver .core .api .score .stream .Joiners .greaterThan ;
41+ import static ai .timefold .solver .core .api .score .stream .Joiners .intersecting ;
3942import static ai .timefold .solver .core .api .score .stream .Joiners .lessThan ;
4043import static ai .timefold .solver .core .api .score .stream .Joiners .overlapping ;
4144
@@ -117,8 +120,8 @@ Constraint speakerUnavailableTimeslot(ConstraintFactory factory) {
117120 return factory .forEachIncludingUnassigned (Talk .class )
118121 .filter (talk -> talk .getTimeslot () != null )
119122 .join (Speaker .class ,
120- filtering (( talk , speaker ) -> talk . hasSpeaker ( speaker )
121- && speaker . getUnavailableTimeslotSet (). contains ( talk . getTimeslot ()) ))
123+ containing ( Talk :: getSpeakerList , speaker -> speaker ),
124+ containedIn ( Talk :: getTimeslot , Speaker :: getUnavailableTimeslotSet ))
122125 .penalize (HardSoftScore .ofHard (100 ), (talk , speaker ) -> talk .getDurationInMinutes ())
123126 .asConstraint (SPEAKER_UNAVAILABLE_TIMESLOT );
124127 }
@@ -127,7 +130,8 @@ Constraint speakerConflict(ConstraintFactory factory) {
127130 return factory .forEachUniquePair (Talk .class ,
128131 overlapping (t -> t .getTimeslot ().getStartDateTime (), t -> t .getTimeslot ().getEndDateTime ()))
129132 .join (Speaker .class ,
130- filtering ((talk1 , talk2 , speaker ) -> talk1 .hasSpeaker (speaker ) && talk2 .hasSpeaker (speaker )))
133+ containing ((talk1 , talk2 ) -> talk1 .getSpeakerList (), speaker -> speaker ),
134+ containing ((talk1 , talk2 ) -> talk2 .getSpeakerList (), speaker -> speaker ))
131135 .penalize (HardSoftScore .ofHard (10 ), (talk1 , talk2 , speaker ) -> talk2 .overlappingDurationInMinutes (talk1 ))
132136 .asConstraint (SPEAKER_CONFLICT );
133137 }
@@ -136,18 +140,18 @@ Constraint talkPrerequisiteTalks(ConstraintFactory factory) {
136140 return factory .forEach (Talk .class )
137141 .join (Talk .class ,
138142 greaterThan (t -> t .getTimeslot ().getEndDateTime (), t -> t .getTimeslot ().getStartDateTime ()),
139- filtering (( talk1 , talk2 ) -> talk2 . getPrerequisiteTalkSet (). contains ( talk1 ) ))
143+ containedIn ( talk -> talk , Talk :: getPrerequisiteTalkSet ))
140144 .penalize (HardSoftScore .ofHard (10 ), Talk ::combinedDurationInMinutes )
141145 .asConstraint (TALK_PREREQUISITE_TALKS );
142146 }
143147
144148 Constraint talkMutuallyExclusiveTalksTags (ConstraintFactory factory ) {
145149 return factory .forEachUniquePair (Talk .class ,
146- overlapping (t -> t .getTimeslot ().getStartDateTime (), t -> t .getTimeslot ().getEndDateTime ()))
147- .expand ((talk1 , talk2 ) -> talk2 .overlappingMutuallyExclusiveTalksTagCount (talk1 ))
148- .filter ((talk1 , talk2 , overlappingTagCount ) -> overlappingTagCount > 0 )
150+ overlapping (t -> t .getTimeslot ().getStartDateTime (), t -> t .getTimeslot ().getEndDateTime ()),
151+ intersecting (Talk ::getMutuallyExclusiveTalksTagSet ))
149152 .penalize (HardSoftScore .ofHard (1 ),
150- (talk1 , talk2 , overlappingTagCount ) -> overlappingTagCount * talk1 .overlappingDurationInMinutes (talk2 ))
153+ (talk1 , talk2 ) -> talk2 .overlappingMutuallyExclusiveTalksTagCount (talk1 )
154+ * talk1 .overlappingDurationInMinutes (talk2 ))
151155 .asConstraint (TALK_MUTUALLY_EXCLUSIVE_TALKS_TAGS );
152156 }
153157
@@ -231,21 +235,19 @@ Constraint themeTrackConflict(ConstraintFactory factory) {
231235 Constraint themeTrackRoomStability (ConstraintFactory factory ) {
232236 return factory .forEachUniquePair (Talk .class ,
233237 equal (talk -> talk .getTimeslot ().getStartDateTime ().toLocalDate ()),
238+ intersecting (Talk ::getThemeTrackTagSet ),
234239 filtering ((talk1 , talk2 ) -> !talk1 .getRoom ().equals (talk2 .getRoom ())))
235- .expand ((talk1 , talk2 ) -> talk2 .overlappingThemeTrackCount (talk1 ))
236240 .penalize (HardSoftScore .ofSoft (10 ),
237- (talk1 , talk2 , overlappingTrackCount ) -> overlappingTrackCount * talk1 .combinedDurationInMinutes (talk2 ))
241+ (talk1 , talk2 ) -> talk2 . overlappingThemeTrackCount ( talk1 ) * talk1 .combinedDurationInMinutes (talk2 ))
238242 .asConstraint (THEME_TRACK_ROOM_STABILITY );
239243 }
240244
241245 Constraint sectorConflict (ConstraintFactory factory ) {
242246 return factory .forEachUniquePair (Talk .class ,
243- overlapping (t -> t .getTimeslot ().getStartDateTime (), t -> t .getTimeslot ().getEndDateTime ()))
244- .expand ((talk1 , talk2 ) -> talk2 .overlappingSectorCount (talk1 ))
245- .filter ((talk1 , talk2 , overlappingSectorCount ) -> overlappingSectorCount > 0 )
247+ overlapping (t -> t .getTimeslot ().getStartDateTime (), t -> t .getTimeslot ().getEndDateTime ()),
248+ intersecting (Talk ::getSectorTagSet ))
246249 .penalize (HardSoftScore .ofSoft (10 ),
247- (talk1 , talk2 , overlappingSectorCount ) -> overlappingSectorCount
248- * talk1 .overlappingDurationInMinutes (talk2 ))
250+ (talk1 , talk2 ) -> talk2 .overlappingSectorCount (talk1 ) * talk1 .overlappingDurationInMinutes (talk2 ))
249251 .asConstraint (SECTOR_CONFLICT );
250252 }
251253
@@ -395,7 +397,7 @@ Constraint talkUndesiredRoomTags(ConstraintFactory factory) {
395397 Constraint speakerMakespan (ConstraintFactory factory ) {
396398 return factory .forEach (Speaker .class )
397399 .join (Talk .class ,
398- filtering (( speaker , talk ) -> talk . hasSpeaker ( speaker ) ))
400+ containedIn ( speaker -> speaker , Talk :: getSpeakerList ))
399401 .groupBy ((speaker , talk ) -> speaker ,
400402 compose (
401403 min ((Speaker speaker , Talk talk ) -> talk , talk -> talk .getTimeslot ().getStartDateTime ()),
0 commit comments