use of org.cpsolver.studentsct.model.Section 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;
for (int idx = 0; idx < current.length; idx++) {
if (isFreeTime(idx)) {
ft = true;
continue;
}
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].getPriority() < current[idx].getPriority())
// less alternative request assigned
return 1;
} else {
if (current[idx] != null && current[idx].getAssignments() != null)
// higher priority request assigned
return -1;
}
}
// 0.5. avoid course time overlaps & unavailabilities
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, section, best[idx].getRequest());
for (Section section : current[idx].getSections()) currentPenalties += getModel().getOverExpected(assignment, 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].getPriority() < current[idx].getPriority())
// 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;
// 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) {
for (int x = 0; x < idx; x++) {
if (best[x] != null && best[x].getAssignments() != null)
bestDistanceConf += getModel().getDistanceConflict().nrConflicts(best[x], best[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 sections
int bestNoTime = 0, currentNoTime = 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.getTime() == null)
bestNoTime++;
for (Section section : current[idx].getSections()) if (section.getTime() == null)
currentNoTime++;
}
}
if (currentNoTime < bestNoTime)
return -1;
if (bestNoTime < currentNoTime)
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();
for (Section section : current[idx].getSections()) currentPenalty += section.getPenalty();
}
}
if (currentPenalty < bestPenalty)
return -1;
if (bestPenalty < currentPenalty)
return 1;
return 0;
}
use of org.cpsolver.studentsct.model.Section in project cpsolver by UniTime.
the class ShuffleStudentsSelection method init.
@Override
public void init(Solver<Request, Enrollment> solver) {
StudentSectioningModel model = (StudentSectioningModel) solver.currentSolution().getModel();
iQueue = new LinkedList<Shuffle>();
Assignment<Request, Enrollment> assignment = solver.currentSolution().getAssignment();
// Check all request groups that have a spread < 1.0
RouletteWheelSelection<RequestGroup> groups = new RouletteWheelSelection<RequestGroup>();
for (Offering offering : model.getOfferings()) {
for (Course course : offering.getCourses()) {
for (RequestGroup group : course.getRequestGroups()) {
double spread = group.getAverageSpread(solver.currentSolution().getAssignment());
if (spread >= 1.0)
continue;
groups.add(group, 1.0 - spread);
}
}
}
// If there are some, pick one randomly (using roulette wheel selection)
if (groups.hasMoreElements()) {
RequestGroup group = groups.nextElement();
RouletteWheelSelection<Subpart> subparts = new RouletteWheelSelection<Subpart>();
for (CourseRequest cr : group.getRequests()) {
Enrollment e = assignment.getValue(cr);
if (e != null)
for (Section section : e.getSections()) if (group.getSectionSpread(assignment, section) < 1.0)
subparts.addExisting(section.getSubpart(), 1.0);
}
if (subparts.hasMoreElements()) {
// Pick a subpart that has sections with a section spread < 1.0
Subpart subpart = subparts.nextElement();
RouletteWheelSelection<Section> sections = new RouletteWheelSelection<Section>();
section: for (Section section : subpart.getSections()) {
// Only take sections that all requests can use
for (CourseRequest cr : group.getRequests()) {
boolean match = false;
for (Enrollment e : cr.values(assignment)) if (e.getSections().contains(section)) {
match = true;
break;
}
if (!match)
continue section;
}
// Take sections with conflicts with lower probability
int nrConflicts = 0;
if (!section.isAllowOverlap())
requests: for (CourseRequest cr : group.getRequests()) {
for (Request r : cr.getStudent().getRequests()) {
if (r.equals(cr))
continue;
Enrollment e = assignment.getValue(r);
if (e != null && !e.isAllowOverlap() && section.isOverlapping(e.getSections())) {
nrConflicts++;
continue requests;
}
}
}
sections.add(section, 1 + group.getRequests().size() - nrConflicts);
}
Set<Section> filter = new HashSet<Section>();
double space = 0.0;
// Pick enough sections
while (sections.hasMoreElements()) {
Section section = sections.nextElement();
if (filter.add(section)) {
if (section.getLimit() < 0)
break;
space += section.getLimit();
}
if (space >= group.getTotalWeight())
break;
}
// Add all requests that should be moved into the queue
for (CourseRequest cr : group.getRequests()) {
Shuffle shuffle = new Shuffle(group, cr, filter);
Enrollment e = assignment.getValue(cr);
if (e != null && shuffle.matchFilter(e))
continue;
iQueue.add(shuffle);
}
} else {
// No subpart -> no section filter
for (CourseRequest cr : group.getRequests()) iQueue.add(new Shuffle(group, cr, null));
}
}
// Shuffle the queue
Collections.shuffle((LinkedList<Shuffle>) iQueue);
// Initialize the backtrack selection, if needed
if (iBacktrack == null) {
try {
iBacktrack = new ShuffleBacktrackNeighbourSelection(solver.getProperties());
iBacktrack.init(solver);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
// Change progress
Progress.getInstance(solver.currentSolution().getModel()).setPhase("Shuffling students along request groups...", iQueue.size());
}
use of org.cpsolver.studentsct.model.Section in project cpsolver by UniTime.
the class ResectioningWeights method getWeight.
@Override
public double getWeight(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
double weight = super.getWeight(assignment, enrollment);
if (enrollment.isCourseRequest() && enrollment.getAssignments() != null && iLastSectionProvider != null) {
int sameChoice = 0;
int sameTime = 0;
int sameRooms = 0;
int sameName = 0;
for (Section section : enrollment.getSections()) {
if (iLastSectionProvider.sameLastChoice(section))
sameChoice++;
if (iLastSectionProvider.sameLastTime(section))
sameTime++;
if (iLastSectionProvider.sameLastRoom(section))
sameRooms++;
if (iLastSectionProvider.sameLastName(section, enrollment.getCourse()))
sameName++;
}
CourseRequest cr = (CourseRequest) enrollment.getRequest();
if (sameChoice == 0 && !cr.getSelectedChoices().isEmpty()) {
for (Section section : enrollment.getSections()) {
if (cr.isSelected(section)) {
sameChoice++;
continue;
}
}
}
double size = enrollment.getAssignments().size();
double sameChoiceFraction = (size - sameChoice) / size;
double sameTimeFraction = (size - sameTime) / size;
double sameRoomsFraction = (size - sameRooms) / size;
double sameNameFraction = (size - sameName) / size;
double base = getBaseWeight(assignment, enrollment);
weight -= sameChoiceFraction * base * iSameChoiceFactor;
weight -= sameTimeFraction * base * iSameTimeFactor;
weight -= sameRoomsFraction * base * iSameRoomsFactor;
weight -= sameNameFraction * base * iSameNameFactor;
}
return weight;
}
use of org.cpsolver.studentsct.model.Section in project cpsolver by UniTime.
the class StudentSchedulingAssistantWeights method best.
private double[] best(Assignment<Request, Enrollment> assignment, CourseRequest cr) {
double[] cached = iCache.get(cr);
if (cached != null)
return cached;
double bestTime = 0;
Double bestOverExpected = null;
Double bestAvgPenalty = null;
double bestSelected = 0.0;
for (Course course : cr.getCourses()) {
for (Config config : course.getOffering().getConfigs()) {
int size = config.getSubparts().size();
double sectionsWithTime = 0;
double overExpected = 0;
double penalty = 0;
double selectedSections = 0;
for (Subpart subpart : config.getSubparts()) {
boolean hasTime = false;
Double sectionPenalty = null;
Double sectionOverExpected = null;
boolean hasSelection = false;
for (Section section : subpart.getSections()) {
if (section.getLimit() == 0)
continue;
if (section.getTime() != null)
hasTime = true;
if (!cr.getSelectedChoices().isEmpty() && cr.isSelected(section))
hasSelection = true;
if (sectionPenalty == null || sectionPenalty > section.getPenalty())
sectionPenalty = section.getPenalty();
double oexp = getOverExpected(assignment, section, cr);
if (sectionOverExpected == null || sectionOverExpected > oexp)
sectionOverExpected = oexp;
}
if (hasTime)
sectionsWithTime++;
if (sectionPenalty != null)
penalty += sectionPenalty;
if (hasSelection)
selectedSections++;
if (sectionOverExpected != null)
overExpected += sectionOverExpected;
}
if (sectionsWithTime / size > bestTime)
bestTime = sectionsWithTime / size;
if (bestOverExpected == null || overExpected < bestOverExpected)
bestOverExpected = overExpected;
if (bestAvgPenalty == null || penalty / size < bestAvgPenalty)
bestAvgPenalty = penalty / size;
if (selectedSections / size > bestSelected)
bestSelected = selectedSections / size;
}
}
cached = new double[] { bestTime, (bestOverExpected == null ? 0.0 : bestOverExpected), (bestAvgPenalty == null ? 0.0 : bestAvgPenalty), bestSelected };
iCache.put(cr, cached);
return cached;
}
use of org.cpsolver.studentsct.model.Section in project cpsolver by UniTime.
the class StudentSchedulingAssistantWeights method getWeight.
@Override
public double getWeight(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
if (!enrollment.isCourseRequest())
return getBaseWeight(assignment, enrollment);
if (enrollment.getAssignments().isEmpty())
return 0;
double base = getBaseWeight(assignment, enrollment);
double weight = base;
int size = enrollment.getAssignments().size();
CourseRequest cr = (CourseRequest) enrollment.getRequest();
double[] best = best(assignment, cr);
double hasTime = 0;
double oexp = 0;
double penalty = 0.0;
for (Section section : enrollment.getSections()) {
if (section.getTime() != null)
hasTime++;
oexp += getOverExpected(assignment, section, cr);
penalty += section.getPenalty();
}
double noTime = best[0] - (hasTime / size);
double overExpected = oexp - best[1];
double avgPenalty = (penalty / size) - best[2];
int nrSelected = 0;
if (!cr.getSelectedChoices().isEmpty()) {
for (Section section : enrollment.getSections()) if (cr.isSelected(section))
nrSelected++;
}
double unselectedFraction = best[3] - (nrSelected / size);
double unavailableSize = 0;
double altSectionsWithLimit = 0;
for (Section section : enrollment.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)
unavailableSize += (averageSize - section.getLimit()) / averageSize;
altSectionsWithLimit++;
}
double unavailableSizeFraction = (unavailableSize > 0 ? unavailableSize / altSectionsWithLimit : 0.0);
weight -= overExpected * base * iOverExpectedFactor;
weight -= unselectedFraction * base * iSelectionFactor;
weight -= noTime * base * iNoTimeFactor;
weight -= unavailableSizeFraction * base * iAvailabilityFactor;
weight -= avgPenalty * iPenaltyFactor;
return round(weight);
}
Aggregations