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