Reputation: 59
I am trying to solve a specific problem in Optaplanner, and I am having trouble figuring out how to best fit it into a domain model.
My problem involves many "Group" entities, with each entity having 3 periods per day in which they can have any one of several Activities. As well, each Group has unique requirements of exactly how many of each Activity they can have in a two-week schedule. For this problem, I was thinking about having four main objects: Group, Activity, Day, and Period. What should I use as my PlanningEntity and PlanningVariable in this situation?
Upvotes: 2
Views: 93
Reputation: 21
Looks like you will need an additional class to serve as your PlanningEntity, which would be a many-to-many "join" class. Something like ActivityAssignment, which will have Group, Period, and Activity PlanningVariable fields.
Activity instances would only represent the list of possible activities. ActivityAssignment is where an activity is tied to a group and a time (or period).
The Day and Period classes are also a tiny bit more complex. You would need to have an instance of Day for each day in the schedule. If this is a repeating schedule, then Day might conceptually represent something like Monday, Week 1; Tuesday, Week 1; ...; through Friday, Week 2. If not a repeating schedule, then each Day instance would represent a calendar date. Regardless, the Day class could have three Period members: Period1, Period2, and Period3 (or you could possibly use a collection of Periods if it could grow in the future. Depending on the rule logic it could be be easier or harder to implement the rules depending if you use a collection vs. three discrete Period fields).
Period would have a bi-directional relationship with Day. Day would have methods like getPeriod1(), getPeriod2(), getPeriod3(). Period would have a method getDay(). This is how you would determine what period and day a particular ActivityAssignment belongs to, like activityAssignment.getPeriod().getDay().
To count the number of activities for a group, you could implement a drools rule or query to select all ActivityAssignments ($aa) where $aa.group.id == "this_group", and $aa.activity.id == "that_activity". If the number is greater than the max allowed for the group (perhaps something like group.getMaxActivityCount("that_activity")), then add an appropriate penalty to the scoreHolder.
EDIT: Just for completeness, you would have a @PlanningSolution annotated solution class with problem fact collections for groups, activities, and periods. You would initialize all the Day instances with 3 Period instance in a loop, but you would also add each Period instance to a separate List, which you would then set on the PlanningSolution class.
You would have a planning solution class something like this:
@PlanningSolution
public class ActivitySchedule {
@ValueRangeProvider(id = "groupRange")
private List<Group> groups;
@ValueRangeProvider(id = "periodRange")
private List<Period> periods;
@ValueRangeProvider(id = "activityRange")
private List<Activity> activities;
private HardSoftScore score;
//...
}
When initializing the solution, do something like this:
// Coded for clarity, not for efficiency ;)
for(int dayCount = 0; dayCount < 10; dayCount++){
Day day = new Day();
day.setIndex(dayCount);
//...
Period period1 = new Period(day);
day.setPeriod1(period1);
Period period2 = new Period(day);
day.setPeriod2(period1);
Period period3 = new Period(day);
day.setPeriod3(period3);
dayList.add(day);
periodList.add(period1);
periodList.add(period2);
periodList.add(period3);
}
activitySolution.setDays(dayList);
activitySolution.setPeriods(periodList);
activitySolution.setActivities(activityList);
//...
Upvotes: 2