001 package net.sf.cpsolver.coursett.constraint; 002 003 import java.util.Collection; 004 import java.util.Collections; 005 import java.util.Comparator; 006 import java.util.Enumeration; 007 import java.util.HashSet; 008 import java.util.Hashtable; 009 import java.util.Iterator; 010 import java.util.Set; 011 import java.util.Vector; 012 013 import net.sf.cpsolver.coursett.Constants; 014 import net.sf.cpsolver.coursett.model.Lecture; 015 import net.sf.cpsolver.coursett.model.Placement; 016 import net.sf.cpsolver.coursett.model.RoomLocation; 017 import net.sf.cpsolver.ifs.model.Constraint; 018 import net.sf.cpsolver.ifs.model.Value; 019 import net.sf.cpsolver.ifs.model.Variable; 020 import net.sf.cpsolver.ifs.util.DataProperties; 021 022 /** Minimize number of used rooms within the set of classes. 023 * <br><br> 024 * This constraint implements the following distribution/group constraint: 025 * <br><br> 026 * MIN_ROOM_USE (Minimize Number Of Rooms Used)<br> 027 * Minimize number of rooms used by the given set of classes. 028 * 029 * @version 030 * CourseTT 1.1 (University Course Timetabling)<br> 031 * Copyright (C) 2006 Tomáš Müller<br> 032 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 033 * Lazenska 391, 76314 Zlin, Czech Republic<br> 034 * <br> 035 * This library is free software; you can redistribute it and/or 036 * modify it under the terms of the GNU Lesser General Public 037 * License as published by the Free Software Foundation; either 038 * version 2.1 of the License, or (at your option) any later version. 039 * <br><br> 040 * This library is distributed in the hope that it will be useful, 041 * but WITHOUT ANY WARRANTY; without even the implied warranty of 042 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 043 * Lesser General Public License for more details. 044 * <br><br> 045 * You should have received a copy of the GNU Lesser General Public 046 * License along with this library; if not, write to the Free Software 047 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 048 */ 049 050 public class MinimizeNumberOfUsedRoomsConstraint extends Constraint implements WeakeningConstraint { 051 private int iUnassignmentsToWeaken = 250; 052 private long iUnassignment = 0; 053 private int iLimit = 1; 054 private Hashtable iUsedRooms = new Hashtable(); 055 boolean iEnabled = false; 056 057 public MinimizeNumberOfUsedRoomsConstraint(DataProperties config) { 058 iUnassignmentsToWeaken = config.getPropertyInt("MinimizeNumberOfUsedRooms.Unassignments2Weaken", iUnassignmentsToWeaken); 059 } 060 061 public boolean isOverLimit(Value value) { 062 return getOverLimit(value)>0; 063 } 064 065 public int getOverLimit(Value value) { 066 if (!iEnabled) return 0; //not enabled 067 if (iUnassignmentsToWeaken==0) return 0; //not working 068 069 Placement placement = (Placement) value; 070 Lecture lecture = (Lecture)placement.variable(); 071 072 if (lecture.getNrRooms()<=0) return 0; //no room 073 if (lecture.roomLocations().size()==lecture.getNrRooms()) return 0; //required room 074 if (lecture.isCommitted()) return 0; //commited class 075 076 int usage = iUsedRooms.size(); 077 if (usage+lecture.getNrRooms()<=iLimit) return 0; //under the limit, quick check 078 079 if (placement.isMultiRoom()) { 080 HashSet assignedRooms = new HashSet(); 081 if (lecture.getAssignment()!=null) 082 assignedRooms.addAll(((Placement)lecture.getAssignment()).getRoomLocations()); 083 for (Enumeration e=placement.getRoomLocations().elements();e.hasMoreElements();) { 084 RoomLocation r = (RoomLocation)e.nextElement(); 085 if (assignedRooms.remove(r)) continue; 086 Integer usageThisRoom = (Integer)iUsedRooms.get(r); 087 if (!iUsedRooms.containsKey(r)) usage++; 088 } 089 for (Iterator i=assignedRooms.iterator();i.hasNext();) { 090 RoomLocation r = (RoomLocation)i.next(); 091 Set lects = (Set)iUsedRooms.get(r); 092 if (lects!=null && lects.size()==1) usage--; 093 } 094 } else { 095 RoomLocation assignedRoom = (lecture.getAssignment()!=null && !lecture.getAssignment().equals(placement)? ((Placement)lecture.getAssignment()).getRoomLocation() : null); 096 RoomLocation room = placement.getRoomLocation(); 097 if (!room.equals(assignedRoom)) { 098 if (!iUsedRooms.containsKey(room)) usage++; 099 if (assignedRoom!=null) { 100 Set lects = (Set)iUsedRooms.get(assignedRoom); 101 if (lects!=null && lects.size()==1) usage--; 102 } 103 } 104 } 105 if (usage<=iUsedRooms.size()) return 0; //number of used rooms not changed 106 if (usage<=iLimit) return 0; //under the limit 107 return usage-iLimit; 108 } 109 110 public void computeConflicts(Value value, Set conflicts) { 111 int overLimit = getOverLimit(value); 112 if (overLimit>0) { 113 Vector adepts = new Vector(); 114 for (Enumeration e=iUsedRooms.elements();e.hasMoreElements();) { 115 Set lects = (Set)e.nextElement(); 116 Vector placementsToUnassign = new Vector(); 117 boolean canUnassign = true; 118 for (Iterator i=lects.iterator();i.hasNext();) { 119 Lecture l = (Lecture)i.next(); 120 if (l.isCommitted()) { 121 canUnassign = false; break; 122 } 123 if (!conflicts.contains(l.getAssignment())) 124 placementsToUnassign.addElement(l.getAssignment()); 125 } 126 if (!canUnassign) continue; 127 adepts.addElement(placementsToUnassign); 128 } 129 if (adepts.size()<overLimit) { 130 conflicts.add(value); 131 } else { 132 Collections.sort(adepts, new Comparator() { 133 public int compare(Object o1, Object o2) { 134 Collection c1 = (Collection)o1; 135 Collection c2 = (Collection)o2; 136 return Double.compare(c1.size(), c2.size()); 137 } 138 }); 139 for (int i=0;i<overLimit;i++) { 140 conflicts.addAll((Collection)adepts.elementAt(i)); 141 } 142 } 143 } 144 } 145 146 public boolean inConflict(Value value) { 147 return isOverLimit(value); 148 } 149 150 public boolean isConsistent(Value value1, Value value2) { 151 return (isOverLimit(value1) || isOverLimit(value2)); 152 } 153 154 public void assigned(long iteration, Value value) { 155 super.assigned(iteration, value); 156 Placement placement = (Placement) value; 157 Lecture lecture = (Lecture)placement.variable(); 158 if (lecture.getNrRooms()<=0) return; 159 if (placement.isMultiRoom()) { 160 for (Enumeration e=placement.getRoomLocations().elements();e.hasMoreElements();) { 161 RoomLocation r = (RoomLocation)e.nextElement(); 162 Set lects = (Set)iUsedRooms.get(r); 163 if (lects==null) { 164 lects = new HashSet(); 165 iUsedRooms.put(r, lects); 166 } 167 lects.add(lecture); 168 } 169 } else { 170 RoomLocation r = placement.getRoomLocation(); 171 Set lects = (Set)iUsedRooms.get(r); 172 if (lects==null) { 173 lects = new HashSet(); 174 iUsedRooms.put(r, lects); 175 } 176 lects.add(lecture); 177 } 178 } 179 180 public void unassigned(long iteration, Value value) { 181 super.unassigned(iteration, value); 182 Placement placement = (Placement) value; 183 Lecture lecture = (Lecture)placement.variable(); 184 if (lecture.getNrRooms()<=0) return; 185 if (placement.isMultiRoom()) { 186 for (Enumeration e=placement.getRoomLocations().elements();e.hasMoreElements();) { 187 RoomLocation r = (RoomLocation)e.nextElement(); 188 Set lects = (Set)iUsedRooms.get(r); 189 if (lects!=null) { 190 lects.remove(lecture); 191 if (lects.isEmpty()) 192 iUsedRooms.remove(r); 193 } 194 } 195 } else { 196 RoomLocation r = placement.getRoomLocation(); 197 Set lects = (Set)iUsedRooms.get(r); 198 if (lects!=null) { 199 lects.remove(lecture); 200 if (lects.isEmpty()) 201 iUsedRooms.remove(r); 202 } 203 } 204 } 205 206 public void weaken() { 207 iUnassignment++; 208 if (iUnassignmentsToWeaken>0 && iUnassignment%iUnassignmentsToWeaken==0) iLimit++; 209 } 210 211 public String getName() { return "Minimize number of used rooms"; } 212 213 public int estimateLimit() { 214 HashSet mandatoryRooms = new HashSet(); 215 for (Enumeration e=variables().elements();e.hasMoreElements();) { 216 Lecture lecture = (Lecture)e.nextElement(); 217 if (lecture.getNrRooms()==0) continue; 218 if (lecture.isCommitted() || lecture.roomLocations().size()==1) 219 mandatoryRooms.addAll(lecture.roomLocations()); 220 } 221 double histogram[][] = new double[Constants.SLOTS_PER_DAY][Constants.NR_DAYS_WEEK]; 222 for (int i=0;i<Constants.SLOTS_PER_DAY_NO_EVENINGS;i++) 223 for (int j=0;j<Constants.NR_DAYS_WEEK;j++) 224 histogram[i][j]=0.0; 225 for (Enumeration e=variables().elements();e.hasMoreElements();) { 226 Lecture lecture = (Lecture)e.nextElement(); 227 if (lecture.getNrRooms()==0) continue; 228 Vector values = lecture.values(); 229 Placement firstPlacement = (values.isEmpty()?null:(Placement)values.firstElement()); 230 for (Enumeration e2=lecture.values().elements();e2.hasMoreElements();) { 231 Placement p = (Placement)e2.nextElement(); 232 int firstSlot = p.getTimeLocation().getStartSlot(); 233 if (firstSlot>Constants.DAY_SLOTS_LAST) continue; 234 int endSlot = firstSlot+p.getTimeLocation().getNrSlotsPerMeeting()-1; 235 if (endSlot<Constants.DAY_SLOTS_FIRST) continue; 236 for (int i=firstSlot;i<=endSlot;i++) { 237 int dayCode = p.getTimeLocation().getDayCode(); 238 for (int j=0;j<Constants.NR_DAYS_WEEK;j++) { 239 if ((dayCode & Constants.DAY_CODES[j])!=0) { 240 histogram[i][j] += ((double)lecture.getNrRooms()) / values.size(); 241 } 242 } 243 } 244 } 245 } 246 int maxAverageRooms = 0; 247 for (int i=0;i<Constants.SLOTS_PER_DAY_NO_EVENINGS;i++) 248 for (int j=0;j<Constants.NR_DAYS_WEEK;j++) 249 maxAverageRooms = Math.max(maxAverageRooms, (int)Math.ceil(histogram[i][j])); 250 return Math.max(1, Math.max(mandatoryRooms.size(), maxAverageRooms)); 251 } 252 253 public void setEnabled(boolean enabled) { 254 iEnabled = enabled; 255 iLimit = Math.max(iUsedRooms.size(), estimateLimit()); 256 } 257 public boolean isEnabled() { return iEnabled; } 258 259 public String toString() { 260 StringBuffer sb = new StringBuffer(); 261 sb.append("Minimize Number Of Rooms Used between "); 262 for (Enumeration e=variables().elements();e.hasMoreElements();) { 263 Variable v = (Variable)e.nextElement(); 264 sb.append(v.getName()); 265 if (e.hasMoreElements()) sb.append(", "); 266 } 267 return sb.toString(); 268 } 269 }