001package net.sf.cpsolver.exam.criteria;
002
003import java.util.Collection;
004import java.util.Set;
005
006import net.sf.cpsolver.exam.criteria.additional.RoomViolation;
007import net.sf.cpsolver.exam.model.Exam;
008import net.sf.cpsolver.exam.model.ExamModel;
009import net.sf.cpsolver.exam.model.ExamPeriod;
010import net.sf.cpsolver.exam.model.ExamPlacement;
011import net.sf.cpsolver.exam.model.ExamRoom;
012import net.sf.cpsolver.exam.model.ExamRoomPlacement;
013import net.sf.cpsolver.ifs.solver.Solver;
014import net.sf.cpsolver.ifs.util.DataProperties;
015
016/**
017 * Room penalty (penalty for using given rooms). I.e., sum of
018 * {@link ExamRoomPlacement#getPenalty(ExamPeriod)} of assigned rooms.
019 * <br><br>
020 * A weight for room penalty can be set by problem property
021 * Exams.RoomPreferenceWeight, or in the input xml file, property
022 * roomWeight).
023 * 
024 * <br>
025 * 
026 * @version ExamTT 1.2 (Examination Timetabling)<br>
027 *          Copyright (C) 2008 - 2012 Tomáš Müller<br>
028 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
029 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
030 * <br>
031 *          This library is free software; you can redistribute it and/or modify
032 *          it under the terms of the GNU Lesser General Public License as
033 *          published by the Free Software Foundation; either version 3 of the
034 *          License, or (at your option) any later version. <br>
035 * <br>
036 *          This library is distributed in the hope that it will be useful, but
037 *          WITHOUT ANY WARRANTY; without even the implied warranty of
038 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
039 *          Lesser General Public License for more details. <br>
040 * <br>
041 *          You should have received a copy of the GNU Lesser General Public
042 *          License along with this library; if not see
043 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
044 */
045public class RoomPenalty extends ExamCriterion {
046    protected Integer iSoftRooms = null;
047    
048    @Override
049    public boolean init(Solver<Exam, ExamPlacement> solver) {
050        if (super.init(solver)) {
051            iSoftRooms = solver.getProperties().getPropertyInteger("Exam.SoftRooms", null);
052            if (iSoftRooms != null) {
053                RoomViolation rv = new RoomViolation();
054                getModel().addCriterion(rv);
055                return rv.init(solver);
056            }
057        }
058        return true;
059    }
060    
061    @Override
062    public String getWeightName() {
063        return "Exams.RoomWeight";
064    }
065    
066    @Override
067    public String getXmlWeightName() {
068        return "roomWeight";
069    }
070    
071    @Override
072    public double getWeightDefault(DataProperties config) {
073        return 0.1;
074    }
075    
076    @Override
077    public double getValue(ExamPlacement value, Set<ExamPlacement> conflicts) {
078        double penalty = 0.0;
079        if (value.getRoomPlacements() != null)
080            for (ExamRoomPlacement r : value.getRoomPlacements()) {
081                penalty += (iSoftRooms != null && (iSoftRooms == r.getPenalty() || iSoftRooms == r.getPenalty(value.getPeriod())) ? 0.0 : r.getPenalty(value.getPeriod()));
082            }
083        return penalty;
084    }
085    
086    private int getMinPenalty(ExamRoom r) {
087        int min = Integer.MAX_VALUE;
088        for (ExamPeriod p : ((ExamModel)getModel()).getPeriods()) {
089            if (r.isAvailable(p) && (iSoftRooms == null || r.getPenalty(p) != iSoftRooms)) {
090                min = Math.min(min, r.getPenalty(p));
091            }
092        }
093        return min;
094    }
095
096    private int getMaxPenalty(ExamRoom r) {
097        int max = Integer.MIN_VALUE;
098        for (ExamPeriod p : ((ExamModel)getModel()).getPeriods()) {
099            if (r.isAvailable(p) && (iSoftRooms == null || r.getPenalty(p) != iSoftRooms)) {
100                max = Math.max(max, r.getPenalty(p));
101            }
102        }
103        return max;
104    }
105    
106    private boolean isAvailable(ExamRoom r) {
107        for (ExamPeriod p : ((ExamModel)getModel()).getPeriods()) {
108            if (r.isAvailable(p) && (iSoftRooms == null || r.getPenalty(p) != iSoftRooms))
109                return true;
110        }
111        return false;
112    }
113
114    @Override
115    public double[] getBounds(Collection<Exam> variables) {
116        double[] bounds = new double[] { 0.0, 0.0 };
117        for (Exam exam : variables) {
118            if (!exam.getRoomPlacements().isEmpty()) {
119                int minPenalty = Integer.MAX_VALUE, maxPenalty = Integer.MIN_VALUE;
120                for (ExamRoomPlacement roomPlacement : exam.getRoomPlacements()) {
121                    if (iSoftRooms != null && iSoftRooms == roomPlacement.getPenalty()) continue;
122                    if (!isAvailable(roomPlacement.getRoom())) continue;
123                    minPenalty = Math.min(minPenalty, 2 * roomPlacement.getPenalty() + getMinPenalty(roomPlacement.getRoom()));
124                    maxPenalty = Math.max(maxPenalty, 2 * roomPlacement.getPenalty() + getMaxPenalty(roomPlacement.getRoom()));
125                }
126                bounds[0] += minPenalty;
127                bounds[1] += maxPenalty;
128            }
129        }
130        return bounds;
131    }
132    
133    @Override
134    public String toString() {
135        return "RP:" + sDoubleFormat.format(getValue());
136    }
137
138    @Override
139    public boolean isPeriodCriterion() { return false; }
140}