001package org.cpsolver.coursett.constraint;
002
003import org.cpsolver.coursett.Constants;
004import org.cpsolver.coursett.criteria.DepartmentBalancingPenalty;
005import org.cpsolver.coursett.model.Lecture;
006import org.cpsolver.coursett.model.Placement;
007import org.cpsolver.ifs.criteria.Criterion;
008import org.cpsolver.ifs.util.DataProperties;
009
010/**
011 * Departmental ballancing constraint. <br>
012 * <br>
013 * The new implementation of the balancing times for departments works as
014 * follows: Initially, there is a histogram for each department computed. For
015 * each time slot, it says how many placements of all classes (of the
016 * department) include this time slot. Each such placement has the weight of 1 /
017 * number of placements of the class, so the total sum of all values in the
018 * histogram (i.e., for all time slots) is equal to the total sum of half-hours
019 * required by the given set of classes. <br>
020 * <br>
021 * On the other hand, each class splits its number of half-hours (e.g., 2x50 has
022 * 4 half-hours, "4 points") into the time slots which it can occupy, according
023 * to the frequencies of the utilization of each time slots (i.e., number of
024 * placements containing the time slots divided by the number of all placements
025 * of the class). <br>
026 * <br>
027 * For example, a histogram for department 1286:<pre><code>
028 * 1: [0.10,0.00,0.10,0.00,0.10] &larr; 7:30 [Mon, Tue, Wed, Thu, Fri]
029 * 2: [0.10,0.00,0.10,0.00,0.10] &larr; 8:00 [Mon, Tue, Wed, Thu, Fri]
030 * 3: [0.35,0.62,0.48,0.62,0.10] ... and so on
031 * 4: [0.35,0.62,0.48,0.62,0.10]
032 * 5: [1.35,1.12,1.48,0.12,1.10]
033 * 6: [1.35,1.12,1.48,0.12,1.10]
034 * 7: [0.35,0.62,0.48,1.63,0.10]
035 * 8: [0.35,0.62,0.48,1.63,0.10]
036 * 9: [0.35,0.12,0.48,0.12,0.10]
037 * 10:[0.35,0.12,0.48,0.12,0.10]
038 * 11:[0.35,0.12,0.48,0.12,0.10]
039 * 12:[0.35,0.12,0.48,0.12,0.10]
040 * 13:[0.35,0.12,0.48,1.12,0.10]
041 * 14:[0.35,0.12,0.48,1.12,0.10]
042 * 15:[0.35,0.12,0.48,0.12,0.10]
043 * 16:[0.35,0.12,0.48,0.12,0.10]
044 * 17:[0.35,0.12,0.48,0.12,0.10]
045 * 18:[0.35,0.12,0.48,0.12,0.10]
046 * 19:[0.10,0.00,0.10,0.00,0.10]
047 * 20:[0.10,0.00,0.10,0.00,0.10]
048 * 21:[0.00,0.00,0.00,0.00,0.00]
049 * </code></pre>
050 * You can easily see, that the time slots which are prohibited for all of the
051 * classes of the department have zero values, also some time slots are used
052 * much often than the others. Note that there are no preferences involved in
053 * this computation, only prohibited / not used times are less involved. <br>
054 * <br>
055 * The idea is to setup the initial limits for each of the time slots according
056 * to the above values. The reason for doing so is to take the requirements
057 * (time patterns, required/prohibited times) of all classes of the department
058 * into account. For instance, take two classes A and B of type MWF 2x100 with
059 * two available locations starting from 7:30 and 8:30. Note that the time slot
060 * Monday 8:30-9:00 will be always used by both of the classes and for instance
061 * the time slot Monday 7:30-8:00 (or Friday 9:30-10:00) can be used by none of
062 * them, only one of them or both of them. From the balancing point of the view,
063 * I believe it should be preferred to have one class starting from 7:30 and the
064 * other one from 8:30. <br>
065 * <br>
066 * So, after the histogram is computed, its values are increased by the given
067 * percentage (same reason as before, to allow some space between the actual
068 * value and the limit, also not to make the limits for a department with 21
069 * time slots less strict than a department with 20 time slots etc.). The
070 * initial limits are than computed as these values rounded upwards (1.90
071 * results in 2). <br>
072 * <br>
073 * Moreover, the value is increased only when the histogram value of a time slot
074 * is below the following value: spread factor * (number of used time slots /
075 * number of all time slots). Is assures a department of at least the given
076 * percentage more time slots than required, but does not provide an additional
077 * reward for above average use of time slots based on 'required' times. <br>
078 * <br>
079 * For example, the department 1286 will have the following limits (histogram
080 * increased by 20% (i.e., each value is 20% higher) and rounded upwards):
081 * <pre><code>
082 * 1: [1,0,1,0,1]
083 * 2: [1,0,1,0,1]
084 * 3: [1,1,1,1,1]
085 * 4: [1,1,1,1,1]
086 * 5: [2,2,2,1,2]
087 * 6: [2,2,2,1,2]
088 * 7: [1,1,1,2,1]
089 * 8: [1,1,1,2,1]
090 * 9: [1,1,1,1,1]
091 * 10:[1,1,1,1,1]
092 * 11:[1,1,1,1,1]
093 * 12:[1,1,1,1,1]
094 * 13:[1,1,1,2,1]
095 * 14:[1,1,1,2,1]
096 * 15:[1,1,1,1,1]
097 * 16:[1,1,1,1,1]
098 * 17:[1,1,1,1,1]
099 * 18:[1,1,1,1,1]
100 * 19:[1,0,1,0,1]
101 * 20:[1,0,1,0,1]
102 * 21:[0,0,0,0,0]
103 * </code></pre>
104 * The maximal penalty (i.e., the maximal number of half-hours which can be used
105 * above the pre-computed limits by a department) of a constraint is used.
106 * Initially, the maximal penalty is set to zero. It is increased by one after
107 * each time when the constraint causes the given number (e.g., 100) of
108 * un-assignments. <br>
109 * <br>
110 * Also, the balancing penalty (the total number of half-hours over the initial
111 * limits) is computed and it can be minimized during the search (soft
112 * constraint). <br>
113 * <br>
114 * Parameters:
115 * <table border='1' summary='Related Solver Parameters'>
116 * <tr>
117 * <th>Parameter</th>
118 * <th>Type</th>
119 * <th>Comment</th>
120 * </tr>
121 * <tr>
122 * <td>DeptBalancing.SpreadFactor</td>
123 * <td>{@link Double}</td>
124 * <td>Initial allowance of the slots for a particular time (factor)<br>
125 * Allowed slots = ROUND(SpreadFactor * (number of requested slots / number of
126 * slots per day))</td>
127 * </tr>
128 * <tr>
129 * <td>DeptBalancing.Unassignments2Weaken</td>
130 * <td>{@link Integer}</td>
131 * <td>Increase the initial allowance when it causes the given number of
132 * unassignments</td>
133 * </tr>
134 * </table>
135 * 
136 * @version CourseTT 1.3 (University Course Timetabling)<br>
137 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
138 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
139 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
140 * <br>
141 *          This library is free software; you can redistribute it and/or modify
142 *          it under the terms of the GNU Lesser General Public License as
143 *          published by the Free Software Foundation; either version 3 of the
144 *          License, or (at your option) any later version. <br>
145 * <br>
146 *          This library is distributed in the hope that it will be useful, but
147 *          WITHOUT ANY WARRANTY; without even the implied warranty of
148 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
149 *          Lesser General Public License for more details. <br>
150 * <br>
151 *          You should have received a copy of the GNU Lesser General Public
152 *          License along with this library; if not see
153 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
154 */
155
156public class DepartmentSpreadConstraint extends SpreadConstraint {
157    private Long iDepartment = null;
158
159    public DepartmentSpreadConstraint(DataProperties config, Long department, String name) {
160        super(name,
161                config.getPropertyDouble("DeptBalancing.SpreadFactor", 1.20),
162                config.getPropertyInt("DeptBalancing.Unassignments2Weaken", 250),
163                config.getPropertyBoolean("General.InteractiveMode", false),
164                config.getPropertyInt("General.FirstDaySlot", Constants.DAY_SLOTS_FIRST),
165                config.getPropertyInt("General.LastDaySlot", Constants.DAY_SLOTS_LAST),
166                config.getPropertyInt("General.FirstWorkDay", 0),
167                config.getPropertyInt("General.LastWorkDay", Constants.NR_DAYS_WEEK - 1)
168                );
169        iDepartment = department;
170    }
171
172    public Long getDepartmentId() {
173        return iDepartment;
174    }
175    
176    @Override
177    protected Criterion<Lecture, Placement> getCriterion() {
178        return getModel().getCriterion(DepartmentBalancingPenalty.class);
179    }
180
181    @Override
182    public String toString() {
183        return "Departmental Balancing for " + getName();
184    }
185}