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}