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}