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}