/*
* Copyright 2009 Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package net.sf.cpsolver.tt;
import de.timefinder.algo.Algorithm;
import de.timefinder.algo.AlgorithmCondition;
import de.timefinder.data.algo.DataPoolSettings;
import de.timefinder.algo.MyStatusBar;
import de.timefinder.algo.util.BiMap;
import de.timefinder.data.DataPool;
import de.timefinder.data.Event;
import de.timefinder.data.Location;
import de.timefinder.data.Person;
import de.timefinder.data.access.Dao;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javolution.util.FastMap;
import net.sf.cpsolver.ifs.model.Variable;
import net.sf.cpsolver.ifs.solution.Solution;
import net.sf.cpsolver.ifs.solver.Solver;
import net.sf.cpsolver.ifs.termination.TerminationCondition;
import net.sf.cpsolver.ifs.util.DataProperties;
import net.sf.cpsolver.ifs.util.Progress;
import net.sf.cpsolver.ifs.util.Progress.Message;
import net.sf.cpsolver.ifs.util.ProgressListener;
import net.sf.cpsolver.ifs.util.ToolBox;
/**
* @author Peter Karich, peat_hal 'at' users 'dot' sourceforge 'dot' net
*/
public class UniTimeOptimization implements Algorithm {
private static java.text.DecimalFormat doubleFormat =
new java.text.DecimalFormat("0.000", new java.text.DecimalFormatSymbols(Locale.US));
private Map<UniActivity, Event> eventForActivity = FastMap.newInstance();
private BiMap<Location, UniResource> locToRoom = new BiMap();
private Logger logger = Logger.getLogger(getClass().getSimpleName());
private DataPoolSettings dataPoolSettings;
private DataPool dataPool;
private MyStatusBar statusBar;
private boolean initialAssignment;
private AlgorithmCondition condition;
private int slotsPerDay;
public UniTimeOptimization() {
}
public void setInitialAssignment(boolean initialAssignment) {
this.initialAssignment = initialAssignment;
}
public UniTimeSimpleModel createModel() {
UniTimeSimpleModel model = new UniTimeSimpleModel(dataPoolSettings.getNumberOfDays(),
dataPoolSettings.getTimeslotsPerDay());
slotsPerDay = dataPoolSettings.getTimeslotsPerDay();
int nrDays = dataPoolSettings.getNumberOfDays();
Dao<Location> locationDao = dataPool.getDao(Location.class);
Dao<Person> personDao = dataPool.getDao(Person.class);
Dao<Event> eventDao = dataPool.getDao(Event.class);
int nrRooms = locationDao.getAll().size();
UniResource rooms[] = new UniResource[nrRooms];
int ii = 0;
for (Location tfLoc : locationDao.getAll()) {
rooms[ii] = new UniResource("" + tfLoc.getId(), UniResource.TYPE_ROOM, tfLoc.getName());
locToRoom.put(tfLoc, rooms[ii]);
model.addConstraint(rooms[ii]);
ii++;
}
// avoid creating a Vector each time in UniActivity.addResourceGroup
List<UniResource> allRooms = new ArrayList();
for (int j = 0; j < rooms.length; j++) {
allRooms.add(rooms[j]);
}
//TODO GroupsOfRooms
// Vector groupOfRooms[] = new Vector[nrGroupsOfRooms];
// for (int i = 0; i < nrGroupsOfRooms; i++) {
// groupOfRooms[i] = new FastVector();
//
// groupOfRooms[i].add(rooms[r]);
// groupForRoom[r].add(groupOfRooms[i]);
// }
//
// for (int i = 0; i < nrLocations; i++) {
// int cnt = 0;
// for (int j = 0; j < nrGroupsOfRooms; j++) {
// if (groupOfRooms[j].contains(rooms[i]))
// cnt++;
// }
// while (cnt < nrRoomInGroupMin) {
// groupOfRooms[r].add(rooms[i]);
// groupForRoom[i].add(groupOfRooms[r]);
// cnt++;
// }
// }
int nrTeachers = personDao.getAll().size();
UniResource instructors[] = new UniResource[nrTeachers];
Map<Person, UniResource> personToResource = FastMap.newInstance();
ii = 0;
for (Person p : personDao.getAll()) {
instructors[ii] = new UniResource(p.getId() + "",
UniResource.TYPE_INSTRUCTOR, p.getName());
model.addConstraint(instructors[ii]);
personToResource.put(p, instructors[ii]);
ii++;
}
List<Integer> starts = new ArrayList();
Map<UniActivity, UniResource> activityToInitialRoom = FastMap.newInstance();
for (Event event : eventDao.getAll()) {
UniActivity activity = new UniActivity(event.getDuration(), ""
+ event.getId(), event.getName());
eventForActivity.put(activity, event);
starts.add(event.getStart());
UniResource roomRes = null;
if (event.getLocation() != null)
roomRes = locToRoom.get(event.getLocation());
if (roomRes == null) {
int room = ToolBox.random(nrRooms);
activityToInitialRoom.put(activity, rooms[room]);
} else
activityToInitialRoom.put(activity, roomRes);
activity.addResourceGroup(allRooms);
for (Person p : event.getPersons()) {
UniResource res = personToResource.get(p);
if (res == null)
throw new NullPointerException("cannot find activity for:" + p.getName());
activity.addResourceGroup(res);
}
// activity.addResourceGroup(classes[aclass]);
model.addVariable(activity);
}
// specify dicourage slots
for (int i = 0; i < model.variables().size(); i++) {
UniActivity activity = (UniActivity) model.variables().elementAt(i);
for (int day = 0; day < nrDays; day++) {
for (int hour = 0; hour < slotsPerDay; hour++) {
addDiscouragedSlot(activity, day, hour, slotsPerDay);
}
}
// create values/assigments
activity.init();
}
for (int i = 0; i < model.variables().size(); i++) {
UniActivity activity = (UniActivity) model.variables().elementAt(i);
if (initialAssignment) {
int start = starts.get(i);
UniResource room = activityToInitialRoom.get(activity);
// find value/assignment with specific room and slot
UniAssignment assignment = null;
for (Enumeration e = activity.values().elements(); e.hasMoreElements();) {
UniAssignment ass = (UniAssignment) e.nextElement();
if (ass.getSlot() == start && ass.getResource(0) == room) {
assignment = ass;
break;
}
}
if (assignment != null) {
Set conflicts = model.conflictValues(assignment);
if (!conflicts.isEmpty()) {
logger.warning("Unable to assign " + assignment.getName()
+ " to " + activity.getName());
} else {
activity.assign(0, assignment);
activity.setInitialAssignment(assignment);
}
activity.setInitialAssignment(assignment);
} else {
// initial assignment fails
logger.warning("Unable to assign " + activity.getName()
+ " -- no assignment matched. slot=" + start + " room=" + room.getName());
}
}
}
logger.info("Total number of " + model.variables().size() + " activities generated.");
return model;
}
public void start() throws InterruptedException {
DataProperties properties = new DataProperties();
properties.setProperty("General.Seed", "123321");
UniTimeSimpleModel model = createModel();
Solver solver = new Solver(properties);
condition.init();
solver.setTerminalCondition(new TerminationCondition() {
@Override
public boolean canContinue(Solution sltn) {
return condition.canContinue() && !statusBar.getMyProgressMonitor().isCanceled();
}
});
Progress p = new Progress();
p.addProgressListener(new ProgressListener() {
private String status;
@Override
public void statusChanged(String status) {
this.status = status;
}
@Override
public void phaseChanged(String phase) {
}
@Override
public void progressChanged(long currentProgress, long maxProgress) {
statusBar.setMessage(status + " " + currentProgress * 100f / maxProgress);
}
@Override
public void progressSaved() {
}
@Override
public void progressRestored() {
}
@Override
public void progressMessagePrinted(Message message) {
statusBar.setMessage(message.getMessage());
}
});
solver.setProgress(p);
solver.setInitalSolution(model);
solver.start();
solver.getSolverThread().join();
if (solver.lastSolution().getBestInfo() == null)
logger.severe("No solution found :-(");
logger.info("Last solution:" + solver.lastSolution().getInfo());
logger.info("Best solution:" + solver.lastSolution().getBestInfo());
Solution best = solver.lastSolution();
best.restoreBest();
int value = 0;
for (Enumeration iv = best.getModel().assignedVariables().elements(); iv.hasMoreElements();) {
value += (int) ((Variable) iv.nextElement()).getAssignment().toDouble();
}
logger.info("Last solution:" + best.getInfo());
logger.info(doubleFormat.format(properties.getPropertyDouble("Generator.FillFactor", 0.0)) + ";"
+ doubleFormat.format(properties.getPropertyInt("Generator.NrRooms", 0)) + ";");
logger.info(doubleFormat.format(best.getTime()) + ";" + best.getIteration() + ";"
+ doubleFormat.format(((double) best.getIteration()) / best.getTime()) + ";");
logger.info(best.getModel().assignedVariables().size() + ";"
+ doubleFormat.format(100.0 * best.getModel().assignedVariables().size()
/ best.getModel().variables().size()) + ";" + value);
logger.info(" time: " + doubleFormat.format(best.getTime()) + " s");
logger.info(" iteration: " + best.getIteration());
logger.info(" speed: " + doubleFormat.format(((double) best.getIteration())
/ best.getTime()) + " it/s");
logger.info(" assigned: " + best.getModel().assignedVariables().size()
+ " (" + doubleFormat.format(100.0 * best.getModel().assignedVariables().size()
/ best.getModel().variables().size()) + "%)");
logger.info(" value: " + value);
// remove previous assignments
for (Location loc : locToRoom.keySet()) {
for (Event ev : loc.getEvents()) {
ev.setLocation(null);
}
loc.setEvents(null);
}
for (Object obj : model.variables()) {
UniActivity activity = (UniActivity) obj;
UniAssignment ass = (UniAssignment) activity.getBestAssignment();
if (ass == null) {
logger.severe("No best assignment found for activity:" + activity.getName());
continue;
}
Event event = eventForActivity.get(activity);
if (event == null) {
logger.severe("No event found for activity:" + activity.getName());
continue;
}
event.setStart(ass.getSlot());
Location tfLoc = locToRoom.getSecond(ass.getResource(0));
if (tfLoc != null) {
event.setLocation(tfLoc);
} else
logger.warning("No TF-room found for activity:" + activity.getName());
}
}
@Override
public de.timefinder.data.algo.Solution doWork() {
try {
start();
} catch (Exception ex) {
statusBar.setErrorMessage(ex.getLocalizedMessage());
logger.log(Level.SEVERE, "", ex);
}
// TODO return real solution
return new de.timefinder.data.algo.Solution();
}
@Override
public void setCondition(AlgorithmCondition condition) {
this.condition = condition;
}
@Override
public void setStatusBar(MyStatusBar bar) {
this.statusBar = bar;
}
@Override
public void setDataPool(DataPool pool) {
this.dataPool = pool;
}
@Override
public DataPool getDataPool() {
return dataPool;
}
@Override
public void setDataPoolSettings(DataPoolSettings settings) {
this.dataPoolSettings = settings;
}
protected void addDiscouragedSlot(UniActivity activity, int day, int hour, int slotsPerDay) {
if (hour >= slotsPerDay / 2) {
Event ev = eventForActivity.get(activity);
float w = hour;
if (ev != null)
w += ev.getPersons().size();
activity.addDiscourage(slotsPerDay * day + hour, w);
}
}
}