    001package org.cpsolver.studentsct.online.expectations;
    002
    003import java.util.Set;
    004
    005import org.cpsolver.ifs.assignment.Assignment;
    006import org.cpsolver.ifs.util.DataProperties;
    007import org.cpsolver.studentsct.model.Enrollment;
    008import org.cpsolver.studentsct.model.Request;
    009import org.cpsolver.studentsct.model.Section;
    010
    011/**
    012 * A class is considered over-expected when it is not available (limit is zero) or when
    013 * there is a time conflict with some other enrollment of the student (despite the
    014 * reservation allowing for the conflict).
    015 *
    016 * @version StudentSct 1.3 (Student Sectioning)<br>
    017 *          Copyright (C) 2014 Tomáš Müller<br>
    018 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    019 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    020 * <br>
    021 *          This library is free software; you can redistribute it and/or modify
    022 *          it under the terms of the GNU Lesser General Public License as
    023 *          published by the Free Software Foundation; either version 3 of the
    024 *          License, or (at your option) any later version. <br>
    025 * <br>
    026 *          This library is distributed in the hope that it will be useful, but
    027 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    028 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    029 *          Lesser General Public License for more details. <br>
    030 * <br>
    031 *          You should have received a copy of the GNU Lesser General Public
    032 *          License along with this library; if not see <a
    033 *          href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>.
    034 *
    035 */
    036public class MinimizeConflicts implements OverExpectedCriterion, OverExpectedCriterion.HasContext {
    037    private boolean iTimeConflicts = true, iSpaceConflicts = true;
    038    private OverExpectedCriterion iParent;
    039    private double iParentWeight = 0.5;
    040
    041    public MinimizeConflicts(DataProperties config) {
    042        this(config, null);
    043    }
    044
    045    public MinimizeConflicts(DataProperties config, OverExpectedCriterion parent) {
    046        iParent = parent;
    047        iTimeConflicts = config.getPropertyBoolean("OverExpected.TimeConflicts", iTimeConflicts);
    048        iSpaceConflicts = config.getPropertyBoolean("OverExpected.SpaceConflicts", iSpaceConflicts);
    049        iParentWeight = config.getPropertyDouble("OverExpected.ParentWeight", iParentWeight);
    050    }
    051
    052    @Override
    053    public double getOverExpected(Assignment<Request, Enrollment> assignment, Section section, Request request) {
    054        double penalty = 0.0;
    055        if (iSpaceConflicts && section.getLimit() == 0) {
    056            // no space in the section >> avoid
    057            penalty += 1.0;
    058        }
    059
    060        if (iTimeConflicts && !section.isAllowOverlap()) {
    061            for (Request r: request.getStudent().getRequests()) {
    062                if (request.equals(r)) break;
    063                Enrollment e = assignment.getValue(r);
    064                if (e != null && e.isCourseRequest() && section.isOverlapping(e.getAssignments())) {
    065                    // time conflict with some other already assigned section >> avoid
    066                    penalty += 1.0;
    067                }
    068            }
    069        }
    070
    071        if (penalty > 0) return penalty / section.getSubpart().getConfig().getSubparts().size();
    072        return (iParent == null ? 0.0 : iParentWeight * iParent.getOverExpected(assignment, section, request));
    073    }
    074
    075    @Override
    076    public Integer getExpected(int sectionLimit, double expectedSpace) {
    077        return (iParent == null ? null : iParent.getExpected(sectionLimit, expectedSpace));
    078    }
    079
    080    @Override
    081    public String toString() {
    082        return "min-conflict" + (iParent == null ? "" : "/" + iParent);
    083    }
    084
    085    @Override
    086    public double getOverExpected(Assignment<Request, Enrollment> assignment, Enrollment selection, Enrollment value, Set<Enrollment> conflicts) {
    087        if (selection == null || !selection.isCourseRequest()) return 0.0;
    088
    089        double penalty = 0;
    090        if (iSpaceConflicts) {
    091            for (Section section: selection.getSections()) {
    092                if (section.getLimit() == 0) {
    093                    // no space in the section >> avoid
    094                    penalty += 1.0 / selection.getSections().size();
    095                }
    096            }
    097        }
    098
    099        Request request = selection.getRequest();
    100        // only count time conflicts on the other requests
    101        if (iTimeConflicts && !request.equals(value.getRequest())) {
    102            for (Section section: selection.getSections()) {
    103                if (!section.isAllowOverlap()) {
    104                    for (Request r: request.getStudent().getRequests()) {
    105                        if (request.equals(r)) break;
    106                        Enrollment e = (value.variable().equals(r) ? value : assignment.getValue(r));
    107                        if (e != null && e.isCourseRequest() && (conflicts == null || !conflicts.contains(e)) && section.isOverlapping(e.getAssignments())) {
    108                            // time conflict with some other already assigned section >> avoid
    109                            penalty += 1.0 / selection.getSections().size();
    110                        }
    111                    }
    112                }
    113            }
    114        }
    115        // because time conflicts are only counted on other request, consider the case when the currently selected value is at the back
    116        if (iTimeConflicts && request.getPriority() < value.getRequest().getPriority() && value.isCourseRequest()) {
    117            for (Section section: value.getSections()) {
    118                if (!section.isAllowOverlap()) {
    119                    if (section.isOverlapping(selection.getSections())) {
    120                        // time conflict with some other already assigned section >> avoid
    121                        penalty += 1.0 / value.getSections().size();
    122                    }
    123                }
    124            }
    125        }
    126
    127        /*
    128        if (iTimeConflicts && !section.isAllowOverlap()) {
    129            // only count time conflicts on the other requests
    130            for (Request r: request.getStudent().getRequests()) {
    131                if (request.equals(r)) break;
    132                Enrollment e = (value.variable().equals(r) ? value : assignment.getValue(r));
    133                if (e != null && e.isCourseRequest() && (conflicts == null || !conflicts.contains(e)) && section.isOverlapping(e.getAssignments())) {
    134                    // time conflict with some other already assigned section >> avoid
    135                    penalty += 1.0;
    136                }
    137            }
    138        }
    139        */
    140
    141        if (penalty > 0) return penalty;
    142        if (iParent == null) return 0.0;
    143
    144        for (Section section: selection.getSections())
    145            penalty += iParentWeight * iParent.getOverExpected(assignment, section, request);
    146        return penalty;
    147    }
    148
    149    @Override
    150    public double getOverExpected(Assignment<Request, Enrollment> assignment, Enrollment[] enrollment, int index, Section section, Request request) {
    151        double penalty = 0;
    152        if (iSpaceConflicts && section.getLimit() == 0) {
    153            // no space in the section >> avoid
    154            //return 1.0 / section.getSubpart().getConfig().getSubparts().size();
    155            penalty += 1.0;
    156        }
    157
    158        if (iTimeConflicts && !section.isAllowOverlap()) {
    159            for (int i = 0; i < index; i++) {
    160                if (enrollment[i] == null || enrollment[i].getSections() == null || !enrollment[i].isCourseRequest()) continue;
    161                if (section.isOverlapping(enrollment[i].getSections())) {
    162                    // time conflict with some other already assigned section >> avoid
    163                    penalty += 1.0;
    164                }
    165            }
    166        }
    167
    168        if (penalty > 0) return penalty / section.getSubpart().getConfig().getSubparts().size();
    169        return (iParent == null ? 0.0 : iParentWeight * iParent.getOverExpected(assignment, section, request));
    170    }
    171}