001package net.sf.cpsolver.studentsct.weights;
002
003import java.text.DecimalFormat;
004import java.util.ArrayList;
005import java.util.BitSet;
006import java.util.HashSet;
007import java.util.Set;
008
009import net.sf.cpsolver.coursett.model.Placement;
010import net.sf.cpsolver.coursett.model.RoomLocation;
011import net.sf.cpsolver.coursett.model.TimeLocation;
012import net.sf.cpsolver.ifs.solution.Solution;
013import net.sf.cpsolver.ifs.util.DataProperties;
014import net.sf.cpsolver.ifs.util.ToolBox;
015import net.sf.cpsolver.studentsct.extension.DistanceConflict;
016import net.sf.cpsolver.studentsct.extension.TimeOverlapsCounter;
017import net.sf.cpsolver.studentsct.model.Assignment;
018import net.sf.cpsolver.studentsct.model.Config;
019import net.sf.cpsolver.studentsct.model.Course;
020import net.sf.cpsolver.studentsct.model.CourseRequest;
021import net.sf.cpsolver.studentsct.model.Enrollment;
022import net.sf.cpsolver.studentsct.model.Offering;
023import net.sf.cpsolver.studentsct.model.Request;
024import net.sf.cpsolver.studentsct.model.Section;
025import net.sf.cpsolver.studentsct.model.Student;
026import 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 Tomáš Müller<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
051public 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}