Search in sources :

Example 21 with Config

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;
}
Also used : CourseRequest(org.cpsolver.studentsct.model.CourseRequest) Config(org.cpsolver.studentsct.model.Config) Subpart(org.cpsolver.studentsct.model.Subpart) Section(org.cpsolver.studentsct.model.Section)

Example 22 with Config

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;
}
Also used : CourseRequest(org.cpsolver.studentsct.model.CourseRequest) CSVFile(org.cpsolver.ifs.util.CSVFile) Config(org.cpsolver.studentsct.model.Config) Subpart(org.cpsolver.studentsct.model.Subpart) CourseRequest(org.cpsolver.studentsct.model.CourseRequest) Request(org.cpsolver.studentsct.model.Request) Course(org.cpsolver.studentsct.model.Course) Offering(org.cpsolver.studentsct.model.Offering) Section(org.cpsolver.studentsct.model.Section)

Example 23 with Config

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;
}
Also used : Config(org.cpsolver.studentsct.model.Config) DistanceConflict(org.cpsolver.studentsct.extension.DistanceConflict) Offering(org.cpsolver.studentsct.model.Offering) Section(org.cpsolver.studentsct.model.Section) Constraint(org.cpsolver.ifs.model.Constraint) TimeOverlapsCounter(org.cpsolver.studentsct.extension.TimeOverlapsCounter) StudentConflict(org.cpsolver.studentsct.constraint.StudentConflict) DistanceConflict(org.cpsolver.studentsct.extension.DistanceConflict) TreeSet(java.util.TreeSet) Subpart(org.cpsolver.studentsct.model.Subpart)

Example 24 with Config

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;
}
Also used : Config(org.cpsolver.studentsct.model.Config) Subpart(org.cpsolver.studentsct.model.Subpart) Element(org.dom4j.Element)

Example 25 with 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);
    }
}
Also used : Config(org.cpsolver.studentsct.model.Config) ArrayList(java.util.ArrayList) Enrollment(org.cpsolver.studentsct.model.Enrollment)

Aggregations

Config (org.cpsolver.studentsct.model.Config)30 Section (org.cpsolver.studentsct.model.Section)23 Subpart (org.cpsolver.studentsct.model.Subpart)20 Course (org.cpsolver.studentsct.model.Course)13 Offering (org.cpsolver.studentsct.model.Offering)13 CourseRequest (org.cpsolver.studentsct.model.CourseRequest)11 Enrollment (org.cpsolver.studentsct.model.Enrollment)9 Request (org.cpsolver.studentsct.model.Request)8 HashSet (java.util.HashSet)6 TreeSet (java.util.TreeSet)6 DistanceConflict (org.cpsolver.studentsct.extension.DistanceConflict)5 TimeOverlapsCounter (org.cpsolver.studentsct.extension.TimeOverlapsCounter)5 BitSet (java.util.BitSet)4 Set (java.util.Set)4 Student (org.cpsolver.studentsct.model.Student)4 Reservation (org.cpsolver.studentsct.reservation.Reservation)4 DecimalFormat (java.text.DecimalFormat)3 ArrayList (java.util.ArrayList)3 HashMap (java.util.HashMap)3 Hashtable (java.util.Hashtable)3