001package org.cpsolver.coursett.neighbourhoods; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.List; 006import java.util.Map; 007import java.util.Set; 008import java.util.concurrent.locks.Lock; 009 010import org.cpsolver.coursett.model.Lecture; 011import org.cpsolver.coursett.model.Placement; 012import org.cpsolver.coursett.model.RoomLocation; 013import org.cpsolver.coursett.model.TimetableModel; 014import org.cpsolver.ifs.algorithms.neighbourhoods.RandomSwapMove; 015import org.cpsolver.ifs.assignment.Assignment; 016import org.cpsolver.ifs.model.Neighbour; 017import org.cpsolver.ifs.model.SimpleNeighbour; 018import org.cpsolver.ifs.solution.Solution; 019import org.cpsolver.ifs.solver.Solver; 020import org.cpsolver.ifs.util.DataProperties; 021import org.cpsolver.ifs.util.JProf; 022import org.cpsolver.ifs.util.ToolBox; 023 024 025/** 026 * Try to assign a class with a new room. A class is selected randomly, a 027 * different room is randomly selected for the class -- the class is 028 * assigned into the new room. If the room is used or there is some other conflict, 029 * it tries to resolve these conflicts by assigning conflicting classes to other 030 * rooms as well. 031 * <br> 032 * 033 * @version IFS 1.3 (Iterative Forward Search)<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 050 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 051 */ 052public class RoomSwap extends RandomSwapMove<Lecture, Placement> { 053 054 public RoomSwap(DataProperties config) { 055 super(config); 056 } 057 058 @Override 059 public void init(Solver<Lecture, Placement> solver) { 060 } 061 062 @Override 063 public Neighbour<Lecture, Placement> selectNeighbour(Solution<Lecture, Placement> solution) { 064 TimetableModel model = (TimetableModel)solution.getModel(); 065 Assignment<Lecture, Placement> assignment = solution.getAssignment(); 066 double total = model.getTotalValue(assignment); 067 int varIdx = ToolBox.random(model.variables().size()); 068 for (int i = 0; i < model.variables().size(); i++) { 069 Lecture lecture = model.variables().get((i + varIdx) % model.variables().size()); 070 Placement old = assignment.getValue(lecture); 071 if (old == null || old.getNrRooms() != 1) continue; 072 073 List<RoomLocation> values = lecture.roomLocations(); 074 if (values.isEmpty()) continue; 075 076 Lock lock = solution.getLock().writeLock(); 077 lock.lock(); 078 try { 079 int attempts = 0; 080 int valIdx = ToolBox.random(values.size()); 081 long startTime = JProf.currentTimeMillis(); 082 for (int j = 0; j < values.size(); j++) { 083 RoomLocation room = values.get((j + valIdx) % values.size()); 084 if (room.getPreference() > 50) continue; 085 if (room.equals(old.getRoomLocation())) continue; 086 087 Placement placement = new Placement(lecture, old.getTimeLocation(), room); 088 if (!placement.isValid()) continue; 089 090 Set<Placement> conflicts = model.conflictValues(assignment, placement); 091 if (conflicts.contains(placement)) continue; 092 if (conflicts.isEmpty()) { 093 SimpleNeighbour<Lecture, Placement> n = new SimpleNeighbour<Lecture, Placement>(lecture, placement); 094 if (!iHC || n.value(assignment) <= 0) return n; 095 else continue; 096 } 097 098 Map<Lecture, Placement> assignments = new HashMap<Lecture, Placement>(); 099 assignments.put(lecture, placement); 100 101 for (Placement conflict: conflicts) 102 assignment.unassign(solution.getIteration(), conflict.variable()); 103 assignment.assign(solution.getIteration(), placement); 104 105 Double v = resolve(solution, total, startTime, assignments, new ArrayList<Placement>(conflicts), 0); 106 if (!conflicts.isEmpty()) 107 attempts ++; 108 109 assignment.unassign(solution.getIteration(), lecture); 110 for (Placement conflict: conflicts) 111 assignment.assign(solution.getIteration(), conflict); 112 assignment.assign(solution.getIteration(), old); 113 114 if (v != null) 115 return new SwapNeighbour(assignments.values(), v); 116 117 if (attempts >= iMaxAttempts) break; 118 } 119 } finally { 120 lock.unlock(); 121 } 122 } 123 return null; 124 } 125 126 127 @Override 128 public Double resolve(Solution<Lecture, Placement> solution, double total, long startTime, Map<Lecture, Placement> assignments, List<Placement> conflicts, int index) { 129 Assignment<Lecture, Placement> assignment = solution.getAssignment(); 130 131 if (index == conflicts.size()) return solution.getModel().getTotalValue(assignment) - total; 132 Placement conflict = conflicts.get(index); 133 Lecture variable = conflict.variable(); 134 135 if (conflict.getNrRooms() != 1) return null; 136 137 List<RoomLocation> values = variable.roomLocations(); 138 if (values.isEmpty()) return null; 139 140 int valIdx = ToolBox.random(values.size()); 141 int attempts = 0; 142 for (int i = 0; i < values.size(); i++) { 143 RoomLocation room = values.get((i + valIdx) % values.size()); 144 if (room.getPreference() > 50) continue; 145 if (room.equals(conflict.getRoomLocation())) continue; 146 147 Placement value = new Placement(variable, conflict.getTimeLocation(), room); 148 if (!value.isValid() || solution.getModel().inConflict(assignment, value)) continue; 149 150 assignment.assign(solution.getIteration(), value); 151 Double v = resolve(solution, total, startTime, assignments, conflicts, 1 + index); 152 assignment.unassign(solution.getIteration(), variable); 153 attempts ++; 154 155 if (v != null && (!iHC || v <= 0)) { 156 assignments.put(variable, value); 157 return v; 158 } 159 if (attempts >= iMaxAttempts || isTimeLimitReached(startTime)) break; 160 } 161 162 return null; 163 } 164}