use of org.cpsolver.studentsct.model.CourseRequest in project cpsolver by UniTime.
the class CourseConflictTable method createTable.
/**
* Create report
*
* @param assignment current assignment
* @param includeLastLikeStudents
* true, if last-like students should be included (i.e.,
* {@link Student#isDummy()} is true)
* @param includeRealStudents
* true, if real students should be included (i.e.,
* {@link Student#isDummy()} is false)
* @param useAmPm use 12-hour format
* @return report as comma separated text file
*/
@SuppressWarnings("unchecked")
public CSVFile createTable(Assignment<Request, Enrollment> assignment, boolean includeLastLikeStudents, boolean includeRealStudents, boolean useAmPm) {
CSVFile csv = new CSVFile();
csv.setHeader(new CSVFile.CSVField[] { new CSVFile.CSVField("UnasgnCrs"), new CSVFile.CSVField("ConflCrs"), new CSVFile.CSVField("NrStud"), new CSVFile.CSVField("StudWeight"), new CSVFile.CSVField("NoAlt"), new CSVFile.CSVField("Reason") });
HashMap<Course, HashMap<Course, Object[]>> unassignedCourseTable = new HashMap<Course, HashMap<Course, Object[]>>();
for (Request request : new ArrayList<Request>(getModel().unassignedVariables(assignment))) {
if (request.getStudent().isDummy() && !includeLastLikeStudents)
continue;
if (!request.getStudent().isDummy() && !includeRealStudents)
continue;
if (request instanceof CourseRequest) {
CourseRequest courseRequest = (CourseRequest) request;
if (courseRequest.getStudent().isComplete(assignment))
continue;
List<Enrollment> values = courseRequest.values(assignment);
SectionLimit limitConstraint = null;
for (GlobalConstraint<Request, Enrollment> c : getModel().globalConstraints()) {
if (c instanceof SectionLimit) {
limitConstraint = (SectionLimit) c;
break;
}
}
if (limitConstraint == null) {
limitConstraint = new SectionLimit(new DataProperties());
limitConstraint.setModel(getModel());
}
List<Enrollment> availableValues = new ArrayList<Enrollment>(values.size());
for (Enrollment enrollment : values) {
if (!limitConstraint.inConflict(assignment, enrollment))
availableValues.add(enrollment);
}
if (availableValues.isEmpty()) {
Course course = courseRequest.getCourses().get(0);
HashMap<Course, Object[]> conflictCourseTable = unassignedCourseTable.get(course);
if (conflictCourseTable == null) {
conflictCourseTable = new HashMap<Course, Object[]>();
unassignedCourseTable.put(course, conflictCourseTable);
}
Object[] weight = conflictCourseTable.get(course);
double nrStud = (weight == null ? 0.0 : ((Double) weight[0]).doubleValue()) + 1.0;
double nrStudW = (weight == null ? 0.0 : ((Double) weight[1]).doubleValue()) + request.getWeight();
boolean noAlt = (weight == null ? true : ((Boolean) weight[2]).booleanValue());
HashSet<String> expl = (weight == null ? new HashSet<String>() : (HashSet<String>) weight[3]);
expl.add(course.getName() + " n/a");
conflictCourseTable.put(course, new Object[] { Double.valueOf(nrStud), Double.valueOf(nrStudW), Boolean.valueOf(noAlt), expl });
}
for (Enrollment enrollment : availableValues) {
Set<Enrollment> conflicts = getModel().conflictValues(assignment, enrollment);
if (conflicts.isEmpty()) {
sLog.warn("Request " + courseRequest + " of student " + courseRequest.getStudent() + " not assigned, however, no conflicts were returned.");
assignment.assign(0, enrollment);
break;
}
Course course = null;
for (Course c : courseRequest.getCourses()) {
if (c.getOffering().equals(enrollment.getConfig().getOffering())) {
course = c;
break;
}
}
if (course == null) {
sLog.warn("Course not found for request " + courseRequest + " of student " + courseRequest.getStudent() + ".");
continue;
}
HashMap<Course, Object[]> conflictCourseTable = unassignedCourseTable.get(course);
if (conflictCourseTable == null) {
conflictCourseTable = new HashMap<Course, Object[]>();
unassignedCourseTable.put(course, conflictCourseTable);
}
for (Enrollment conflict : conflicts) {
if (conflict.variable() instanceof CourseRequest) {
CourseRequest conflictCourseRequest = (CourseRequest) conflict.variable();
Course conflictCourse = null;
for (Course c : conflictCourseRequest.getCourses()) {
if (c.getOffering().equals(conflict.getConfig().getOffering())) {
conflictCourse = c;
break;
}
}
if (conflictCourse == null) {
sLog.warn("Course not found for request " + conflictCourseRequest + " of student " + conflictCourseRequest.getStudent() + ".");
continue;
}
double weightThisConflict = request.getWeight() / availableValues.size() / conflicts.size();
double partThisConflict = 1.0 / availableValues.size() / conflicts.size();
Object[] weight = conflictCourseTable.get(conflictCourse);
double nrStud = (weight == null ? 0.0 : ((Double) weight[0]).doubleValue()) + partThisConflict;
double nrStudW = (weight == null ? 0.0 : ((Double) weight[1]).doubleValue()) + weightThisConflict;
boolean noAlt = (weight == null ? areInHardConfict(assignment, request, conflict.getRequest()) : ((Boolean) weight[2]).booleanValue());
HashSet<String> expl = (weight == null ? new HashSet<String>() : (HashSet<String>) weight[3]);
expl.addAll(explanations(assignment, enrollment, conflict, useAmPm));
conflictCourseTable.put(conflictCourse, new Object[] { Double.valueOf(nrStud), Double.valueOf(nrStudW), Boolean.valueOf(noAlt), expl });
}
}
}
}
}
for (Map.Entry<Course, HashMap<Course, Object[]>> entry : unassignedCourseTable.entrySet()) {
Course unassignedCourse = entry.getKey();
HashMap<Course, Object[]> conflictCourseTable = entry.getValue();
for (Map.Entry<Course, Object[]> entry2 : conflictCourseTable.entrySet()) {
Course conflictCourse = entry2.getKey();
Object[] weight = entry2.getValue();
HashSet<String> expl = (HashSet<String>) weight[3];
String explStr = "";
for (Iterator<String> k = new TreeSet<String>(expl).iterator(); k.hasNext(); ) explStr += k.next() + (k.hasNext() ? "\n" : "");
csv.addLine(new CSVFile.CSVField[] { new CSVFile.CSVField(unassignedCourse.getName()), new CSVFile.CSVField(conflictCourse.getName()), new CSVFile.CSVField(sDF.format(weight[0])), new CSVFile.CSVField(sDF.format(weight[1])), new CSVFile.CSVField(((Boolean) weight[2]).booleanValue() ? "Y" : "N"), new CSVFile.CSVField(explStr) });
}
}
if (csv.getLines() != null)
Collections.sort(csv.getLines(), new Comparator<CSVFile.CSVLine>() {
@Override
public int compare(CSVFile.CSVLine l1, CSVFile.CSVLine l2) {
// int cmp =
// l2.getField(3).toString().compareTo(l1.getField(3).toString());
// if (cmp!=0) return cmp;
int cmp = Double.compare(l2.getField(2).toDouble(), l1.getField(2).toDouble());
if (cmp != 0)
return cmp;
cmp = l1.getField(0).toString().compareTo(l2.getField(0).toString());
if (cmp != 0)
return cmp;
return l1.getField(1).toString().compareTo(l2.getField(1).toString());
}
});
return csv;
}
use of org.cpsolver.studentsct.model.CourseRequest in project cpsolver by UniTime.
the class MultiCriteriaBranchAndBoundSelection method canAssign.
/**
* True if the given request can be assigned
*/
public boolean canAssign(Request request, int idx) {
if (iCurrentAssignment[idx] != null)
return true;
int alt = 0;
int i = 0;
float credit = 0;
for (Iterator<Request> e = iStudent.getRequests().iterator(); e.hasNext(); i++) {
Request r = e.next();
if (r.equals(request))
credit += r.getMinCredit();
else if (iCurrentAssignment[i] != null)
credit += iCurrentAssignment[i].getCredit();
if (r.equals(request))
continue;
if (r.isAlternative()) {
if (iCurrentAssignment[i] != null || (r instanceof CourseRequest && ((CourseRequest) r).isWaitlist()))
alt--;
} else {
if (r instanceof CourseRequest && !((CourseRequest) r).isWaitlist() && iCurrentAssignment[i] == null)
alt++;
}
}
return (!request.isAlternative() || alt > 0) && (credit <= request.getStudent().getMaxCredit());
}
use of org.cpsolver.studentsct.model.CourseRequest in project cpsolver by UniTime.
the class MultiCriteriaBranchAndBoundSelection method select.
/**
* Execute branch & bound, return the best found schedule for the selected
* student.
*/
public BranchBoundNeighbour select() {
iT0 = JProf.currentTimeMillis();
iTimeoutReached = false;
iCurrentAssignment = new Enrollment[iStudent.getRequests().size()];
iBestAssignment = null;
int i = 0;
for (Request r : iStudent.getRequests()) iCurrentAssignment[i++] = iAssignment.getValue(r);
saveBest();
for (int j = 0; j < iCurrentAssignment.length; j++) iCurrentAssignment[j] = null;
iValues = new HashMap<CourseRequest, List<Enrollment>>();
backTrack(0);
iT1 = JProf.currentTimeMillis();
if (iBestAssignment == null)
return null;
return new BranchBoundNeighbour(iStudent, iComparator.getTotalWeight(iAssignment, iBestAssignment), iBestAssignment);
}
use of org.cpsolver.studentsct.model.CourseRequest in project cpsolver by UniTime.
the class OnlineSectioningCriterion method compare.
@Override
public int compare(Assignment<Request, Enrollment> assignment, Enrollment[] current, Enrollment[] best) {
if (best == null)
return -1;
// 0. best priority & alternativity ignoring free time requests
boolean ft = false;
boolean res = false;
for (int idx = 0; idx < current.length; idx++) {
if (isFreeTime(idx)) {
ft = true;
continue;
}
Request request = getRequest(idx);
if (request instanceof CourseRequest && ((CourseRequest) request).hasReservations())
res = true;
if (best[idx] != null && best[idx].getAssignments() != null) {
if (current[idx] == null || current[idx].getSections() == null)
// higher priority request assigned
return 1;
if (best[idx].getTruePriority() < current[idx].getTruePriority())
// less alternative request assigned
return 1;
if (best[idx].getTruePriority() > current[idx].getTruePriority())
// less alternative request assigned
return -1;
} else {
if (current[idx] != null && current[idx].getAssignments() != null)
// higher priority request assigned
return -1;
}
}
// 0.1. allowed, but not available sections
int bestNotAvailable = 0, currentNotAvailable = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest && best[idx].getReservation() != null && best[idx].getReservation().canAssignOverLimit()) {
for (Section section : best[idx].getSections()) {
if (section.getLimit() == 0)
bestNotAvailable++;
}
}
if (current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest && current[idx].getReservation() != null && current[idx].getReservation().canAssignOverLimit()) {
for (Section section : current[idx].getSections()) {
if (section.getLimit() == 0)
currentNotAvailable++;
}
}
}
if (bestNotAvailable > currentNotAvailable)
return -1;
if (bestNotAvailable < currentNotAvailable)
return 1;
// 0.5. avoid course time overlaps & unavailabilities
if (getModel().getStudentQuality() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getAssignments() != null && best[x].getRequest() instanceof CourseRequest)
bestTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.CourseTimeOverlap, best[x], best[idx]);
}
}
if (current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getAssignments() != null && current[x].getRequest() instanceof CourseRequest)
currentTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.CourseTimeOverlap, current[x], current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.Unavailability, best[idx]);
}
if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.Unavailability, current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return -1;
if (bestTimeOverlaps < currentTimeOverlaps)
return 1;
} else if (getModel().getTimeOverlaps() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getAssignments() != null && best[x].getRequest() instanceof CourseRequest)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]);
}
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getAssignments() != null && current[x].getRequest() instanceof CourseRequest)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]);
}
if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return -1;
if (bestTimeOverlaps < currentTimeOverlaps)
return 1;
}
// 1. minimize number of penalties
double bestPenalties = 0, currentPenalties = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
for (Section section : best[idx].getSections()) bestPenalties += getModel().getOverExpected(assignment, best, idx, section, best[idx].getRequest());
for (Section section : current[idx].getSections()) currentPenalties += getModel().getOverExpected(assignment, current, idx, section, current[idx].getRequest());
}
}
if (currentPenalties < bestPenalties)
return -1;
if (bestPenalties < currentPenalties)
return 1;
// 2. best priority & alternativity including free time requests
if (ft) {
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
if (current[idx] == null || current[idx].getSections() == null)
// higher priority request assigned
return 1;
if (best[idx].getTruePriority() < current[idx].getTruePriority())
// less alternative request assigned
return 1;
if (best[idx].getTruePriority() > current[idx].getTruePriority())
// less alternative request assigned
return -1;
} else {
if (current[idx] != null && current[idx].getAssignments() != null)
// higher priority request assigned
return -1;
}
}
}
// 3. maximize selection
int bestSelected = 0, currentSelected = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
Set<Section> preferred = getPreferredSections(best[idx].getRequest());
if (preferred != null && !preferred.isEmpty()) {
for (Section section : best[idx].getSections()) if (preferred.contains(section))
bestSelected++;
for (Section section : current[idx].getSections()) if (preferred.contains(section))
currentSelected++;
}
}
}
if (currentSelected > bestSelected)
return -1;
if (bestSelected > currentSelected)
return 1;
// 3.5 maximize preferences
double bestSelectedConfigs = 0, currentSelectedConfigs = 0;
double bestSelectedSections = 0, currentSelectedSections = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestSelectedSections += best[idx].percentSelectedSameSection();
bestSelectedConfigs += best[idx].percentSelectedSameConfig();
}
if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentSelectedSections += current[idx].percentSelectedSameSection();
currentSelectedConfigs += current[idx].percentSelectedSameConfig();
}
}
if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections)
return -1;
if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections)
return 1;
// 3.9 maximize selection with penalization for not followed reservations
if (res) {
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
if (current[idx] == null || current[idx].getSections() == null)
// higher priority request assigned
return 1;
if (best[idx].getAdjustedPriority() < current[idx].getAdjustedPriority())
// less alternative request assigned
return 1;
if (best[idx].getAdjustedPriority() > current[idx].getAdjustedPriority())
// less alternative request assigned
return -1;
} else {
if (current[idx] != null && current[idx].getAssignments() != null)
// higher priority request assigned
return -1;
}
}
}
// 3.95 avoid past sections
int bestPast = 0, currentPast = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
for (Section section : best[idx].getSections()) {
if (section.isPast())
bestPast++;
}
}
if (current[idx] != null && current[idx].getAssignments() != null) {
for (Section section : current[idx].getSections()) {
if (section.isPast())
currentPast++;
}
}
}
if (currentPast < bestPast)
return -1;
if (bestPast < currentPast)
return 1;
// 4-5. student quality
if (getModel().getStudentQuality() != null) {
double bestQuality = 0, currentQuality = 0;
for (StudentQuality.Type type : StudentQuality.Type.values()) {
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
bestQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, best[idx]);
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getAssignments() != null)
bestQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, best[x], best[idx]);
}
}
if (current[idx] != null && current[idx].getAssignments() != null) {
currentQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, current[idx]);
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getAssignments() != null)
currentQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, current[x], current[idx]);
}
}
}
}
if (currentQuality < bestQuality)
return -1;
if (bestQuality < currentQuality)
return 1;
} else {
// 4. avoid time overlaps
if (getModel().getTimeOverlaps() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getAssignments() != null)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]);
else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), best[idx]);
}
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getAssignments() != null)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]);
else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]);
}
if (current[idx] != null && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return -1;
if (bestTimeOverlaps < currentTimeOverlaps)
return 1;
}
// 5. avoid distance conflicts
if (getModel().getDistanceConflict() != null) {
int bestDistanceConf = 0, currentDistanceConf = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[idx]);
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getAssignments() != null)
bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]);
}
}
if (current[idx] != null && current[idx].getAssignments() != null) {
currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[idx]);
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getAssignments() != null)
currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], current[idx]);
}
}
}
if (currentDistanceConf < bestDistanceConf)
return -1;
if (bestDistanceConf < currentDistanceConf)
return 1;
}
}
// 6. avoid no-time and online sections (no-time first, online second)
int bestNoTime = 0, currentNoTime = 0;
int bestOnline = 0, currentOnline = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
for (Section section : best[idx].getSections()) {
if (!section.hasTime())
bestNoTime++;
if (section.isOnline())
bestOnline++;
}
for (Section section : current[idx].getSections()) {
if (!section.hasTime())
currentNoTime++;
if (section.isOnline())
currentOnline++;
}
}
}
if (currentNoTime < bestNoTime)
return -1;
if (bestNoTime < currentNoTime)
return 1;
if (currentOnline < bestOnline)
return -1;
if (bestOnline < currentOnline)
return 1;
// 7. balance sections
double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0;
int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
for (Section section : best[idx].getSections()) {
Subpart subpart = section.getSubpart();
// skip unlimited and single section subparts
if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0)
continue;
// average size
double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size();
// section is below average
if (section.getLimit() < averageSize)
bestUnavailableSize += (averageSize - section.getLimit()) / averageSize;
bestAltSectionsWithLimit++;
}
for (Section section : current[idx].getSections()) {
Subpart subpart = section.getSubpart();
// skip unlimited and single section subparts
if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0)
continue;
// average size
double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size();
// section is below average
if (section.getLimit() < averageSize)
currentUnavailableSize += (averageSize - section.getLimit()) / averageSize;
currentAltSectionsWithLimit++;
}
}
}
double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit : 0.0);
double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize / currentAltSectionsWithLimit : 0.0);
if (currentUnavailableSizeFraction < bestUnavailableSizeFraction)
return -1;
if (bestUnavailableSizeFraction < currentUnavailableSizeFraction)
return 1;
// 8. average penalty sections
double bestPenalty = 0.0, currentPenalty = 0.0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
for (Section section : best[idx].getSections()) bestPenalty += section.getPenalty() / best[idx].getSections().size();
for (Section section : current[idx].getSections()) currentPenalty += section.getPenalty() / current[idx].getSections().size();
}
}
if (currentPenalty < bestPenalty)
return -1;
if (bestPenalty < currentPenalty)
return 1;
return 0;
}
use of org.cpsolver.studentsct.model.CourseRequest in project cpsolver by UniTime.
the class OnlineSectioningCriterion method canImprove.
@Override
public boolean canImprove(Assignment<Request, Enrollment> assignment, int maxIdx, Enrollment[] current, Enrollment[] best) {
// 0. best priority & alternativity ignoring free time requests
int alt = 0;
boolean ft = false;
boolean res = false;
for (int idx = 0; idx < current.length; idx++) {
if (isFreeTime(idx)) {
ft = true;
continue;
}
Request request = getRequest(idx);
if (request instanceof CourseRequest && ((CourseRequest) request).hasReservations())
res = true;
if (idx < maxIdx) {
if (best[idx] != null) {
if (current[idx] == null)
// higher priority request assigned
return false;
if (best[idx].getTruePriority() < current[idx].getTruePriority())
// less alternative request assigned
return false;
if (best[idx].getTruePriority() > current[idx].getTruePriority())
// less alternative request assigned
return true;
if (request.isAlternative())
alt--;
} else {
if (current[idx] != null)
// higher priority request assigned
return true;
if (!request.isAlternative())
alt++;
}
} else {
if (best[idx] != null) {
if (best[idx].getTruePriority() > 0)
// alternativity can be improved
return true;
} else {
if (!request.isAlternative() || alt > 0)
// priority can be improved
return true;
}
}
}
// 0.1. allowed, but not available sections
int notAvailable = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].getRequest() instanceof CourseRequest && best[idx].getReservation() != null && best[idx].getReservation().canAssignOverLimit()) {
for (Section section : best[idx].getSections()) {
if (section.getLimit() == 0)
notAvailable++;
}
}
if (idx < maxIdx && current[idx] != null && current[idx].getAssignments() != null && current[idx].getRequest() instanceof CourseRequest && current[idx].getReservation() != null && current[idx].getReservation().canAssignOverLimit()) {
for (Section section : current[idx].getSections()) {
if (section.getLimit() == 0)
notAvailable--;
}
}
}
if (notAvailable > 0) {
return true;
}
// 0.5. avoid course time overlaps & unavailability overlaps
if (getModel().getStudentQuality() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getRequest() instanceof CourseRequest)
bestTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.CourseTimeOverlap, best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getRequest() instanceof CourseRequest)
currentTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.CourseTimeOverlap, current[x], current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.Unavailability, best[idx]);
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getStudentQuality().penalty(StudentQuality.Type.Unavailability, current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return true;
if (bestTimeOverlaps < currentTimeOverlaps)
return false;
} else if (getModel().getTimeOverlaps() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getRequest() instanceof CourseRequest)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getRequest() instanceof CourseRequest) {
for (int x = 0; x < idx; x++) {
if (current[x] != null && current[x].getRequest() instanceof CourseRequest)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]);
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return true;
if (bestTimeOverlaps < currentTimeOverlaps)
return false;
}
// 1. maximize number of penalties
double bestPenalties = 0, currentPenalties = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) bestPenalties += getModel().getOverExpected(assignment, best, idx, section, best[idx].getRequest());
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) currentPenalties += getModel().getOverExpected(assignment, current, idx, section, current[idx].getRequest());
}
}
if (currentPenalties < bestPenalties)
return true;
if (bestPenalties < currentPenalties)
return false;
// 2. best priority & alternativity including free times
if (ft) {
alt = 0;
for (int idx = 0; idx < current.length; idx++) {
Request request = getStudent().getRequests().get(idx);
if (idx < maxIdx) {
if (best[idx] != null) {
if (current[idx] == null)
// higher priority request assigned
return false;
if (best[idx].getTruePriority() < current[idx].getTruePriority())
// less alternative request assigned
return false;
if (best[idx].getTruePriority() > current[idx].getTruePriority())
// less alternative request assigned
return true;
if (request.isAlternative())
alt--;
} else {
if (current[idx] != null)
// higher priority request assigned
return true;
if (request instanceof CourseRequest && !request.isAlternative())
alt++;
}
} else {
if (best[idx] != null) {
if (best[idx].getTruePriority() > 0)
// alternativity can be improved
return true;
} else {
if (!request.isAlternative() || alt > 0)
// priority can be improved
return true;
}
}
}
}
// 3. maximize selection
int bestSelected = 0, currentSelected = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].isCourseRequest()) {
Set<Section> preferred = getPreferredSections(best[idx].getRequest());
if (preferred != null && !preferred.isEmpty()) {
for (Section section : best[idx].getSections()) if (preferred.contains(section)) {
if (idx < maxIdx)
bestSelected++;
} else if (idx >= maxIdx)
bestSelected--;
}
}
if (current[idx] != null && idx < maxIdx && current[idx].isCourseRequest()) {
Set<Section> preferred = getPreferredSections(current[idx].getRequest());
if (preferred != null && !preferred.isEmpty()) {
for (Section section : current[idx].getSections()) if (preferred.contains(section))
currentSelected++;
}
}
}
if (currentSelected > bestSelected)
return true;
if (bestSelected > currentSelected)
return false;
// 3.5 maximize preferences
double bestSelectedConfigs = 0, currentSelectedConfigs = 0;
double bestSelectedSections = 0, currentSelectedSections = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestSelectedSections += best[idx].percentSelectedSameSection();
bestSelectedConfigs += best[idx].percentSelectedSameConfig();
if (idx >= maxIdx) {
bestSelectedSections -= 1.0;
bestSelectedConfigs -= 1.0;
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentSelectedSections += current[idx].percentSelectedSameSection();
currentSelectedConfigs += current[idx].percentSelectedSameConfig();
}
}
if (0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections > 0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections)
return true;
if (0.3 * bestSelectedConfigs + 0.7 * bestSelectedSections > 0.3 * currentSelectedConfigs + 0.7 * currentSelectedSections)
return false;
// 3.9 maximize selection with penalization for not followed reservations
if (res) {
alt = 0;
for (int idx = 0; idx < current.length; idx++) {
Request request = getStudent().getRequests().get(idx);
if (idx < maxIdx) {
if (best[idx] != null) {
if (current[idx] == null)
// higher priority request assigned
return false;
if (best[idx].getAdjustedPriority() < current[idx].getAdjustedPriority())
// less alternative request assigned
return false;
if (best[idx].getAdjustedPriority() > current[idx].getAdjustedPriority())
// less alternative request assigned
return true;
if (request.isAlternative())
alt--;
} else {
if (current[idx] != null)
// higher priority request assigned
return true;
if (request instanceof CourseRequest && !request.isAlternative())
alt++;
}
} else {
if (best[idx] != null) {
if (best[idx].getTruePriority() > 0)
// alternativity can be improved
return true;
} else {
if (!request.isAlternative() || alt > 0)
// priority can be improved
return true;
}
}
}
}
// 3.95 avoid past sections
int bestPast = 0, currentPast = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null) {
for (Section section : best[idx].getSections()) {
if (section.isPast())
bestPast++;
}
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null) {
for (Section section : current[idx].getSections()) {
if (section.isPast())
currentPast++;
}
}
}
if (currentPast < bestPast)
return true;
if (bestPast < currentPast)
return false;
// 4-5. student quality
if (getModel().getStudentQuality() != null) {
double bestQuality = 0, currentQuality = 0;
for (StudentQuality.Type type : StudentQuality.Type.values()) {
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
bestQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, best[idx]);
for (int x = 0; x < idx; x++) {
if (best[x] != null)
bestQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx) {
currentQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, current[idx]);
for (int x = 0; x < idx; x++) {
if (current[x] != null)
currentQuality += iQalityWeights[type.ordinal()] * getModel().getStudentQuality().penalty(type, current[x], current[idx]);
}
}
}
}
if (currentQuality < bestQuality)
return true;
if (bestQuality < currentQuality)
return false;
} else {
// 4. avoid time overlaps
if (getModel().getTimeOverlaps() != null) {
int bestTimeOverlaps = 0, currentTimeOverlaps = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (int x = 0; x < idx; x++) {
if (best[x] != null)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(best[x], best[idx]);
else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest)
bestTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), best[idx]);
}
}
if (current[idx] != null && idx < maxIdx) {
for (int x = 0; x < idx; x++) {
if (current[x] != null)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(current[x], current[idx]);
else if (getStudent().getRequests().get(x) instanceof FreeTimeRequest)
currentTimeOverlaps += getModel().getTimeOverlaps().nrConflicts(((FreeTimeRequest) getStudent().getRequests().get(x)).createEnrollment(), current[idx]);
}
}
}
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null && best[idx].getAssignments() != null && best[idx].isCourseRequest()) {
bestTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(best[idx]);
}
if (current[idx] != null && idx < maxIdx && current[idx].getAssignments() != null && current[idx].isCourseRequest()) {
currentTimeOverlaps += getModel().getTimeOverlaps().nrNotAvailableTimeConflicts(current[idx]);
}
}
if (currentTimeOverlaps < bestTimeOverlaps)
return true;
if (bestTimeOverlaps < currentTimeOverlaps)
return false;
}
// 5. avoid distance conflicts
if (getModel().getDistanceConflict() != null) {
int bestDistanceConf = 0, currentDistanceConf = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[idx]);
for (int x = 0; x < idx; x++) {
if (best[x] != null)
bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[idx]);
}
}
if (current[idx] != null && idx < maxIdx) {
currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[idx]);
for (int x = 0; x < idx; x++) {
if (current[x] != null)
currentDistanceConf += getModel().getDistanceConflict().nrConflicts(current[x], current[idx]);
}
}
}
if (currentDistanceConf < bestDistanceConf)
return true;
if (bestDistanceConf < currentDistanceConf)
return false;
}
}
// 6. avoid no-time and online sections (no-time first, online second)
int bestNoTime = 0, currentNoTime = 0;
int bestOnline = 0, currentOnline = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) {
if (!section.hasTime())
bestNoTime++;
if (section.isOnline())
bestOnline++;
}
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) {
if (!section.hasTime())
currentNoTime++;
if (section.isOnline())
currentOnline++;
}
}
}
if (currentNoTime < bestNoTime)
return true;
if (bestNoTime < currentNoTime)
return false;
if (currentOnline < bestOnline)
return true;
if (bestOnline < currentOnline)
return false;
// 7. balance sections
double bestUnavailableSize = 0.0, currentUnavailableSize = 0.0;
int bestAltSectionsWithLimit = 0, currentAltSectionsWithLimit = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) {
Subpart subpart = section.getSubpart();
// skip unlimited and single section subparts
if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0)
continue;
// average size
double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size();
// section is below average
if (section.getLimit() < averageSize)
bestUnavailableSize += (averageSize - section.getLimit()) / averageSize;
bestAltSectionsWithLimit++;
}
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) {
Subpart subpart = section.getSubpart();
// skip unlimited and single section subparts
if (subpart.getSections().size() <= 1 || subpart.getLimit() <= 0)
continue;
// average size
double averageSize = ((double) subpart.getLimit()) / subpart.getSections().size();
// section is below average
if (section.getLimit() < averageSize)
currentUnavailableSize += (averageSize - section.getLimit()) / averageSize;
currentAltSectionsWithLimit++;
}
}
}
double bestUnavailableSizeFraction = (bestUnavailableSize > 0 ? bestUnavailableSize / bestAltSectionsWithLimit : 0.0);
double currentUnavailableSizeFraction = (currentUnavailableSize > 0 ? currentUnavailableSize / currentAltSectionsWithLimit : 0.0);
if (currentUnavailableSizeFraction < bestUnavailableSizeFraction)
return true;
if (bestUnavailableSizeFraction < currentUnavailableSizeFraction)
return false;
// 8. average penalty sections
double bestPenalty = 0.0, currentPenalty = 0.0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) bestPenalty += section.getPenalty() / best[idx].getSections().size();
if (idx >= maxIdx && best[idx].isCourseRequest())
bestPenalty -= ((CourseRequest) best[idx].getRequest()).getMinPenalty();
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) currentPenalty += section.getPenalty() / current[idx].getSections().size();
}
}
if (currentPenalty < bestPenalty)
return true;
if (bestPenalty < currentPenalty)
return false;
return true;
}
Aggregations