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