use of org.cpsolver.studentsct.model.Request in project cpsolver by UniTime.
the class BestPenaltyCriterion 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;
for (int idx = 0; idx < current.length; idx++) {
if (isFreeTime(idx))
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;
}
}
}
// 1. maximize number of penalties
int bestPenalties = 0, currentPenalties = 0;
for (int idx = 0; idx < current.length; idx++) {
if (best[idx] != null) {
for (Section section : best[idx].getSections()) bestPenalties += iModel.getOverExpected(assignment, section, best[idx].getRequest());
}
if (current[idx] != null && idx < maxIdx) {
for (Section section : current[idx].getSections()) currentPenalties += iModel.getOverExpected(assignment, section, current[idx].getRequest());
}
}
if (currentPenalties < bestPenalties)
return true;
if (bestPenalties < currentPenalties)
return false;
return false;
}
use of org.cpsolver.studentsct.model.Request in project cpsolver by UniTime.
the class Test method loadCrsReqFiles.
/**
* Load course request from the given files (in the format being used by the
* old MSF system)
*
* @param model
* student sectioning model (with offerings loaded)
* @param files
* semi-colon separated list of files to be loaded
*/
public static void loadCrsReqFiles(StudentSectioningModel model, String files) {
try {
boolean lastLike = model.getProperties().getPropertyBoolean("Test.CrsReqIsLastLike", true);
boolean shuffleIds = model.getProperties().getPropertyBoolean("Test.CrsReqShuffleStudentIds", true);
boolean tryWithoutSuffix = model.getProperties().getPropertyBoolean("Test.CrsReqTryWithoutSuffix", false);
HashMap<Long, Student> students = new HashMap<Long, Student>();
long reqId = 0;
for (StringTokenizer stk = new StringTokenizer(files, ";"); stk.hasMoreTokens(); ) {
String file = stk.nextToken();
sLog.debug("Loading " + file + " ...");
BufferedReader in = new BufferedReader(new FileReader(file));
String line;
int lineIndex = 0;
while ((line = in.readLine()) != null) {
lineIndex++;
if (line.length() <= 150)
continue;
char code = line.charAt(13);
if (code == 'H' || code == 'T')
// skip header and tail
continue;
long studentId = Long.parseLong(line.substring(14, 23));
Student student = students.get(new Long(studentId));
if (student == null) {
student = new Student(studentId);
if (lastLike)
student.setDummy(true);
students.put(new Long(studentId), student);
sLog.debug(" -- loading student " + studentId + " ...");
} else
sLog.debug(" -- updating student " + studentId + " ...");
line = line.substring(150);
while (line.length() >= 20) {
String subjectArea = line.substring(0, 4).trim();
String courseNbr = line.substring(4, 8).trim();
if (subjectArea.length() == 0 || courseNbr.length() == 0) {
line = line.substring(20);
continue;
}
/*
* // UNUSED String instrSel = line.substring(8,10);
* //ZZ - Remove previous instructor selection char
* reqPDiv = line.charAt(10); //P - Personal preference;
* C - Conflict resolution; //0 - (Zero) used by program
* only, for change requests to reschedule division //
* (used to reschedule canceled division) String reqDiv
* = line.substring(11,13); //00 - Reschedule division
* String reqSect = line.substring(13,15); //Contains
* designator for designator-required courses String
* credit = line.substring(15,19); char nameRaise =
* line.charAt(19); //N - Name raise
*/
// A - Add; D - Drop; C -
char action = line.charAt(19);
// Change
sLog.debug(" -- requesting " + subjectArea + " " + courseNbr + " (action:" + action + ") ...");
Course course = null;
offerings: for (Offering offering : model.getOfferings()) {
for (Course c : offering.getCourses()) {
if (c.getSubjectArea().equals(subjectArea) && c.getCourseNumber().equals(courseNbr)) {
course = c;
break offerings;
}
}
}
if (course == null && tryWithoutSuffix && courseNbr.charAt(courseNbr.length() - 1) >= 'A' && courseNbr.charAt(courseNbr.length() - 1) <= 'Z') {
String courseNbrNoSfx = courseNbr.substring(0, courseNbr.length() - 1);
offerings: for (Offering offering : model.getOfferings()) {
for (Course c : offering.getCourses()) {
if (c.getSubjectArea().equals(subjectArea) && c.getCourseNumber().equals(courseNbrNoSfx)) {
course = c;
break offerings;
}
}
}
}
if (course == null) {
if (courseNbr.charAt(courseNbr.length() - 1) >= 'A' && courseNbr.charAt(courseNbr.length() - 1) <= 'Z') {
} else {
sLog.warn(" -- course " + subjectArea + " " + courseNbr + " not found (file " + file + ", line " + lineIndex + ")");
}
} else {
CourseRequest courseRequest = null;
for (Request request : student.getRequests()) {
if (request instanceof CourseRequest && ((CourseRequest) request).getCourses().contains(course)) {
courseRequest = (CourseRequest) request;
break;
}
}
if (action == 'A') {
if (courseRequest == null) {
List<Course> courses = new ArrayList<Course>(1);
courses.add(course);
courseRequest = new CourseRequest(reqId++, student.getRequests().size(), false, student, courses, false, null);
} else {
sLog.warn(" -- request for course " + course + " is already present");
}
} else if (action == 'D') {
if (courseRequest == null) {
sLog.warn(" -- request for course " + course + " is not present -- cannot be dropped");
} else {
student.getRequests().remove(courseRequest);
}
} else if (action == 'C') {
if (courseRequest == null) {
sLog.warn(" -- request for course " + course + " is not present -- cannot be changed");
} else {
// ?
}
} else {
sLog.warn(" -- unknown action " + action);
}
}
line = line.substring(20);
}
}
in.close();
}
HashMap<Course, List<Request>> requests = new HashMap<Course, List<Request>>();
Set<Long> studentIds = new HashSet<Long>();
for (Student student : students.values()) {
if (!student.getRequests().isEmpty())
model.addStudent(student);
if (shuffleIds) {
long newId = -1;
while (true) {
newId = 1 + (long) (999999999L * Math.random());
if (studentIds.add(new Long(newId)))
break;
}
student.setId(newId);
}
if (student.isDummy()) {
for (Request request : student.getRequests()) {
if (request instanceof CourseRequest) {
Course course = ((CourseRequest) request).getCourses().get(0);
List<Request> requestsThisCourse = requests.get(course);
if (requestsThisCourse == null) {
requestsThisCourse = new ArrayList<Request>();
requests.put(course, requestsThisCourse);
}
requestsThisCourse.add(request);
}
}
}
}
Collections.sort(model.getStudents(), new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getId(), o2.getId());
}
});
for (Map.Entry<Course, List<Request>> entry : requests.entrySet()) {
Course course = entry.getKey();
List<Request> requestsThisCourse = entry.getValue();
double weight = getLastLikeStudentWeight(course, 0, requestsThisCourse.size());
for (Request request : requestsThisCourse) {
request.setWeight(weight);
}
}
if (model.getProperties().getProperty("Test.EtrChk") != null) {
for (StringTokenizer stk = new StringTokenizer(model.getProperties().getProperty("Test.EtrChk"), ";"); stk.hasMoreTokens(); ) {
String file = stk.nextToken();
sLog.debug("Loading " + file + " ...");
BufferedReader in = new BufferedReader(new FileReader(file));
try {
String line;
while ((line = in.readLine()) != null) {
if (line.length() < 55)
continue;
char code = line.charAt(12);
if (code == 'H' || code == 'T')
// skip header and tail
continue;
if (code == 'D' || code == 'K')
// skip delete nad cancel
continue;
long studentId = Long.parseLong(line.substring(2, 11));
Student student = students.get(new Long(studentId));
if (student == null) {
sLog.info(" -- student " + studentId + " not found");
continue;
}
sLog.info(" -- reading student " + studentId);
String area = line.substring(15, 18).trim();
if (area.length() == 0)
continue;
String clasf = line.substring(18, 20).trim();
String major = line.substring(21, 24).trim();
String minor = line.substring(24, 27).trim();
student.getAcademicAreaClasiffications().clear();
student.getMajors().clear();
student.getMinors().clear();
student.getAcademicAreaClasiffications().add(new AcademicAreaCode(area, clasf));
if (major.length() > 0)
student.getMajors().add(new AcademicAreaCode(area, major));
if (minor.length() > 0)
student.getMinors().add(new AcademicAreaCode(area, minor));
}
} finally {
in.close();
}
}
}
int without = 0;
for (Student student : students.values()) {
if (student.getAcademicAreaClasiffications().isEmpty())
without++;
}
fixPriorities(model);
sLog.info("Students without academic area: " + without);
} catch (Exception e) {
sLog.error(e.getMessage(), e);
}
}
use of org.cpsolver.studentsct.model.Request in project cpsolver by UniTime.
the class Test method fixPriorities.
public static void fixPriorities(StudentSectioningModel model) {
for (Student student : model.getStudents()) {
Collections.sort(student.getRequests(), new Comparator<Request>() {
@Override
public int compare(Request r1, Request r2) {
int cmp = Double.compare(r1.getPriority(), r2.getPriority());
if (cmp != 0)
return cmp;
return Double.compare(r1.getId(), r2.getId());
}
});
int priority = 0;
for (Request request : student.getRequests()) {
if (priority != request.getPriority()) {
sLog.debug("Change priority of " + request + " to " + priority);
request.setPriority(priority);
}
}
}
}
use of org.cpsolver.studentsct.model.Request in project cpsolver by UniTime.
the class Test method onlineSectioning.
/** Online sectioning test
* @param cfg solver configuration
* @return resultant solution
* @throws Exception thrown when the sectioning fails
**/
public static Solution<Request, Enrollment> onlineSectioning(DataProperties cfg) throws Exception {
Solution<Request, Enrollment> solution = load(cfg);
if (solution == null)
return null;
StudentSectioningModel model = (StudentSectioningModel) solution.getModel();
Assignment<Request, Enrollment> assignment = solution.getAssignment();
solution.addSolutionListener(new TestSolutionListener());
double startTime = JProf.currentTimeSec();
Solver<Request, Enrollment> solver = new Solver<Request, Enrollment>(cfg);
solver.setInitalSolution(solution);
solver.initSolver();
OnlineSelection onlineSelection = new OnlineSelection(cfg);
onlineSelection.init(solver);
double totalPenalty = 0, minPenalty = 0, maxPenalty = 0;
double minAvEnrlPenalty = 0, maxAvEnrlPenalty = 0;
double totalPrefPenalty = 0, minPrefPenalty = 0, maxPrefPenalty = 0;
double minAvEnrlPrefPenalty = 0, maxAvEnrlPrefPenalty = 0;
int nrChoices = 0, nrEnrollments = 0, nrCourseRequests = 0;
int chChoices = 0, chCourseRequests = 0, chStudents = 0;
int choiceLimit = model.getProperties().getPropertyInt("Test.ChoicesLimit", -1);
File outDir = new File(model.getProperties().getProperty("General.Output", "."));
outDir.mkdirs();
PrintWriter pw = new PrintWriter(new FileWriter(new File(outDir, "choices.csv")));
List<Student> students = model.getStudents();
try {
@SuppressWarnings("rawtypes") Class studentOrdClass = Class.forName(model.getProperties().getProperty("Test.StudentOrder", StudentRandomOrder.class.getName()));
@SuppressWarnings("unchecked") StudentOrder studentOrd = (StudentOrder) studentOrdClass.getConstructor(new Class[] { DataProperties.class }).newInstance(new Object[] { model.getProperties() });
students = studentOrd.order(model.getStudents());
} catch (Exception e) {
sLog.error("Unable to reorder students, reason: " + e.getMessage(), e);
}
ShutdownHook hook = new ShutdownHook(solver);
Runtime.getRuntime().addShutdownHook(hook);
for (Student student : students) {
if (student.nrAssignedRequests(assignment) > 0)
// skip students with assigned courses (i.e., students
continue;
// already assigned by a batch sectioning process)
sLog.info("Sectioning student: " + student);
BranchBoundSelection.Selection selection = onlineSelection.getSelection(assignment, student);
BranchBoundNeighbour neighbour = selection.select();
if (neighbour != null) {
StudentPreferencePenalties penalties = null;
if (selection instanceof OnlineSelection.EpsilonSelection) {
OnlineSelection.EpsilonSelection epsSelection = (OnlineSelection.EpsilonSelection) selection;
penalties = epsSelection.getPenalties();
for (int i = 0; i < neighbour.getAssignment().length; i++) {
Request r = student.getRequests().get(i);
if (r instanceof CourseRequest) {
nrCourseRequests++;
chCourseRequests++;
int chChoicesThisRq = 0;
CourseRequest request = (CourseRequest) r;
for (Enrollment x : request.getAvaiableEnrollments(assignment)) {
nrEnrollments++;
if (epsSelection.isAllowed(i, x)) {
nrChoices++;
if (choiceLimit <= 0 || chChoicesThisRq < choiceLimit) {
chChoices++;
chChoicesThisRq++;
}
}
}
}
}
chStudents++;
if (chStudents == 100) {
pw.println(sDF.format(((double) chChoices) / chCourseRequests));
pw.flush();
chStudents = 0;
chChoices = 0;
chCourseRequests = 0;
}
}
for (int i = 0; i < neighbour.getAssignment().length; i++) {
if (neighbour.getAssignment()[i] == null)
continue;
Enrollment enrollment = neighbour.getAssignment()[i];
if (enrollment.getRequest() instanceof CourseRequest) {
CourseRequest request = (CourseRequest) enrollment.getRequest();
double[] avEnrlMinMax = getMinMaxAvailableEnrollmentPenalty(assignment, request);
minAvEnrlPenalty += avEnrlMinMax[0];
maxAvEnrlPenalty += avEnrlMinMax[1];
totalPenalty += enrollment.getPenalty();
minPenalty += request.getMinPenalty();
maxPenalty += request.getMaxPenalty();
if (penalties != null) {
double[] avEnrlPrefMinMax = penalties.getMinMaxAvailableEnrollmentPenalty(assignment, enrollment.getRequest());
minAvEnrlPrefPenalty += avEnrlPrefMinMax[0];
maxAvEnrlPrefPenalty += avEnrlPrefMinMax[1];
totalPrefPenalty += penalties.getPenalty(enrollment);
minPrefPenalty += penalties.getMinPenalty(enrollment.getRequest());
maxPrefPenalty += penalties.getMaxPenalty(enrollment.getRequest());
}
}
}
neighbour.assign(assignment, solution.getIteration());
sLog.info("Student " + student + " enrolls into " + neighbour);
onlineSelection.updateSpace(assignment, student);
} else {
sLog.warn("No solution found.");
}
solution.update(JProf.currentTimeSec() - startTime);
}
if (chCourseRequests > 0)
pw.println(sDF.format(((double) chChoices) / chCourseRequests));
pw.flush();
pw.close();
HashMap<String, String> extra = new HashMap<String, String>();
sLog.info("Overall penalty is " + getPerc(totalPenalty, minPenalty, maxPenalty) + "% (" + sDF.format(totalPenalty) + "/" + sDF.format(minPenalty) + ".." + sDF.format(maxPenalty) + ")");
extra.put("Overall penalty", getPerc(totalPenalty, minPenalty, maxPenalty) + "% (" + sDF.format(totalPenalty) + "/" + sDF.format(minPenalty) + ".." + sDF.format(maxPenalty) + ")");
extra.put("Overall available enrollment penalty", getPerc(totalPenalty, minAvEnrlPenalty, maxAvEnrlPenalty) + "% (" + sDF.format(totalPenalty) + "/" + sDF.format(minAvEnrlPenalty) + ".." + sDF.format(maxAvEnrlPenalty) + ")");
if (onlineSelection.isUseStudentPrefPenalties()) {
sLog.info("Overall preference penalty is " + getPerc(totalPrefPenalty, minPrefPenalty, maxPrefPenalty) + "% (" + sDF.format(totalPrefPenalty) + "/" + sDF.format(minPrefPenalty) + ".." + sDF.format(maxPrefPenalty) + ")");
extra.put("Overall preference penalty", getPerc(totalPrefPenalty, minPrefPenalty, maxPrefPenalty) + "% (" + sDF.format(totalPrefPenalty) + "/" + sDF.format(minPrefPenalty) + ".." + sDF.format(maxPrefPenalty) + ")");
extra.put("Overall preference available enrollment penalty", getPerc(totalPrefPenalty, minAvEnrlPrefPenalty, maxAvEnrlPrefPenalty) + "% (" + sDF.format(totalPrefPenalty) + "/" + sDF.format(minAvEnrlPrefPenalty) + ".." + sDF.format(maxAvEnrlPrefPenalty) + ")");
extra.put("Average number of choices", sDF.format(((double) nrChoices) / nrCourseRequests) + " (" + nrChoices + "/" + nrCourseRequests + ")");
extra.put("Average number of enrollments", sDF.format(((double) nrEnrollments) / nrCourseRequests) + " (" + nrEnrollments + "/" + nrCourseRequests + ")");
}
hook.setExtra(extra);
return solution;
}
use of org.cpsolver.studentsct.model.Request 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;
}
Aggregations