Skip to content

Commit

Permalink
Course Timetabling: Same Dates Group Constraint
Browse files Browse the repository at this point in the history
- added a new group constraint SAME_DATES
  - given classes must be taught on the same dates
  - if one of the classes meets more often, the class meeting less often can only meet on the dates when the other class is meeting
  - when prohibited or (strongly) discouraged: given classes cannot be taught on the same days (there cannot be a date when both classes are meeting)
- note: unlike with the same days/weeks constraint, this constraint consider individual meeting dates of both classes
  • Loading branch information
tomas-muller committed Nov 11, 2020
1 parent 835f055 commit 0e491d2
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/org/cpsolver/coursett/constraint/GroupConstraint.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
Expand Down Expand Up @@ -950,6 +951,20 @@ public boolean isSatisfied(GroupConstraint gc, Placement plc1, Placement plc2) {
public boolean isViolated(GroupConstraint gc, Placement plc1, Placement plc2) {
return gc.isFollowingWeeksBTB(plc1, plc2, false);
}}),
/**
* Given classes must be taught on the same dates. If one of the classes meets more often, the class meeting less often can only meet on the dates when the other class is meeting.<br>
* When prohibited or (strongly) discouraged: given classes cannot be taught on the same days (there cannot be a date when both classes are meeting).<br>
* Note: unlike with the same days/weeks constraint, this constraint consider individual meeting dates of both classes.
*/
SAME_DATES("SAME_DATES", "Same Dates", new PairCheck() {
@Override
public boolean isSatisfied(GroupConstraint gc, Placement plc1, Placement plc2) {
return gc.isSameDates(plc1.getTimeLocation(), plc2.getTimeLocation());
}
@Override
public boolean isViolated(GroupConstraint gc, Placement plc1, Placement plc2) {
return gc.isDifferentDates(plc1.getTimeLocation(), plc2.getTimeLocation());
}}),
;

String iReference, iName;
Expand Down Expand Up @@ -2227,4 +2242,26 @@ else if (following) // back-to-back and following classes: must be within a week
else // back-to-back and not following: just the order
return true;
}

private boolean isDifferentDates(TimeLocation t1, TimeLocation t2) {
if (!t1.shareDays(t2) || !t1.shareWeeks(t2)) return true;
for (Enumeration<Integer> e = t1.getDates(iDayOfWeekOffset); e.hasMoreElements(); ) {
Integer date = e.nextElement();
if (t2.hasDate(date, iDayOfWeekOffset)) return false;
}
return true;
}

private boolean isSameDates(TimeLocation t1, TimeLocation t2) {
if (!t1.shareDays(t2) || !t1.shareWeeks(t2)) return false;
// t1 is meets less often
if (t1.countDates(iDayOfWeekOffset) > t2.countDates(iDayOfWeekOffset)) {
TimeLocation t = t1; t1 = t2; t2 = t;
}
for (Enumeration<Integer> e = t1.getDates(iDayOfWeekOffset); e.hasMoreElements(); ) {
Integer date = e.nextElement();
if (!t2.hasDate(date, iDayOfWeekOffset)) return false;
}
return true;
}
}
74 changes: 74 additions & 0 deletions src/org/cpsolver/coursett/model/TimeLocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -555,4 +555,78 @@ public int getFirstMeeting(int dayOfWeekOffset) {
}
return iFirstMeeting;
}

/** List dates when this time location meets.
* @return enumeration of dates of this time (indexes to the {@link TimeLocation#getWeekCode()} for matching days of the week)
**/
public IntEnumeration getDates(int dayOfWeekOffset) {
return new DateEnum(dayOfWeekOffset);
}

/**
* Check if the given time location has a particular date
* @param date a date, expressed as an index to the {@link TimeLocation#getWeekCode()}
* @param dayOfWeekOffset day of the week offset for the weeks pattern
* @return true if this time location is meeting on the given date
*/
public boolean hasDate(int date, int dayOfWeekOffset) {
if (getWeekCode().get(date)) {
int dow = (date + dayOfWeekOffset) % 7;
if ((getDayCode() & Constants.DAY_CODES[dow]) != 0) return true;
}
return false;
}

/**
* Count how many times this time location is meeting
* @param dayOfWeekOffset day of the week offset for the weeks pattern
* @return number of dates during which this time location is meeting
*/
public int countDates(int dayOfWeekOffset) {
int idx = -1;
int count = 0;
while ((idx = getWeekCode().nextSetBit(1 + idx)) >= 0) {
int dow = (idx + dayOfWeekOffset) % 7;
if ((getDayCode() & Constants.DAY_CODES[dow]) != 0) count++;
}
return count;
}

private class DateEnum implements IntEnumeration {
int dayOfWeekOffset = 0;
int nextDate = -1;
boolean hasNext = false;

private DateEnum(int dayOfWeekOffset) {
this.dayOfWeekOffset = dayOfWeekOffset;
hasNext = nextDate();
}

boolean nextDate() {
while (true) {
nextDate = getWeekCode().nextSetBit(1 + nextDate);
if (nextDate < 0) return false;
int dow = (nextDate + dayOfWeekOffset) % 7;
if ((getDayCode() & Constants.DAY_CODES[dow]) != 0) return true;
}
}

@Override
public boolean hasMoreElements() {
return hasNext;
}

@Override
public Integer nextElement() {
int ret = nextDate;
hasNext = nextDate();
return ret;
}

@Deprecated
@Override
public Integer nextInt() {
return nextElement();
}
}
}

0 comments on commit 0e491d2

Please sign in to comment.