use of org.cpsolver.studentsct.StudentSectioningModel in project cpsolver by UniTime.
the class RandomizedBacktrackNeighbourSelection method values.
/**
* List of values of a variable.
* {@link CourseRequest#computeRandomEnrollments(Assignment, int)} with the provided
* limit is used for a {@link CourseRequest}.
*/
@Override
protected Iterator<Enrollment> values(BacktrackNeighbourSelection<Request, Enrollment>.BacktrackNeighbourSelectionContext<Request, Enrollment> context, Request variable) {
if (variable instanceof CourseRequest) {
final CourseRequest request = (CourseRequest) variable;
final StudentSectioningModel model = (StudentSectioningModel) context.getModel();
final Assignment<Request, Enrollment> assignment = context.getAssignment();
List<Enrollment> values = (iMaxValues > 0 ? request.computeRandomEnrollments(assignment, iMaxValues) : request.computeEnrollments(assignment));
Collections.sort(values, new Comparator<Enrollment>() {
private HashMap<Enrollment, Double> iValues = new HashMap<Enrollment, Double>();
private Double value(Enrollment e) {
Double value = iValues.get(e);
if (value == null) {
value = model.getStudentWeights().getWeight(assignment, e, (model.getDistanceConflict() == null ? null : model.getDistanceConflict().conflicts(e)), (model.getTimeOverlaps() == null ? null : model.getTimeOverlaps().conflicts(e)));
iValues.put(e, value);
}
return value;
}
@Override
public int compare(Enrollment e1, Enrollment e2) {
if (e1.equals(assignment.getValue(request)))
return -1;
if (e2.equals(assignment.getValue(request)))
return 1;
Double v1 = value(e1), v2 = value(e2);
return v1.equals(v2) ? e1.compareTo(assignment, e2) : v2.compareTo(v1);
}
});
return values.iterator();
} else {
return variable.computeEnrollments(context.getAssignment()).iterator();
}
}
use of org.cpsolver.studentsct.StudentSectioningModel 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.StudentSectioningModel in project cpsolver by UniTime.
the class CourseRequest method computeEnrollments.
/**
* Recursive computation of enrollments
*
* @param enrollments
* list of enrollments to be returned
* @param priority
* zero for the course, one for the first alternative, two for the second alternative
* @param penalty
* penalty of the selected sections
* @param course
* selected course
* @param config
* selected configuration
* @param sections
* sections selected so far
* @param idx
* index of the subparts (a section of 0..idx-1 subparts has been
* already selected)
* @param availableOnly
* only use available sections
* @param skipSameTime
* for each possible times, pick only one section
* @param selectedOnly
* select only sections that are selected (
* {@link CourseRequest#isSelected(Section)} is true)
* @param random
* pick sections in a random order (useful when limit is used)
* @param limit
* when above zero, limit the number of selected enrollments to
* this limit
* @param ignoreDisabled
* are sections that are disabled for student scheduling allowed to be used
* @param reservations
* list of applicable reservations
*/
private void computeEnrollments(Assignment<Request, Enrollment> assignment, Collection<Enrollment> enrollments, int priority, double penalty, Course course, Config config, HashSet<Section> sections, int idx, boolean availableOnly, boolean skipSameTime, boolean selectedOnly, boolean random, int limit) {
if (limit > 0 && enrollments.size() >= limit)
return;
if (idx == 0) {
// run only once for each configuration
if (isNotAllowed(course, config))
return;
boolean canOverLimit = false;
if (availableOnly) {
for (Reservation r : getReservations(course)) {
if (!r.canBatchAssignOverLimit())
continue;
if (r.neverIncluded())
continue;
if (!r.getConfigs().isEmpty() && !r.getConfigs().contains(config))
continue;
if (r.getReservedAvailableSpace(assignment, this) < getWeight())
continue;
canOverLimit = true;
break;
}
}
if (!canOverLimit) {
if (availableOnly && config.getLimit() >= 0 && ConfigLimit.getEnrollmentWeight(assignment, config, this) > config.getLimit())
return;
if (availableOnly && course.getLimit() >= 0 && CourseLimit.getEnrollmentWeight(assignment, course, this) > course.getLimit())
return;
if (config.getOffering().hasReservations()) {
boolean hasReservation = false, hasConfigReservation = false, reservationMustBeUsed = false;
for (Reservation r : getReservations(course)) {
if (r.mustBeUsed())
reservationMustBeUsed = true;
if (availableOnly && r.getReservedAvailableSpace(assignment, this) < getWeight())
continue;
if (r.neverIncluded()) {
} else if (r.getConfigs().isEmpty()) {
hasReservation = true;
} else if (r.getConfigs().contains(config)) {
hasReservation = true;
hasConfigReservation = true;
}
}
if (!hasConfigReservation && config.getTotalUnreservedSpace() < getWeight())
return;
if (!hasReservation && config.getOffering().getTotalUnreservedSpace() < getWeight())
return;
if (availableOnly && !hasReservation && config.getOffering().getUnreservedSpace(assignment, this) < getWeight())
return;
if (availableOnly && !hasConfigReservation && config.getUnreservedSpace(assignment, this) < getWeight())
return;
if (!hasReservation && reservationMustBeUsed)
return;
}
}
}
if (config.getSubparts().size() == idx) {
if (skipSameTime && sSameTimePrecise) {
boolean waitListedOrSelected = false;
if (!getSelectedChoices().isEmpty() || !getWaitlistedChoices().isEmpty()) {
for (Section section : sections) {
if (isWaitlisted(section) || isSelected(section)) {
waitListedOrSelected = true;
break;
}
}
}
if (!waitListedOrSelected) {
for (Enrollment enrollment : enrollments) {
if (sameTimes(enrollment.getSections(), sections))
return;
}
}
}
Enrollment e = new Enrollment(this, priority, course, config, new HashSet<SctAssignment>(sections), null);
if (isNotAllowed(e)) {
} else if (!config.getOffering().hasReservations()) {
enrollments.add(e);
} else {
boolean mustHaveReservation = config.getOffering().getTotalUnreservedSpace() < getWeight();
boolean mustHaveConfigReservation = config.getTotalUnreservedSpace() < getWeight();
boolean mustHaveSectionReservation = false;
boolean containDisabledSection = false;
for (Section s : sections) {
if (s.getTotalUnreservedSpace() < getWeight()) {
mustHaveSectionReservation = true;
}
if (!getStudent().isAllowDisabled() && !s.isEnabled()) {
containDisabledSection = true;
}
}
boolean canOverLimit = false;
if (availableOnly) {
for (Reservation r : getReservations(course)) {
if (!r.canBatchAssignOverLimit() || !r.isIncluded(e))
continue;
if (r.getReservedAvailableSpace(assignment, this) < getWeight())
continue;
if (containDisabledSection && !r.isAllowDisabled())
continue;
enrollments.add(new Enrollment(this, priority, null, config, new HashSet<SctAssignment>(sections), r));
canOverLimit = true;
}
}
if (!canOverLimit) {
boolean reservationMustBeUsed = false;
reservations: for (Reservation r : (availableOnly ? getSortedReservations(assignment, course) : getReservations(course))) {
if (r.mustBeUsed())
reservationMustBeUsed = true;
if (!r.isIncluded(e))
continue;
if (availableOnly && r.getReservedAvailableSpace(assignment, this) < getWeight())
continue;
if (mustHaveConfigReservation && r.getConfigs().isEmpty())
continue;
if (mustHaveSectionReservation)
for (Section s : sections) if (r.getSections(s.getSubpart()) == null && s.getTotalUnreservedSpace() < getWeight())
continue reservations;
if (containDisabledSection && !r.isAllowDisabled())
continue;
enrollments.add(new Enrollment(this, priority, null, config, new HashSet<SctAssignment>(sections), r));
// only one available reservation suffice (the best matching one)
if (availableOnly)
return;
}
// a case w/o reservation
if (!(mustHaveReservation || mustHaveConfigReservation || mustHaveSectionReservation) && !(availableOnly && config.getOffering().getUnreservedSpace(assignment, this) < getWeight()) && !reservationMustBeUsed && !containDisabledSection) {
enrollments.add(new Enrollment(this, priority, !getReservations(course).isEmpty(), null, config, new HashSet<SctAssignment>(sections), null));
}
}
}
} else {
Subpart subpart = config.getSubparts().get(idx);
HashSet<TimeLocation> times = (skipSameTime ? new HashSet<TimeLocation>() : null);
List<Section> sectionsThisSubpart = subpart.getSections();
if (skipSameTime) {
sectionsThisSubpart = new ArrayList<Section>(subpart.getSections());
Collections.sort(sectionsThisSubpart, new AssignmentComparator<Section, Request, Enrollment>(assignment));
}
List<Section> matchingSectionsThisSubpart = new ArrayList<Section>(subpart.getSections().size());
boolean hasChildren = !subpart.getChildren().isEmpty();
for (Section section : sectionsThisSubpart) {
if (section.isCancelled())
continue;
if (!isRequired(section))
continue;
if (getInitialAssignment() != null && (getModel() != null && ((StudentSectioningModel) getModel()).getKeepInitialAssignments()) && !getInitialAssignment().getAssignments().contains(section))
continue;
if (isFixed() && !getFixedValue().getAssignments().contains(section))
continue;
if (section.getParent() != null && !sections.contains(section.getParent()))
continue;
if (section.isOverlapping(sections))
continue;
if (selectedOnly && hasSelection(section) && !isSelected(section))
continue;
if (isNotAllowed(course, section))
continue;
if (!getStudent().isAvailable(section)) {
boolean canOverlap = false;
for (Reservation r : getReservations(course)) {
if (!r.isAllowOverlap())
continue;
if (r.getSections(subpart) != null && !r.getSections(subpart).contains(section))
continue;
if (r.getReservedAvailableSpace(assignment, this) < getWeight())
continue;
canOverlap = true;
break;
}
if (!canOverlap)
continue;
}
boolean canOverLimit = false;
if (availableOnly) {
for (Reservation r : getReservations(course)) {
if (!r.canBatchAssignOverLimit())
continue;
if (r.getSections(subpart) != null && !r.getSections(subpart).contains(section))
continue;
if (r.getReservedAvailableSpace(assignment, this) < getWeight())
continue;
canOverLimit = true;
break;
}
}
if (!canOverLimit) {
if (availableOnly && section.getLimit() >= 0 && SectionLimit.getEnrollmentWeight(assignment, section, this) > section.getLimit())
continue;
if (config.getOffering().hasReservations()) {
boolean hasReservation = false, hasSectionReservation = false, reservationMustBeUsed = false;
for (Reservation r : getReservations(course)) {
if (r.mustBeUsed())
reservationMustBeUsed = true;
if (availableOnly && r.getReservedAvailableSpace(assignment, this) < getWeight())
continue;
if (r.getSections(subpart) == null) {
hasReservation = true;
} else if (r.getSections(subpart).contains(section)) {
hasReservation = true;
hasSectionReservation = true;
}
}
if (!hasSectionReservation && section.getTotalUnreservedSpace() < getWeight())
continue;
if (availableOnly && !hasSectionReservation && section.getUnreservedSpace(assignment, this) < getWeight())
continue;
if (!hasReservation && reservationMustBeUsed)
continue;
}
}
if (!getStudent().isAllowDisabled() && !section.isEnabled()) {
boolean allowDisabled = false;
for (Reservation r : getReservations(course)) {
if (!r.isAllowDisabled())
continue;
if (r.getSections(subpart) != null && !r.getSections(subpart).contains(section))
continue;
if (!r.getConfigs().isEmpty() && !r.getConfigs().contains(config))
continue;
allowDisabled = true;
break;
}
if (!allowDisabled)
continue;
}
if (skipSameTime && section.getTime() != null && !hasChildren && !times.add(section.getTime()) && !isSelected(section) && !isWaitlisted(section) && (section.getIgnoreConflictWithSectionIds() == null || section.getIgnoreConflictWithSectionIds().isEmpty()))
continue;
matchingSectionsThisSubpart.add(section);
}
if (random || limit > 0) {
sectionsThisSubpart = new ArrayList<Section>(sectionsThisSubpart);
Collections.shuffle(sectionsThisSubpart);
}
int i = 0;
for (Section section : matchingSectionsThisSubpart) {
sections.add(section);
computeEnrollments(assignment, enrollments, priority, penalty + section.getPenalty(), course, config, sections, idx + 1, availableOnly, skipSameTime, selectedOnly, random, limit < 0 ? limit : Math.max(1, limit * (1 + i) / matchingSectionsThisSubpart.size()));
sections.remove(section);
i++;
}
}
}
use of org.cpsolver.studentsct.StudentSectioningModel in project cpsolver by UniTime.
the class RandomizedBacktrackNeighbourSelection method values.
/**
* List of values of a variable.
* {@link CourseRequest#computeRandomEnrollments(Assignment, int)} with the provided
* limit is used for a {@link CourseRequest}.
*/
@Override
protected Iterator<Enrollment> values(BacktrackNeighbourSelection<Request, Enrollment>.BacktrackNeighbourSelectionContext context, Request variable) {
if (variable instanceof CourseRequest) {
final CourseRequest request = (CourseRequest) variable;
final StudentSectioningModel model = (StudentSectioningModel) context.getModel();
final Assignment<Request, Enrollment> assignment = context.getAssignment();
final Enrollment current = assignment.getValue(request);
List<Enrollment> values = (iMaxValues > 0 ? request.computeRandomEnrollments(assignment, iMaxValues) : request.computeEnrollments(assignment));
Collections.sort(values, new Comparator<Enrollment>() {
private HashMap<Enrollment, Double> iValues = new HashMap<Enrollment, Double>();
private Double value(Enrollment e) {
Double value = iValues.get(e);
if (value == null) {
if (model.getStudentQuality() != null)
value = model.getStudentWeights().getWeight(assignment, e, model.getStudentQuality().conflicts(e));
else
value = model.getStudentWeights().getWeight(assignment, e, (model.getDistanceConflict() == null ? null : model.getDistanceConflict().conflicts(e)), (model.getTimeOverlaps() == null ? null : model.getTimeOverlaps().conflicts(e)));
iValues.put(e, value);
}
return value;
}
@Override
public int compare(Enrollment e1, Enrollment e2) {
if (e1.equals(e2))
return 0;
if (e1.equals(current))
return -1;
if (e2.equals(current))
return 1;
Double v1 = value(e1), v2 = value(e2);
return v1.equals(v2) ? e1.compareTo(assignment, e2) : v2.compareTo(v1);
}
});
return values.iterator();
} else {
return variable.computeEnrollments(context.getAssignment()).iterator();
}
}
use of org.cpsolver.studentsct.StudentSectioningModel in project cpsolver by UniTime.
the class EnrollmentSelection method isAllowed.
/**
* true, if it is allowed to assign given value
* @param assignment current assignment
* @param value given value
* @param conflicts conflicting assignments
* @return true if it is allowed
*/
public boolean isAllowed(Assignment<Request, Enrollment> assignment, Enrollment value, Set<Enrollment> conflicts) {
if (value == null)
return true;
StudentSectioningModel model = (StudentSectioningModel) value.variable().getModel();
if (model.getNrLastLikeRequests(false) == 0 || model.getNrRealRequests(false) == 0)
return true;
Request request = value.variable();
if (request.getStudent().isDummy()) {
if (conflicts == null)
conflicts = value.variable().getModel().conflictValues(assignment, value);
for (Enrollment conflict : conflicts) {
if (!conflict.getRequest().getStudent().isDummy())
return false;
}
} else {
if (conflicts == null)
conflicts = value.variable().getModel().conflictValues(assignment, value);
if (conflicts.size() > (assignment.getValue(request) == null ? 1 : 0))
return false;
}
return true;
}
Aggregations