use of org.cpsolver.studentsct.model.Config in project cpsolver by UniTime.
the class MultiCriteriaBranchAndBoundSelection method isAllowed.
public boolean isAllowed(int idx, Enrollment enrollment) {
if (enrollment.isCourseRequest()) {
CourseRequest request = (CourseRequest) enrollment.getRequest();
Config reqConfig = iRequiredConfig.get(request);
if (reqConfig != null) {
if (!reqConfig.equals(enrollment.getConfig()))
return false;
Hashtable<Subpart, Section> reqSections = iRequiredSection.get(request);
for (Section section : enrollment.getSections()) {
Section reqSection = reqSections.get(section.getSubpart());
if (reqSection == null)
continue;
if (!section.equals(reqSection))
return false;
}
}
} else if (iRequiredFreeTimes.contains(enrollment.getRequest())) {
if (enrollment.getAssignments() == null || enrollment.getAssignments().isEmpty())
return false;
}
return true;
}
use of org.cpsolver.studentsct.model.Config in project cpsolver by UniTime.
the class CourseLimitCheck method check.
/**
* Check for courses where the limit is below the number of students that
* request the course
*
* @return false, if there is such a case
*/
public boolean check() {
sLog.info("Checking for course limits...");
boolean ret = true;
for (Offering offering : getModel().getOfferings()) {
boolean hasUnlimitedSection = false;
if (iFixUnlimited)
for (Config config : offering.getConfigs()) {
for (Subpart subpart : config.getSubparts()) {
for (Section section : subpart.getSections()) {
if (section.getLimit() < 0)
hasUnlimitedSection = true;
}
}
}
int offeringLimit = 0;
int nrStudents = 0;
for (Course course : offering.getCourses()) {
if (course.getLimit() < 0) {
offeringLimit = -1;
continue;
}
if (iFixUnlimited && hasUnlimitedSection) {
sLog.info("Course " + course + " made unlimited.");
course.setLimit(-1);
offeringLimit = -1;
continue;
}
double total = 0;
double lastLike = 0, real = 0;
for (Request request : getModel().variables()) {
if (request instanceof CourseRequest && ((CourseRequest) request).getCourses().contains(course)) {
total += request.getWeight();
if (request.getStudent().isDummy())
lastLike += request.getWeight();
else
real += request.getWeight();
}
}
nrStudents += Math.round(total);
offeringLimit += course.getLimit();
if (Math.round(total) > course.getLimit()) {
sLog.error("Course " + course + " is requested by " + sDF.format(total) + " students, but its limit is only " + course.getLimit());
ret = false;
iCSVFile.addLine(new CSVFile.CSVField[] { new CSVFile.CSVField(course.getName()), new CSVFile.CSVField(course.getLimit()), new CSVFile.CSVField(total), new CSVFile.CSVField(real), new CSVFile.CSVField(lastLike) });
if (iUpZeroLimits && course.getLimit() == 0) {
int oldLimit = course.getLimit();
course.setLimit((int) Math.round(total));
sLog.info(" -- limit of course " + course + " increased to " + course.getLimit() + " (was " + oldLimit + ")");
} else if (iUpNonZeroLimits && course.getLimit() > 0) {
int oldLimit = course.getLimit();
course.setLimit((int) Math.round(total));
sLog.info(" -- limit of course " + course + " increased to " + course.getLimit() + " (was " + oldLimit + ")");
}
}
}
if (iUpZeroLimits && offeringLimit == 0 && nrStudents > 0) {
for (Config config : offering.getConfigs()) {
for (Subpart subpart : config.getSubparts()) {
for (Section section : subpart.getSections()) {
int oldLimit = section.getLimit();
section.setLimit(Math.max(section.getLimit(), (int) Math.ceil(nrStudents / subpart.getSections().size())));
sLog.info(" -- limit of section " + section + " increased to " + section.getLimit() + " (was " + oldLimit + ")");
}
}
}
} else if (iUpNonZeroLimits && offeringLimit >= 0 && nrStudents > offeringLimit) {
double fact = ((double) nrStudents) / offeringLimit;
for (Config config : offering.getConfigs()) {
for (Subpart subpart : config.getSubparts()) {
for (Section section : subpart.getSections()) {
int oldLimit = section.getLimit();
section.setLimit((int) Math.ceil(fact * section.getLimit()));
sLog.info(" -- limit of section " + section + " increased to " + section.getLimit() + " (was " + oldLimit + ")");
}
}
}
}
if (offeringLimit >= 0) {
int totalSectionLimit = 0;
for (Config config : offering.getConfigs()) {
int configLimit = -1;
for (Subpart subpart : config.getSubparts()) {
int subpartLimit = 0;
for (Section section : subpart.getSections()) {
subpartLimit += section.getLimit();
}
if (configLimit < 0)
configLimit = subpartLimit;
else
configLimit = Math.min(configLimit, subpartLimit);
}
totalSectionLimit += configLimit;
}
if (totalSectionLimit < offeringLimit) {
sLog.error("Offering limit of " + offering + " is " + offeringLimit + ", but total section limit is only " + totalSectionLimit);
if (iUpZeroLimits && totalSectionLimit == 0) {
for (Config config : offering.getConfigs()) {
for (Subpart subpart : config.getSubparts()) {
for (Section section : subpart.getSections()) {
int oldLimit = section.getLimit();
section.setLimit(Math.max(section.getLimit(), (int) Math.ceil(((double) offeringLimit) / subpart.getSections().size())));
sLog.info(" -- limit of section " + section + " increased to " + section.getLimit() + " (was " + oldLimit + ")");
}
}
}
} else if (iUpNonZeroLimits && totalSectionLimit > 0) {
double fact = ((double) offeringLimit) / totalSectionLimit;
for (Config config : offering.getConfigs()) {
for (Subpart subpart : config.getSubparts()) {
for (Section section : subpart.getSections()) {
int oldLimit = section.getLimit();
section.setLimit((int) Math.ceil(fact * section.getLimit()));
sLog.info(" -- limit of section " + section + " increased to " + section.getLimit() + " (was " + oldLimit + ")");
}
}
}
}
}
}
}
return ret;
}
use of org.cpsolver.studentsct.model.Config in project cpsolver by UniTime.
the class StudentSectioningModel method getExtendedInfo.
/**
* Model extended info. Some more information (that is more expensive to
* compute) is added to an ordinary {@link Model#getInfo(Assignment)}.
*/
@Override
public Map<String, String> getExtendedInfo(Assignment<Request, Enrollment> assignment) {
Map<String, String> info = getInfo(assignment);
/*
int nrLastLikeStudents = getNrLastLikeStudents(true);
if (nrLastLikeStudents != 0 && nrLastLikeStudents != getStudents().size()) {
int nrRealStudents = getStudents().size() - nrLastLikeStudents;
int nrLastLikeCompleteStudents = getNrCompleteLastLikeStudents(true);
int nrRealCompleteStudents = getCompleteStudents().size() - nrLastLikeCompleteStudents;
info.put("Projected students with complete schedule", sDecimalFormat.format(100.0
* nrLastLikeCompleteStudents / nrLastLikeStudents)
+ "% (" + nrLastLikeCompleteStudents + "/" + nrLastLikeStudents + ")");
info.put("Real students with complete schedule", sDecimalFormat.format(100.0 * nrRealCompleteStudents
/ nrRealStudents)
+ "% (" + nrRealCompleteStudents + "/" + nrRealStudents + ")");
int nrLastLikeRequests = getNrLastLikeRequests(true);
int nrRealRequests = variables().size() - nrLastLikeRequests;
int nrLastLikeAssignedRequests = getNrAssignedLastLikeRequests(true);
int nrRealAssignedRequests = assignedVariables().size() - nrLastLikeAssignedRequests;
info.put("Projected assigned requests", sDecimalFormat.format(100.0 * nrLastLikeAssignedRequests
/ nrLastLikeRequests)
+ "% (" + nrLastLikeAssignedRequests + "/" + nrLastLikeRequests + ")");
info.put("Real assigned requests", sDecimalFormat.format(100.0 * nrRealAssignedRequests / nrRealRequests)
+ "% (" + nrRealAssignedRequests + "/" + nrRealRequests + ")");
}
*/
// info.put("Average unassigned priority", sDecimalFormat.format(avgUnassignPriority()));
// info.put("Average number of requests", sDecimalFormat.format(avgNrRequests()));
/*
double total = 0;
for (Request r: variables())
if (r.getAssignment() != null)
total += r.getWeight() * iStudentWeights.getWeight(r.getAssignment());
*/
double dc = 0;
if (getDistanceConflict() != null && getDistanceConflict().getTotalNrConflicts(assignment) != 0) {
Set<DistanceConflict.Conflict> conf = getDistanceConflict().getAllConflicts(assignment);
for (DistanceConflict.Conflict c : conf) dc += avg(c.getR1().getWeight(), c.getR2().getWeight()) * iStudentWeights.getDistanceConflictWeight(assignment, c);
if (!conf.isEmpty())
info.put("Student distance conflicts", conf.size() + " (weighted: " + sDecimalFormat.format(dc) + ")");
}
double toc = 0;
if (getTimeOverlaps() != null && getTimeOverlaps().getTotalNrConflicts(assignment) != 0) {
Set<TimeOverlapsCounter.Conflict> conf = getTimeOverlaps().getAllConflicts(assignment);
int share = 0;
for (TimeOverlapsCounter.Conflict c : conf) {
if (c.getR1() != null)
toc += c.getR1Weight() * iStudentWeights.getTimeOverlapConflictWeight(assignment, c.getE1(), c);
if (c.getR2() != null)
toc += c.getR2Weight() * iStudentWeights.getTimeOverlapConflictWeight(assignment, c.getE2(), c);
share += c.getShare();
}
if (toc != 0.0)
info.put("Time overlapping conflicts", share + " (average: " + sDecimalFormat.format(5.0 * share / getStudents().size()) + " min, weighted: " + sDoubleFormat.format(toc) + ")");
}
/*
info.put("Overall solution value", sDecimalFormat.format(total - dc - toc) + (dc == 0.0 && toc == 0.0 ? "" :
" (" + (dc != 0.0 ? "distance: " + sDecimalFormat.format(dc): "") + (dc != 0.0 && toc != 0.0 ? ", " : "") +
(toc != 0.0 ? "overlap: " + sDecimalFormat.format(toc) : "") + ")")
);
*/
double disbWeight = 0;
int disbSections = 0;
int disb10Sections = 0;
int disb10Limit = getProperties().getPropertyInt("Info.ListDisbalancedSections", 0);
Set<String> disb10SectionList = (disb10Limit == 0 ? null : new TreeSet<String>());
for (Offering offering : getOfferings()) {
for (Config config : offering.getConfigs()) {
double enrl = config.getEnrollmentTotalWeight(assignment, null);
for (Subpart subpart : config.getSubparts()) {
if (subpart.getSections().size() <= 1)
continue;
if (subpart.getLimit() > 0) {
// sections have limits -> desired size is section limit x (total enrollment / total limit)
double ratio = enrl / subpart.getLimit();
for (Section section : subpart.getSections()) {
double desired = ratio * section.getLimit();
disbWeight += Math.abs(section.getEnrollmentTotalWeight(assignment, null) - desired);
disbSections++;
if (Math.abs(desired - section.getEnrollmentTotalWeight(assignment, null)) >= Math.max(1.0, 0.1 * section.getLimit())) {
disb10Sections++;
if (disb10SectionList != null)
disb10SectionList.add(section.getSubpart().getConfig().getOffering().getName() + " " + section.getSubpart().getName() + " " + section.getName());
}
}
} else {
// unlimited sections -> desired size is total enrollment / number of sections
for (Section section : subpart.getSections()) {
double desired = enrl / subpart.getSections().size();
disbWeight += Math.abs(section.getEnrollmentTotalWeight(assignment, null) - desired);
disbSections++;
if (Math.abs(desired - section.getEnrollmentTotalWeight(assignment, null)) >= Math.max(1.0, 0.1 * desired)) {
disb10Sections++;
if (disb10SectionList != null)
disb10SectionList.add(section.getSubpart().getConfig().getOffering().getName() + " " + section.getSubpart().getName() + " " + section.getName());
}
}
}
}
}
}
if (disbSections != 0) {
double assignedCRWeight = getContext(assignment).getAssignedCourseRequestWeight();
info.put("Average disbalance", sDecimalFormat.format(disbWeight / disbSections) + " (" + sDecimalFormat.format(assignedCRWeight == 0 ? 0.0 : 100.0 * disbWeight / assignedCRWeight) + "%)");
String list = "";
if (disb10SectionList != null) {
int i = 0;
for (String section : disb10SectionList) {
if (i == disb10Limit) {
list += "<br>...";
break;
}
list += "<br>" + section;
i++;
}
}
info.put("Sections disbalanced by 10% or more", disb10Sections + " (" + sDecimalFormat.format(disbSections == 0 ? 0.0 : 100.0 * disb10Sections / disbSections) + "%)" + list);
}
return info;
}
use of org.cpsolver.studentsct.model.Config in project cpsolver by UniTime.
the class StudentSectioningXMLLoader method loadConfig.
/**
* Load config
* @param configEl config element
* @param offering parent offering
* @param subpartTable subpart table (of the offering)
* @param sectionTable section table (of the offering)
* @param timetable provided timetable
* @return loaded config
*/
protected Config loadConfig(Element configEl, Offering offering, Map<Long, Subpart> subpartTable, Map<Long, Section> sectionTable, Map<Long, Placement> timetable) {
Config config = new Config(Long.parseLong(configEl.attributeValue("id")), Integer.parseInt(configEl.attributeValue("limit", "-1")), configEl.attributeValue("name", "G" + configEl.attributeValue("id")), offering);
Element imEl = configEl.element("instructional-method");
if (imEl != null) {
config.setInstructionalMethodId(Long.parseLong(imEl.attributeValue("id")));
config.setInstructionalMethodName(imEl.attributeValue("name", "M" + imEl.attributeValue("id")));
}
for (Iterator<?> k = configEl.elementIterator("subpart"); k.hasNext(); ) {
Element subpartEl = (Element) k.next();
Subpart subpart = loadSubpart(subpartEl, config, subpartTable, sectionTable, timetable);
subpartTable.put(new Long(subpart.getId()), subpart);
}
return config;
}
use of org.cpsolver.studentsct.model.Config in project cpsolver by UniTime.
the class ConfigLimit method computeConflicts.
/**
* A given enrollment is conflicting, if the config's enrollment
* (computed by {@link ConfigLimit#getEnrollmentWeight(Assignment, Config, Request)})
* exceeds the limit. <br>
* If the limit is breached, one or more existing enrollments are
* (randomly) selected as conflicting until the overall weight is under the
* limit.
*
* @param enrollment
* {@link Enrollment} that is being considered
* @param conflicts
* all computed conflicting requests are added into this set
*/
@Override
public void computeConflicts(Assignment<Request, Enrollment> assignment, Enrollment enrollment, Set<Enrollment> conflicts) {
// check reservation can assign over the limit
if (enrollment.getReservation() != null && enrollment.getReservation().canBatchAssignOverLimit())
return;
// enrollment's config
Config config = enrollment.getConfig();
// exclude free time requests
if (config == null)
return;
// unlimited config
if (config.getLimit() < 0)
return;
// new enrollment weight
double enrlWeight = getEnrollmentWeight(assignment, config, enrollment.getRequest());
// below limit -> ok
if (enrlWeight <= config.getLimit())
return;
// above limit -> compute adepts (current assignments that are not
// yet conflicting)
// exclude all conflicts as well
List<Enrollment> adepts = new ArrayList<Enrollment>(config.getEnrollments(assignment).size());
for (Enrollment e : config.getEnrollments(assignment)) {
if (e.getRequest().equals(enrollment.getRequest()))
continue;
if (e.getReservation() != null && e.getReservation().canBatchAssignOverLimit())
continue;
if (conflicts.contains(e))
enrlWeight -= e.getRequest().getWeight();
else
adepts.add(e);
}
// while above limit -> pick an adept and make it conflicting
while (enrlWeight > config.getLimit()) {
if (adepts.isEmpty()) {
// no adepts -> enrollment cannot be assigned
conflicts.add(enrollment);
return;
}
// pick adept (prefer dummy students & students w/o reservation), decrease enrollment
// weight, make conflict
List<Enrollment> best = new ArrayList<Enrollment>();
boolean bestDummy = false;
double bestValue = 0;
boolean bestRes = true;
for (Enrollment adept : adepts) {
boolean dummy = adept.getStudent().isDummy();
double value = adept.toDouble(assignment, false);
boolean res = (adept.getReservation() != null);
if (iPreferDummyStudents && dummy != bestDummy) {
if (dummy) {
best.clear();
best.add(adept);
bestDummy = dummy;
bestValue = value;
bestRes = res;
}
continue;
}
if (bestRes != res) {
if (!res) {
best.clear();
best.add(adept);
bestDummy = dummy;
bestValue = value;
bestRes = res;
}
continue;
}
if (best.isEmpty() || value > bestValue) {
if (best.isEmpty())
best.clear();
best.add(adept);
bestDummy = dummy;
bestValue = value;
bestRes = res;
} else if (bestValue == value) {
best.add(adept);
}
}
Enrollment conflict = ToolBox.random(best);
adepts.remove(conflict);
enrlWeight -= conflict.getRequest().getWeight();
conflicts.add(conflict);
}
}
Aggregations