use of org.cpsolver.studentsct.model.FreeTimeRequest in project cpsolver by UniTime.
the class StudentRequestXml method exportModel.
public static Document exportModel(Assignment<Request, Enrollment> assignment, StudentSectioningModel model) {
Document document = DocumentHelper.createDocument();
Element requestElement = document.addElement("request");
requestElement.addAttribute("campus", model.getProperties().getProperty("Data.Initiative"));
requestElement.addAttribute("year", model.getProperties().getProperty("Data.Year"));
requestElement.addAttribute("term", model.getProperties().getProperty("Data.Term"));
for (Student student : model.getStudents()) {
Element studentElement = requestElement.addElement("student");
studentElement.addAttribute("key", String.valueOf(student.getId()));
Element courseRequestsElement = studentElement.addElement("updateCourseRequests");
courseRequestsElement.addAttribute("commit", "true");
Collections.sort(student.getRequests(), new Comparator<Request>() {
@Override
public int compare(Request r1, Request r2) {
if (r1.isAlternative() != r2.isAlternative()) {
return (r1.isAlternative() ? 1 : -1);
}
return Double.compare(r1.getPriority(), r2.getPriority());
}
});
boolean hasSchedule = false;
for (Request request : student.getRequests()) {
if (assignment.getValue(request) != null)
hasSchedule = true;
if (request instanceof FreeTimeRequest) {
FreeTimeRequest ftReq = (FreeTimeRequest) request;
Element ftReqElement = courseRequestsElement.addElement("freeTime");
requestElement.addAttribute("days", ftReq.getTime().getDayHeader());
int startSlot = ftReq.getTime().getStartSlot();
int startTime = startSlot * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN;
ftReqElement.addAttribute("startTime", s2zDF.format(startTime / 60) + s2zDF.format(startTime % 60));
int endTime = startTime + ftReq.getTime().getLength() * Constants.SLOT_LENGTH_MIN - ftReq.getTime().getBreakTime();
ftReqElement.addAttribute("endTime", s2zDF.format(endTime / 60) + s2zDF.format(endTime % 60));
ftReqElement.addAttribute("length", String.valueOf(ftReq.getTime().getLength() * Constants.SLOT_LENGTH_MIN));
} else {
CourseRequest crReq = (CourseRequest) request;
Element crReqElement = courseRequestsElement.addElement("courseOffering");
Course course = crReq.getCourses().get(0);
crReqElement.addAttribute("subjectArea", course.getSubjectArea());
crReqElement.addAttribute("courseNumber", course.getCourseNumber());
crReqElement.addAttribute("waitlist", crReq.isWaitlist() ? "true" : "false");
crReqElement.addAttribute("alternative", crReq.isAlternative() ? "true" : "false");
for (int i = 1; i < crReq.getCourses().size(); i++) {
Course altCourse = crReq.getCourses().get(i);
Element altCourseElement = crReqElement.addElement("alternative");
altCourseElement.addAttribute("subjectArea", altCourse.getSubjectArea());
altCourseElement.addAttribute("courseNumber", altCourse.getCourseNumber());
}
}
}
if (hasSchedule) {
Element requestScheduleElement = studentElement.addElement("requestSchedule");
requestScheduleElement.addAttribute("type", "commit");
for (Request request : student.getRequests()) {
if (request instanceof CourseRequest) {
CourseRequest crReq = (CourseRequest) request;
Enrollment enrollment = assignment.getValue(crReq);
if (enrollment == null)
continue;
Element crReqElement = requestScheduleElement.addElement("courseOffering");
Course course = enrollment.getCourse();
crReqElement.addAttribute("subjectArea", course.getSubjectArea());
crReqElement.addAttribute("courseNumber", course.getCourseNumber());
for (Section section : enrollment.getSections()) {
Element classEl = crReqElement.addElement("class");
classEl.addAttribute("id", section.getSubpart().getInstructionalType());
classEl.addAttribute("assignmentId", String.valueOf(section.getId()));
}
}
}
}
}
return document;
}
use of org.cpsolver.studentsct.model.FreeTimeRequest in project cpsolver by UniTime.
the class InevitableStudentConflicts method check.
/** Check model for inevitable student conflicts
* @param assignment current assignment
* @return true if there are no inevitable student conflicts
**/
public boolean check(Assignment<Request, Enrollment> assignment) {
sLog.info("Checking for inevitable student conflicts...");
HashMap<TreeSet<Object>, Object[]> noGoods = new HashMap<TreeSet<Object>, Object[]>();
long studentWithoutCompleteSchedule = 0;
long inevitableRequests = 0;
double inevitableRequestWeight = 0.0;
long incompleteInevitableRequests = 0;
double incompleteInevitableRequestWeight = 0.0;
long total = 0;
Comparator<Object> simpleCmp = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
return o1.toString().compareTo(o2.toString());
}
};
HashSet<Request> requests2remove = new HashSet<Request>();
for (Student student : getModel().getStudents()) {
sLog.debug(" Checking " + (++total) + ". student " + student + "...");
if (student.isComplete(assignment)) {
for (Request request : student.getRequests()) {
if (assignment.getValue(request) == null) {
inevitableRequests++;
inevitableRequestWeight += request.getWeight();
}
}
} else {
StudentCheck ch = new StudentCheck(student.getRequests());
ch.check(assignment);
if (!ch.isBestComplete()) {
sLog.info(" Student " + student + " cannot have a complete schedule");
studentWithoutCompleteSchedule++;
}
int idx = 0;
for (Iterator<Request> f = student.getRequests().iterator(); f.hasNext(); idx++) {
Request request = f.next();
Enrollment enrollment = ch.getBestAssignment()[idx];
if (enrollment == null) {
if (!ch.isBestComplete()) {
List<Request> noGood = noGood(assignment, student, ch, idx);
sLog.info(" Request " + request + " cannot be assigned");
for (Request r : noGood) {
sLog.debug(" " + r);
Collection<Enrollment> values = null;
if (r instanceof CourseRequest) {
values = ((CourseRequest) r).getEnrollmentsSkipSameTime(assignment);
} else {
values = request.computeEnrollments(assignment);
}
for (Enrollment en : values) {
sLog.debug(" " + enrollment2string(en));
}
}
if (iDeleteInevitable) {
// noGood.lastElement()
requests2remove.add(request);
sLog.info(" -- request " + request + " picked to be removed from the model");
}
TreeSet<Object> key = new TreeSet<Object>(simpleCmp);
for (Request r : noGood) {
if (r instanceof CourseRequest) {
key.add(((CourseRequest) r).getCourses().get(0));
} else {
key.add("Free " + ((FreeTimeRequest) r).getTime().getLongName(true));
}
}
Object[] counter = noGoods.get(key);
int ir = (counter == null ? 1 : ((Integer) counter[0]).intValue() + 1);
double irw = (counter == null ? 0.0 : ((Double) counter[1]).doubleValue()) + request.getWeight();
noGoods.put(key, new Object[] { new Integer(ir), new Double(irw) });
if (ch.canAssign(request, idx)) {
incompleteInevitableRequests++;
incompleteInevitableRequestWeight += request.getWeight();
}
}
inevitableRequests++;
inevitableRequestWeight += request.getWeight();
}
}
}
}
for (Map.Entry<TreeSet<Object>, Object[]> entry : noGoods.entrySet()) {
TreeSet<Object> noGood = entry.getKey();
Object[] counter = entry.getValue();
List<CSVFile.CSVField> fields = new ArrayList<CSVFile.CSVField>();
String courseStr = "";
for (Iterator<Object> j = noGood.iterator(); j.hasNext(); ) {
Object x = j.next();
if (x instanceof Course) {
Course course = (Course) x;
courseStr += course.getName();
} else
courseStr += x.toString();
if (j.hasNext())
courseStr += ", ";
}
fields.add(new CSVFile.CSVField(courseStr));
fields.add(new CSVFile.CSVField(((Integer) counter[0]).intValue()));
fields.add(new CSVFile.CSVField(((Double) counter[1]).doubleValue()));
for (Iterator<Object> j = noGood.iterator(); j.hasNext(); ) {
Object x = j.next();
if (x instanceof Course) {
Course course = (Course) x;
List<Course> courses = new ArrayList<Course>(1);
courses.add(course);
CourseRequest cr = new CourseRequest(-1, 0, false, new Student(-1), courses, false, null);
String field = course.getName();
int idx = 0;
for (Iterator<Enrollment> k = cr.getEnrollmentsSkipSameTime(assignment).iterator(); k.hasNext(); ) {
if (idx++ > 20) {
field += "\n ...";
break;
} else {
field += "\n " + enrollment2string(k.next());
}
}
fields.add(new CSVFile.CSVField(field));
} else
fields.add(new CSVFile.CSVField(x.toString()));
}
iCSVFile.addLine(fields);
}
if (!requests2remove.isEmpty()) {
for (Request request : requests2remove) {
removeRequest(request);
}
}
sLog.info("Students that can never obtain a complete schedule: " + studentWithoutCompleteSchedule);
sLog.info("Inevitable student requests: " + inevitableRequests);
sLog.info("Inevitable student request weight: " + inevitableRequestWeight);
sLog.info("Inevitable student requests of students without a complete schedule: " + incompleteInevitableRequests);
sLog.info("Inevitable student request weight of students without a complete schedule: " + incompleteInevitableRequestWeight);
if (iCSVFile.getLines() != null)
Collections.sort(iCSVFile.getLines(), new Comparator<CSVFile.CSVLine>() {
@Override
public int compare(CSVFile.CSVLine l1, CSVFile.CSVLine l2) {
int cmp = Double.compare(l2.getField(1).toDouble(), l1.getField(1).toDouble());
if (cmp != 0)
return cmp;
return l1.getField(0).toString().compareTo(l2.getField(0).toString());
}
});
return (inevitableRequests == 0);
}
use of org.cpsolver.studentsct.model.FreeTimeRequest in project cpsolver by UniTime.
the class TimeOverlapsCounter method allConflicts.
/**
* The set of all conflicts ({@link Conflict} objects) of the given
* enrollment and other enrollments that are assigned to the same student.
* @param assignment current assignment
* @param enrollment given enrollment
* @return all conflicts of the given enrollment
*/
public Set<Conflict> allConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
Set<Conflict> ret = new HashSet<Conflict>();
if (enrollment.getRequest() instanceof FreeTimeRequest)
return ret;
for (Request request : enrollment.getStudent().getRequests()) {
if (request.equals(enrollment.getRequest()))
continue;
Enrollment other = assignment.getValue(request);
if (request instanceof FreeTimeRequest) {
FreeTimeRequest ft = (FreeTimeRequest) request;
ret.addAll(conflicts(enrollment, ft.createEnrollment()));
continue;
} else if (other != null) {
ret.addAll(conflicts(enrollment, other));
}
}
for (Unavailability unavailability : enrollment.getStudent().getUnavailabilities()) for (SctAssignment section : enrollment.getAssignments()) if (inConflict(section, unavailability))
ret.add(new Conflict(enrollment.getStudent(), share(section, unavailability), enrollment, section, unavailability.createEnrollment(), unavailability));
return ret;
}
use of org.cpsolver.studentsct.model.FreeTimeRequest in project cpsolver by UniTime.
the class Test method section.
public boolean section(Student original) {
OnlineSectioningModel model = new TestModel(iModel.getProperties());
model.setOverExpectedCriterion(iModel.getOverExpectedCriterion());
Student student = new Student(original.getId());
Hashtable<CourseRequest, Set<Section>> preferredSectionsForCourse = new Hashtable<CourseRequest, Set<Section>>();
Map<Long, Section> classTable = new HashMap<Long, Section>();
synchronized (iModel) {
for (Request request : original.getRequests()) {
Request clonnedRequest = addRequest(student, original, request, classTable, model);
Enrollment enrollment = assignment().getValue(request);
if (enrollment != null && enrollment.isCourseRequest()) {
Set<Section> sections = new HashSet<Section>();
for (Section section : enrollment.getSections()) sections.add(classTable.get(section.getId()));
preferredSectionsForCourse.put((CourseRequest) clonnedRequest, sections);
}
}
}
model.addStudent(student);
model.setDistanceConflict(new DistanceConflict(iModel.getDistanceConflict().getDistanceMetric(), model.getProperties()));
model.setTimeOverlaps(new TimeOverlapsCounter(null, model.getProperties()));
for (LinkedSections link : iModel.getLinkedSections()) {
List<Section> sections = new ArrayList<Section>();
for (Offering offering : link.getOfferings()) for (Subpart subpart : link.getSubparts(offering)) for (Section section : link.getSections(subpart)) {
Section x = classTable.get(section.getId());
if (x != null)
sections.add(x);
}
if (sections.size() >= 2)
model.addLinkedSections(link.isMustBeUsed(), sections);
}
OnlineSectioningSelection selection = null;
if (model.getProperties().getPropertyBoolean("StudentWeights.MultiCriteria", true)) {
selection = new MultiCriteriaBranchAndBoundSelection(iModel.getProperties());
} else {
selection = new SuggestionSelection(model.getProperties());
}
selection.setModel(model);
selection.setPreferredSections(preferredSectionsForCourse);
selection.setRequiredSections(new Hashtable<CourseRequest, Set<Section>>());
selection.setRequiredFreeTimes(new HashSet<FreeTimeRequest>());
long t0 = JProf.currentTimeMillis();
Assignment<Request, Enrollment> newAssignment = new AssignmentMap<Request, Enrollment>();
BranchBoundNeighbour neighbour = selection.select(newAssignment, student);
long time = JProf.currentTimeMillis() - t0;
inc("[C] CPU Time", time);
if (neighbour == null) {
inc("[F] Failure");
} else {
if (iSuggestions) {
StudentPreferencePenalties penalties = new StudentPreferencePenalties(StudentPreferencePenalties.sDistTypePreference);
double maxOverExpected = 0;
int assigned = 0;
double penalty = 0.0;
Hashtable<CourseRequest, Set<Section>> enrollments = new Hashtable<CourseRequest, Set<Section>>();
List<RequestSectionPair> pairs = new ArrayList<RequestSectionPair>();
for (int i = 0; i < neighbour.getAssignment().length; i++) {
Enrollment enrl = neighbour.getAssignment()[i];
if (enrl != null && enrl.isCourseRequest() && enrl.getAssignments() != null) {
assigned++;
for (Section section : enrl.getSections()) {
maxOverExpected += model.getOverExpected(newAssignment, section, enrl.getRequest());
pairs.add(new RequestSectionPair(enrl.variable(), section));
}
enrollments.put((CourseRequest) enrl.variable(), enrl.getSections());
penalty += penalties.getPenalty(enrl);
}
}
penalty /= assigned;
inc("[S] Initial Penalty", penalty);
double nrSuggestions = 0.0, nrAccepted = 0.0, totalSuggestions = 0.0, nrTries = 0.0;
for (int i = 0; i < pairs.size(); i++) {
RequestSectionPair pair = pairs.get(i);
SuggestionsBranchAndBound suggestionBaB = null;
if (model.getProperties().getPropertyBoolean("StudentWeights.MultiCriteria", true)) {
suggestionBaB = new MultiCriteriaBranchAndBoundSuggestions(model.getProperties(), student, newAssignment, new Hashtable<CourseRequest, Set<Section>>(), new HashSet<FreeTimeRequest>(), enrollments, pair.getRequest(), pair.getSection(), null, maxOverExpected, iModel.getProperties().getPropertyBoolean("StudentWeights.PriorityWeighting", true));
} else {
suggestionBaB = new SuggestionsBranchAndBound(model.getProperties(), student, newAssignment, new Hashtable<CourseRequest, Set<Section>>(), new HashSet<FreeTimeRequest>(), enrollments, pair.getRequest(), pair.getSection(), null, maxOverExpected);
}
long x0 = JProf.currentTimeMillis();
TreeSet<SuggestionsBranchAndBound.Suggestion> suggestions = suggestionBaB.computeSuggestions();
inc("[S] Suggestion CPU Time", JProf.currentTimeMillis() - x0);
totalSuggestions += suggestions.size();
if (!suggestions.isEmpty())
nrSuggestions += 1.0;
nrTries += 1.0;
SuggestionsBranchAndBound.Suggestion best = null;
for (SuggestionsBranchAndBound.Suggestion suggestion : suggestions) {
int a = 0;
double p = 0.0;
for (int j = 0; j < suggestion.getEnrollments().length; j++) {
Enrollment e = suggestion.getEnrollments()[j];
if (e != null && e.isCourseRequest() && e.getAssignments() != null) {
p += penalties.getPenalty(e);
a++;
}
}
p /= a;
if (a > assigned || (assigned == a && p < penalty)) {
best = suggestion;
}
}
if (best != null) {
nrAccepted += 1.0;
Enrollment[] e = best.getEnrollments();
for (int j = 0; j < e.length; j++) if (e[j] != null && e[j].getAssignments() == null)
e[j] = null;
neighbour = new BranchBoundNeighbour(student, best.getValue(), e);
assigned = 0;
penalty = 0.0;
enrollments.clear();
pairs.clear();
for (int j = 0; j < neighbour.getAssignment().length; j++) {
Enrollment enrl = neighbour.getAssignment()[j];
if (enrl != null && enrl.isCourseRequest() && enrl.getAssignments() != null) {
assigned++;
for (Section section : enrl.getSections()) pairs.add(new RequestSectionPair(enrl.variable(), section));
enrollments.put((CourseRequest) enrl.variable(), enrl.getSections());
penalty += penalties.getPenalty(enrl);
}
}
penalty /= assigned;
inc("[S] Improved Penalty", penalty);
}
}
inc("[S] Final Penalty", penalty);
if (nrSuggestions > 0) {
inc("[S] Classes with suggestion", nrSuggestions);
inc("[S] Avg. # of suggestions", totalSuggestions / nrSuggestions);
inc("[S] Suggestion acceptance rate [%]", nrAccepted / nrSuggestions);
} else {
inc("[S] Student with no suggestions available", 1.0);
}
if (!pairs.isEmpty())
inc("[S] Probability that a class has suggestions [%]", nrSuggestions / nrTries);
}
List<Enrollment> enrollments = new ArrayList<Enrollment>();
i: for (int i = 0; i < neighbour.getAssignment().length; i++) {
Request request = original.getRequests().get(i);
Enrollment clonnedEnrollment = neighbour.getAssignment()[i];
if (clonnedEnrollment != null && clonnedEnrollment.getAssignments() != null) {
if (request instanceof FreeTimeRequest) {
enrollments.add(((FreeTimeRequest) request).createEnrollment());
} else {
for (Course course : ((CourseRequest) request).getCourses()) if (course.getId() == clonnedEnrollment.getCourse().getId())
for (Config config : course.getOffering().getConfigs()) if (config.getId() == clonnedEnrollment.getConfig().getId()) {
Set<Section> assignments = new HashSet<Section>();
for (Subpart subpart : config.getSubparts()) for (Section section : subpart.getSections()) {
if (clonnedEnrollment.getSections().contains(section)) {
assignments.add(section);
}
}
Reservation reservation = null;
if (clonnedEnrollment.getReservation() != null) {
for (Reservation r : course.getOffering().getReservations()) if (r.getId() == clonnedEnrollment.getReservation().getId()) {
reservation = r;
break;
}
}
enrollments.add(new Enrollment(request, clonnedEnrollment.getPriority(), course, config, assignments, reservation));
continue i;
}
}
}
}
synchronized (iModel) {
for (Request r : original.getRequests()) {
Enrollment e = assignment().getValue(r);
r.setInitialAssignment(e);
if (e != null)
updateSpace(assignment(), e, true);
}
for (Request r : original.getRequests()) if (assignment().getValue(r) != null)
assignment().unassign(0, r);
boolean fail = false;
for (Enrollment enrl : enrollments) {
if (iModel.conflictValues(assignment(), enrl).isEmpty()) {
assignment().assign(0, enrl);
} else {
fail = true;
break;
}
}
if (fail) {
for (Request r : original.getRequests()) if (assignment().getValue(r) != null)
assignment().unassign(0, r);
for (Request r : original.getRequests()) if (r.getInitialAssignment() != null)
assignment().assign(0, r.getInitialAssignment());
for (Request r : original.getRequests()) if (assignment().getValue(r) != null)
updateSpace(assignment(), assignment().getValue(r), false);
} else {
for (Enrollment enrl : enrollments) updateSpace(assignment(), enrl, false);
}
if (fail)
return false;
}
neighbour.assign(newAssignment, 0);
int a = 0, u = 0, np = 0, zp = 0, pp = 0, cp = 0;
double over = 0;
double p = 0.0;
for (Request r : student.getRequests()) {
if (r instanceof CourseRequest) {
Enrollment e = newAssignment.getValue(r);
if (e != null) {
for (Section s : e.getSections()) {
if (s.getPenalty() < 0.0)
np++;
if (s.getPenalty() == 0.0)
zp++;
if (s.getPenalty() > 0.0)
pp++;
if (s.getLimit() > 0) {
p += s.getPenalty();
cp++;
}
over += model.getOverExpected(newAssignment, s, r);
}
a++;
} else {
u++;
}
}
}
inc("[A] Student");
if (over > 0.0)
inc("[O] Over", over);
if (a > 0)
inc("[A] Assigned", a);
if (u > 0)
inc("[A] Not Assigned", u);
inc("[V] Value", neighbour.value(newAssignment));
if (zp > 0)
inc("[P] Zero penalty", zp);
if (np > 0)
inc("[P] Negative penalty", np);
if (pp > 0)
inc("[P] Positive penalty", pp);
if (cp > 0)
inc("[P] Average penalty", p / cp);
}
inc("[T0] Time <10ms", time < 10 ? 1 : 0);
inc("[T1] Time <100ms", time < 100 ? 1 : 0);
inc("[T2] Time <250ms", time < 250 ? 1 : 0);
inc("[T3] Time <500ms", time < 500 ? 1 : 0);
inc("[T4] Time <1s", time < 1000 ? 1 : 0);
inc("[T5] Time >=1s", time >= 1000 ? 1 : 0);
return true;
}
use of org.cpsolver.studentsct.model.FreeTimeRequest 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;
for (int idx = 0; idx < current.length; idx++) {
if (isFreeTime(idx)) {
ft = true;
continue;
}
Request request = getRequest(idx);
if (idx < maxIdx) {
if (best[idx] != null) {
if (current[idx] == null)
// higher priority request assigned
return false;
if (best[idx].getPriority() < current[idx].getPriority())
// less alternative request assigned
return false;
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].getPriority() > 0)
// alternativity can be improved
return true;
} else {
if (!request.isAlternative() || alt > 0)
// priority can be improved
return true;
}
}
}
// 0.5. avoid course time overlaps & unavailability overlaps
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, section, best[idx].getRequest());
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) currentPenalties += getModel().getOverExpected(assignment, 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].getPriority() < current[idx].getPriority())
// less alternative request assigned
return false;
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].getPriority() > 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;
// 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) {
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) {
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 sections
int bestNoTime = 0, currentNoTime = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) if (section.getTime() == null)
bestNoTime++;
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) if (section.getTime() == null)
currentNoTime++;
}
}
if (currentNoTime < bestNoTime)
return true;
if (bestNoTime < currentNoTime)
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();
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();
}
}
if (currentPenalty < bestPenalty)
return true;
if (bestPenalty < currentPenalty)
return false;
return true;
}
Aggregations