001package org.cpsolver.studentsct.online.selection; 002 003import java.util.List; 004 005import org.cpsolver.coursett.model.RoomLocation; 006import org.cpsolver.coursett.model.TimeLocation; 007import org.cpsolver.ifs.assignment.Assignment; 008import org.cpsolver.ifs.util.DataProperties; 009import org.cpsolver.ifs.util.ToolBox; 010import org.cpsolver.studentsct.model.Choice; 011import org.cpsolver.studentsct.model.Course; 012import org.cpsolver.studentsct.model.CourseRequest; 013import org.cpsolver.studentsct.model.Enrollment; 014import org.cpsolver.studentsct.model.Request; 015import org.cpsolver.studentsct.model.Section; 016import org.cpsolver.studentsct.weights.StudentWeights; 017 018/** 019 * Re-scheduling variant of {@link StudentWeights} model. It is based on the 020 * {@link StudentSchedulingAssistantWeights}. This model is used when a single 021 * course of a student is rescheduled. Following criteria are included: 022 * <ul> 023 * <li>StudentWeights.SameChoiceFactor .. penalization of selecting a different 024 * choice (see {@link Choice}) 025 * <li>StudentWeights.SameRoomsFactor .. penalization of selecting different 026 * room 027 * <li>StudentWeights.SameTimeFactor .. penalization of selecting different time 028 * <li>StudentWeights.SameNameFactor .. penalization of selecting different 029 * section (section name / external id does not match) 030 * </ul> 031 * 032 * @version StudentSct 1.3 (Student Sectioning)<br> 033 * Copyright (C) 2014 Tomas Muller<br> 034 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 035 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 036 * <br> 037 * This library is free software; you can redistribute it and/or modify 038 * it under the terms of the GNU Lesser General Public License as 039 * published by the Free Software Foundation; either version 3 of the 040 * License, or (at your option) any later version. <br> 041 * <br> 042 * This library is distributed in the hope that it will be useful, but 043 * WITHOUT ANY WARRANTY; without even the implied warranty of 044 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 045 * Lesser General Public License for more details. <br> 046 * <br> 047 * You should have received a copy of the GNU Lesser General Public 048 * License along with this library; if not see <a 049 * href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>. 050 * 051 */ 052public class ResectioningWeights extends StudentSchedulingAssistantWeights { 053 private double iSameChoiceFactor = 0.125; 054 private double iSameRoomsFactor = 0.007; 055 private double iSameTimeFactor = 0.070; 056 private double iSameNameFactor = 0.014; 057 private LastSectionProvider iLastSectionProvider = null; 058 059 public ResectioningWeights(DataProperties properties) { 060 super(properties); 061 iSameChoiceFactor = properties.getPropertyDouble("StudentWeights.SameChoiceFactor", iSameChoiceFactor); 062 iSameRoomsFactor = properties.getPropertyDouble("StudentWeights.SameRoomsFactor", iSameRoomsFactor); 063 iSameTimeFactor = properties.getPropertyDouble("StudentWeights.SameTimeFactor", iSameTimeFactor); 064 iSameNameFactor = properties.getPropertyDouble("StudentWeights.SameNameFactor", iSameNameFactor); 065 } 066 067 public void setLastSectionProvider(LastSectionProvider lastSectionProvider) { 068 iLastSectionProvider = lastSectionProvider; 069 } 070 071 @Override 072 public double getWeight(Assignment<Request, Enrollment> assignment, Enrollment enrollment) { 073 double weight = super.getWeight(assignment, enrollment); 074 075 if (enrollment.isCourseRequest() && enrollment.getAssignments() != null && iLastSectionProvider != null) { 076 int sameChoice = 0; 077 int sameTime = 0; 078 int sameRooms = 0; 079 int sameName = 0; 080 for (Section section : enrollment.getSections()) { 081 if (iLastSectionProvider.sameLastChoice(section)) 082 sameChoice++; 083 if (iLastSectionProvider.sameLastTime(section)) 084 sameTime++; 085 if (iLastSectionProvider.sameLastRoom(section)) 086 sameRooms++; 087 if (iLastSectionProvider.sameLastName(section, enrollment.getCourse())) 088 sameName++; 089 } 090 CourseRequest cr = (CourseRequest) enrollment.getRequest(); 091 if (sameChoice == 0 && !cr.getSelectedChoices().isEmpty()) { 092 for (Section section : enrollment.getSections()) { 093 if (cr.getSelectedChoices().contains(section.getChoice())) { 094 sameChoice++; 095 continue; 096 } 097 } 098 } 099 double size = enrollment.getAssignments().size(); 100 double sameChoiceFraction = (size - sameChoice) / size; 101 double sameTimeFraction = (size - sameTime) / size; 102 double sameRoomsFraction = (size - sameRooms) / size; 103 double sameNameFraction = (size - sameName) / size; 104 double base = getBaseWeight(assignment, enrollment); 105 weight -= sameChoiceFraction * base * iSameChoiceFactor; 106 weight -= sameTimeFraction * base * iSameTimeFactor; 107 weight -= sameRoomsFraction * base * iSameRoomsFactor; 108 weight -= sameNameFraction * base * iSameNameFactor; 109 } 110 111 return weight; 112 } 113 114 public static boolean sameChoice(Section s, Choice ch) { 115 return sameChoice(s, ch == null ? null : ch.getId()); 116 } 117 118 public static boolean sameChoice(Section s, String ch) { 119 if (s.getChoice() == null && ch == null) 120 return true; 121 if (s.getChoice() == null || ch == null) 122 return false; 123 return s.getChoice().getId().equals(ch); 124 } 125 126 public static boolean isSame(Enrollment e1, Enrollment e2) { 127 if (e1.getSections().size() != e2.getSections().size()) 128 return false; 129 s1: for (Section s1 : e1.getSections()) { 130 for (Section s2 : e2.getSections()) 131 if (sameChoice(s1, s2.getChoice())) 132 continue s1; 133 return false; 134 } 135 return true; 136 } 137 138 public static boolean sameRooms(Section s, List<RoomLocation> rooms) { 139 if (s.getRooms() == null && rooms == null) 140 return true; 141 if (s.getRooms() == null || rooms == null) 142 return false; 143 return s.getRooms().size() == rooms.size() && s.getRooms().containsAll(rooms); 144 } 145 146 public static boolean sameTime(Section s, TimeLocation t) { 147 if (s.getTime() == null && t == null) 148 return true; 149 if (s.getTime() == null || t == null) 150 return false; 151 return s.getTime().getStartSlot() == t.getStartSlot() && s.getTime().getLength() == t.getLength() 152 && s.getTime().getDayCode() == t.getDayCode() 153 && ToolBox.equals(s.getTime().getDatePatternName(), t.getDatePatternName()); 154 } 155 156 public static boolean sameName(Long courseId, Section s1, Section s2) { 157 return s1.getName(courseId).equals(s2.getName(courseId)); 158 } 159 160 /** 161 * Compare the old assignment with the current one 162 */ 163 public static interface LastSectionProvider { 164 /** 165 * Check the choice (see {@link Choice}) 166 * 167 * @param current 168 * current section 169 * @return true if the choice matches 170 */ 171 public boolean sameLastChoice(Section current); 172 173 /** 174 * Check the time 175 * 176 * @param current 177 * current section 178 * @return true if the time matches 179 */ 180 public boolean sameLastTime(Section current); 181 182 /** 183 * Check the room 184 * 185 * @param current 186 * current section 187 * @return true if the room matches 188 */ 189 public boolean sameLastRoom(Section current); 190 191 /** 192 * Check section name (external id) 193 * 194 * @param current 195 * current section 196 * @param course 197 * current course 198 * @return true if the section name matches 199 */ 200 public boolean sameLastName(Section current, Course course); 201 } 202}