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    }