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}