001 package net.sf.cpsolver.studentsct.weights;
002
003 import java.text.DecimalFormat;
004 import java.util.ArrayList;
005 import java.util.BitSet;
006 import java.util.HashSet;
007 import java.util.Set;
008
009 import net.sf.cpsolver.coursett.model.Placement;
010 import net.sf.cpsolver.coursett.model.RoomLocation;
011 import net.sf.cpsolver.coursett.model.TimeLocation;
012 import net.sf.cpsolver.ifs.solution.Solution;
013 import net.sf.cpsolver.ifs.util.DataProperties;
014 import net.sf.cpsolver.ifs.util.ToolBox;
015 import net.sf.cpsolver.studentsct.extension.DistanceConflict;
016 import net.sf.cpsolver.studentsct.extension.TimeOverlapsCounter;
017 import net.sf.cpsolver.studentsct.model.Assignment;
018 import net.sf.cpsolver.studentsct.model.Config;
019 import net.sf.cpsolver.studentsct.model.Course;
020 import net.sf.cpsolver.studentsct.model.CourseRequest;
021 import net.sf.cpsolver.studentsct.model.Enrollment;
022 import net.sf.cpsolver.studentsct.model.Offering;
023 import net.sf.cpsolver.studentsct.model.Request;
024 import net.sf.cpsolver.studentsct.model.Section;
025 import net.sf.cpsolver.studentsct.model.Student;
026 import net.sf.cpsolver.studentsct.model.Subpart;
027
028 /**
029 * Original weighting that was used before this student weightings model was introduced
030 *
031 * @version StudentSct 1.2 (Student Sectioning)<br>
032 * Copyright (C) 2007 - 2010 Tomas Muller<br>
033 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
034 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
035 * <br>
036 * This library is free software; you can redistribute it and/or modify
037 * it under the terms of the GNU Lesser General Public License as
038 * published by the Free Software Foundation; either version 3 of the
039 * License, or (at your option) any later version. <br>
040 * <br>
041 * This library is distributed in the hope that it will be useful, but
042 * WITHOUT ANY WARRANTY; without even the implied warranty of
043 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
044 * Lesser General Public License for more details. <br>
045 * <br>
046 * You should have received a copy of the GNU Lesser General Public
047 * License along with this library; if not see
048 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
049 */
050
051 public class OriginalStudentWeights implements StudentWeights {
052 private double iPriorityWeight = 0.90;
053 private double iAlterativeWeight = 1.0;
054 private double iInitialWeight = 1.2;
055 private double iSelectedWeight = 1.1;
056 private double iWaitlistedWeight = 1.01;
057 private double iDistConfWeight = 0.95;
058 /**
059 * Enrollment value: value * sAltValue ^ index, where index is zero for the
060 * first course, one for the second course etc.
061 */
062 private double iAltValue = 0.5;
063 private double iDummyStudentWeight = 0.5;
064 private double iNormPenalty = 5.0;
065
066 public OriginalStudentWeights(DataProperties config) {
067 iDummyStudentWeight = config.getPropertyDouble("Student.DummyStudentWeight", iDummyStudentWeight);
068 }
069
070 /**
071 * Normalized enrollment penalty -- to be used in
072 * {@link Enrollment#toDouble()}
073 */
074 public double normalizePenalty(double penalty) {
075 return iNormPenalty / (iNormPenalty + penalty);
076 }
077
078
079 public double getWeight(Request request) {
080 return Math.pow(iPriorityWeight, request.getPriority())
081 * (request.isAlternative() ? iAlterativeWeight : 1.0)
082 * (request.getStudent().isDummy() ? iDummyStudentWeight : 1.0);
083 }
084
085 @Override
086 public double getBound(Request request) {
087 double w = getWeight(request) * Math.pow(iInitialWeight, (request.getInitialAssignment() == null ? 0 : 1));
088 if (request instanceof CourseRequest) {
089 CourseRequest cr = (CourseRequest)request;
090 w *= Math.pow(iSelectedWeight, cr.getSelectedChoices().isEmpty() ? 0 : 1);
091 w *= Math.pow(iWaitlistedWeight, cr.getWaitlistedChoices().isEmpty() ? 0 : 1);
092 w *= normalizePenalty(cr.getMinPenalty());
093 }
094 return w;
095 }
096
097 @Override
098 public double getWeight(Enrollment enrollment) {
099 return getWeight(enrollment.getRequest())
100 * Math.pow(iAltValue, enrollment.getPriority())
101 * Math.pow(iInitialWeight, enrollment.percentInitial())
102 * Math.pow(iSelectedWeight, enrollment.percentSelected())
103 * Math.pow(iWaitlistedWeight, enrollment.percentWaitlisted())
104 * normalizePenalty(enrollment.getPenalty());
105 }
106
107 @Override
108 public double getWeight(Enrollment enrollment, Set<DistanceConflict.Conflict> distanceConflicts, Set<TimeOverlapsCounter.Conflict> timeOverlappingConflicts) {
109 int share = 0;
110 if (timeOverlappingConflicts != null)
111 for (TimeOverlapsCounter.Conflict c: timeOverlappingConflicts)
112 share += c.getShare();
113 return getWeight(enrollment)
114 * (distanceConflicts == null || distanceConflicts.isEmpty() ? 1.0 : Math.pow(iDistConfWeight, distanceConflicts.size()))
115 * Math.max(share == 0 ? 1.0 : 1.0 - (((double)share) / enrollment.getNrSlots()) / 2.0, 0.5);
116 }
117
118 @Override
119 public double getDistanceConflictWeight(DistanceConflict.Conflict c) {
120 if (c.getR1().getPriority() < c.getR2().getPriority()) {
121 return (1.0 - iDistConfWeight) * getWeight(c.getE1());
122 } else {
123 return (1.0 - iDistConfWeight) * getWeight(c.getE2());
124 }
125 }
126
127 @Override
128 public double getTimeOverlapConflictWeight(Enrollment enrollment, TimeOverlapsCounter.Conflict timeOverlap) {
129 return Math.min(0.5 * timeOverlap.getShare() / enrollment.getNrSlots(), 0.5) * getWeight(enrollment);
130 }
131
132 @Override
133 public boolean isBetterThanBestSolution(Solution<Request, Enrollment> currentSolution) {
134 if (currentSolution.getBestInfo() == null) return true;
135 int unassigned = currentSolution.getModel().nrUnassignedVariables();
136 if (currentSolution.getModel().getBestUnassignedVariables() != unassigned)
137 return currentSolution.getModel().getBestUnassignedVariables() > unassigned;
138 return currentSolution.getModel().getTotalValue() < currentSolution.getBestValue();
139 }
140
141 /**
142 * Test case -- run to see the weights for a few courses
143 */
144 public static void main(String[] args) {
145 OriginalStudentWeights pw = new OriginalStudentWeights(new DataProperties());
146 DecimalFormat df = new DecimalFormat("0.000");
147 Student s = new Student(0l);
148 new CourseRequest(1l, 0, false, s, ToolBox.toList(
149 new Course(1, "A", "1", new Offering(0, "A")),
150 new Course(1, "A", "2", new Offering(0, "A")),
151 new Course(1, "A", "3", new Offering(0, "A"))), false, null);
152 new CourseRequest(2l, 1, false, s, ToolBox.toList(
153 new Course(1, "B", "1", new Offering(0, "B")),
154 new Course(1, "B", "2", new Offering(0, "B")),
155 new Course(1, "B", "3", new Offering(0, "B"))), false, null);
156 new CourseRequest(3l, 2, false, s, ToolBox.toList(
157 new Course(1, "C", "1", new Offering(0, "C")),
158 new Course(1, "C", "2", new Offering(0, "C")),
159 new Course(1, "C", "3", new Offering(0, "C"))), false, null);
160 new CourseRequest(4l, 3, false, s, ToolBox.toList(
161 new Course(1, "D", "1", new Offering(0, "D")),
162 new Course(1, "D", "2", new Offering(0, "D")),
163 new Course(1, "D", "3", new Offering(0, "D"))), false, null);
164 new CourseRequest(5l, 4, false, s, ToolBox.toList(
165 new Course(1, "E", "1", new Offering(0, "E")),
166 new Course(1, "E", "2", new Offering(0, "E")),
167 new Course(1, "E", "3", new Offering(0, "E"))), false, null);
168 new CourseRequest(6l, 5, true, s, ToolBox.toList(
169 new Course(1, "F", "1", new Offering(0, "F")),
170 new Course(1, "F", "2", new Offering(0, "F")),
171 new Course(1, "F", "3", new Offering(0, "F"))), false, null);
172 new CourseRequest(7l, 6, true, s, ToolBox.toList(
173 new Course(1, "G", "1", new Offering(0, "G")),
174 new Course(1, "G", "2", new Offering(0, "G")),
175 new Course(1, "G", "3", new Offering(0, "G"))), false, null);
176
177 Placement p = new Placement(null, new TimeLocation(1, 90, 12, 0, 0, null, null, new BitSet(), 10), new ArrayList<RoomLocation>());
178 for (Request r: s.getRequests()) {
179 CourseRequest cr = (CourseRequest)r;
180 double[] w = new double[] {0.0, 0.0, 0.0};
181 for (int i = 0; i < cr.getCourses().size(); i++) {
182 Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
183 Set<Assignment> sections = new HashSet<Assignment>();
184 sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null, null, null));
185 Enrollment e = new Enrollment(cr, i, cfg, sections);
186 w[i] = pw.getWeight(e, null, null);
187 }
188 System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
189 }
190
191 System.out.println("With one distance conflict:");
192 for (Request r: s.getRequests()) {
193 CourseRequest cr = (CourseRequest)r;
194 double[] w = new double[] {0.0, 0.0, 0.0};
195 for (int i = 0; i < cr.getCourses().size(); i++) {
196 Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
197 Set<Assignment> sections = new HashSet<Assignment>();
198 sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null, null, null));
199 Enrollment e = new Enrollment(cr, i, cfg, sections);
200 Set<DistanceConflict.Conflict> dc = new HashSet<DistanceConflict.Conflict>();
201 dc.add(new DistanceConflict.Conflict(s, e, (Section)sections.iterator().next(), e, (Section)sections.iterator().next()));
202 w[i] = pw.getWeight(e, dc, null);
203 }
204 System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
205 }
206
207 System.out.println("With two distance conflicts:");
208 for (Request r: s.getRequests()) {
209 CourseRequest cr = (CourseRequest)r;
210 double[] w = new double[] {0.0, 0.0, 0.0};
211 for (int i = 0; i < cr.getCourses().size(); i++) {
212 Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
213 Set<Assignment> sections = new HashSet<Assignment>();
214 sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null, null, null));
215 Enrollment e = new Enrollment(cr, i, cfg, sections);
216 Set<DistanceConflict.Conflict> dc = new HashSet<DistanceConflict.Conflict>();
217 dc.add(new DistanceConflict.Conflict(s, e, (Section)sections.iterator().next(), e, (Section)sections.iterator().next()));
218 dc.add(new DistanceConflict.Conflict(s, e, (Section)sections.iterator().next(), e,
219 new Section(1, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null, null, null)));
220 w[i] = pw.getWeight(e, dc, null);
221 }
222 System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
223 }
224
225 System.out.println("With 25% time overlapping conflicts:");
226 for (Request r: s.getRequests()) {
227 CourseRequest cr = (CourseRequest)r;
228 double[] w = new double[] {0.0, 0.0, 0.0};
229 for (int i = 0; i < cr.getCourses().size(); i++) {
230 Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
231 Set<Assignment> sections = new HashSet<Assignment>();
232 sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null, null, null));
233 Enrollment e = new Enrollment(cr, i, cfg, sections);
234 Set<TimeOverlapsCounter.Conflict> toc = new HashSet<TimeOverlapsCounter.Conflict>();
235 toc.add(new TimeOverlapsCounter.Conflict(s, 3, e, sections.iterator().next(), e, sections.iterator().next()));
236 w[i] = pw.getWeight(e, null, toc);
237 }
238 System.out.println(cr + ": " + df.format(w[0]) + " " + df.format(w[1]) + " " + df.format(w[2]));
239 }
240 }
241
242 @Override
243 public boolean isFreeTimeAllowOverlaps() {
244 return true;
245 }
246 }