001package org.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 org.cpsolver.coursett.model.Placement;
010import org.cpsolver.coursett.model.RoomLocation;
011import org.cpsolver.coursett.model.TimeLocation;
012import org.cpsolver.ifs.assignment.Assignment;
013import org.cpsolver.ifs.assignment.DefaultSingleAssignment;
014import org.cpsolver.ifs.solution.Solution;
015import org.cpsolver.ifs.util.DataProperties;
016import org.cpsolver.ifs.util.ToolBox;
017import org.cpsolver.studentsct.StudentSectioningModel;
018import org.cpsolver.studentsct.extension.DistanceConflict;
019import org.cpsolver.studentsct.extension.TimeOverlapsCounter;
020import org.cpsolver.studentsct.model.Choice;
021import org.cpsolver.studentsct.model.Config;
022import org.cpsolver.studentsct.model.Course;
023import org.cpsolver.studentsct.model.CourseRequest;
024import org.cpsolver.studentsct.model.Enrollment;
025import org.cpsolver.studentsct.model.Instructor;
026import org.cpsolver.studentsct.model.Offering;
027import org.cpsolver.studentsct.model.Request;
028import org.cpsolver.studentsct.model.SctAssignment;
029import org.cpsolver.studentsct.model.Section;
030import org.cpsolver.studentsct.model.Student;
031import org.cpsolver.studentsct.model.Subpart;
032
033
034/**
035 * Student weight is spread equally among student's course requests. Only alternatives have lower weight.
036 * The rest is inherited from {@link PriorityStudentWeights}.
037 * 
038 * @author  Tomáš Müller
039 * @version StudentSct 1.3 (Student Sectioning)<br>
040 *          Copyright (C) 2007 - 2014 Tomáš Müller<br>
041 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
042 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
043 * <br>
044 *          This library is free software; you can redistribute it and/or modify
045 *          it under the terms of the GNU Lesser General Public License as
046 *          published by the Free Software Foundation; either version 3 of the
047 *          License, or (at your option) any later version. <br>
048 * <br>
049 *          This library is distributed in the hope that it will be useful, but
050 *          WITHOUT ANY WARRANTY; without even the implied warranty of
051 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
052 *          Lesser General Public License for more details. <br>
053 * <br>
054 *          You should have received a copy of the GNU Lesser General Public
055 *          License along with this library; if not see
056 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
057 */
058
059public class EqualStudentWeights extends PriorityStudentWeights {
060
061    public EqualStudentWeights(DataProperties config) {
062        super(config);
063    }
064
065    @Override
066    public double getWeight(Request request) {
067        if (request.getStudent().isDummy() && iProjectedStudentWeight >= 0.0) {
068            double weight = iProjectedStudentWeight;
069            if (request.isAlternative())
070                weight *= iAlternativeRequestFactor;
071            return weight;
072        }
073        double weight = 1.0 / request.getStudent().nrRequests();
074        if (request.isAlternative())
075            weight *= iAlternativeRequestFactor;
076        return round(weight);
077    }
078    
079    @Override
080    public boolean isBetterThanBestSolution(Solution<Request, Enrollment> currentSolution) {
081        if (currentSolution.getBestInfo() == null) return true;
082        if (iMPP) return super.isBetterThanBestSolution(currentSolution);
083        int unassigned = currentSolution.getModel().nrUnassignedVariables(currentSolution.getAssignment());
084        if (currentSolution.getModel().getBestUnassignedVariables() != unassigned)
085            return currentSolution.getModel().getBestUnassignedVariables() > unassigned;
086        return ((StudentSectioningModel)currentSolution.getModel()).getTotalValue(currentSolution.getAssignment(), iPreciseComparison) < currentSolution.getBestValue();
087    }
088    
089    @Override
090    public boolean isFreeTimeAllowOverlaps() {
091        return true;
092    }
093    
094    /**
095     * Test case -- run to see the weights for a few courses
096     * @param args program arguments
097     */
098    public static void main(String[] args) {
099        EqualStudentWeights pw = new EqualStudentWeights(new DataProperties());
100        DecimalFormat df = new DecimalFormat("0.0000");
101        Student s = new Student(0l);
102        new CourseRequest(1l, 0, false, s, ToolBox.toList(
103                new Course(1, "A", "1", new Offering(0, "A")),
104                new Course(1, "A", "2", new Offering(0, "A")),
105                new Course(1, "A", "3", new Offering(0, "A"))), false, null);
106        new CourseRequest(2l, 1, false, s, ToolBox.toList(
107                new Course(1, "B", "1", new Offering(0, "B")),
108                new Course(1, "B", "2", new Offering(0, "B")),
109                new Course(1, "B", "3", new Offering(0, "B"))), false, null);
110        new CourseRequest(3l, 2, false, s, ToolBox.toList(
111                new Course(1, "C", "1", new Offering(0, "C")),
112                new Course(1, "C", "2", new Offering(0, "C")),
113                new Course(1, "C", "3", new Offering(0, "C"))), false, null);
114        new CourseRequest(5l, 4, false, s, ToolBox.toList(
115                new Course(1, "E", "1", new Offering(0, "E")),
116                new Course(1, "E", "2", new Offering(0, "E")),
117                new Course(1, "E", "3", new Offering(0, "E"))), false, null);
118        new CourseRequest(6l, 5, true, s, ToolBox.toList(
119                new Course(1, "F", "1", new Offering(0, "F")),
120                new Course(1, "F", "2", new Offering(0, "F")),
121                new Course(1, "F", "3", new Offering(0, "F"))), false, null);
122        new CourseRequest(7l, 6, true, s, ToolBox.toList(
123                new Course(1, "G", "1", new Offering(0, "G")),
124                new Course(1, "G", "2", new Offering(0, "G")),
125                new Course(1, "G", "3", new Offering(0, "G"))), false, null);
126        
127        Assignment<Request, Enrollment> assignment = new DefaultSingleAssignment<Request, Enrollment>();
128        Placement p = new Placement(null, new TimeLocation(1, 90, 12, 0, 0, null, null, new BitSet(), 10), new ArrayList<RoomLocation>());
129        for (Request r: s.getRequests()) {
130            CourseRequest cr = (CourseRequest)r;
131            double[] w = new double[] {0.0, 0.0, 0.0};
132            for (int i = 0; i < cr.getCourses().size(); i++) {
133                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
134                Set<SctAssignment> sections = new HashSet<SctAssignment>();
135                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
136                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
137                w[i] = pw.getWeight(assignment, e, null, null);
138            }
139            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]));
140        }
141
142        System.out.println("With one distance conflict:");
143        for (Request r: s.getRequests()) {
144            CourseRequest cr = (CourseRequest)r;
145            double[] w = new double[] {0.0, 0.0, 0.0};
146            for (int i = 0; i < cr.getCourses().size(); i++) {
147                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
148                Set<SctAssignment> sections = new HashSet<SctAssignment>();
149                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
150                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
151                Set<DistanceConflict.Conflict> dc = new HashSet<DistanceConflict.Conflict>();
152                dc.add(new DistanceConflict.Conflict(s, e, (Section)sections.iterator().next(), e, (Section)sections.iterator().next()));
153                w[i] = pw.getWeight(assignment, e, dc, null);
154            }
155            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]));
156        }
157
158        System.out.println("With two distance conflicts:");
159        for (Request r: s.getRequests()) {
160            CourseRequest cr = (CourseRequest)r;
161            double[] w = new double[] {0.0, 0.0, 0.0};
162            for (int i = 0; i < cr.getCourses().size(); i++) {
163                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
164                Set<SctAssignment> sections = new HashSet<SctAssignment>();
165                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
166                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
167                Set<DistanceConflict.Conflict> dc = new HashSet<DistanceConflict.Conflict>();
168                dc.add(new DistanceConflict.Conflict(s, e, (Section)sections.iterator().next(), e, (Section)sections.iterator().next()));
169                dc.add(new DistanceConflict.Conflict(s, e, (Section)sections.iterator().next(), e,
170                        new Section(1, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null)));
171                w[i] = pw.getWeight(assignment, e, dc, null);
172            }
173            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]));
174        }
175
176        System.out.println("With 25% time overlapping conflict:");
177        for (Request r: s.getRequests()) {
178            CourseRequest cr = (CourseRequest)r;
179            double[] w = new double[] {0.0, 0.0, 0.0};
180            for (int i = 0; i < cr.getCourses().size(); i++) {
181                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
182                Set<SctAssignment> sections = new HashSet<SctAssignment>();
183                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
184                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
185                Set<TimeOverlapsCounter.Conflict> toc = new HashSet<TimeOverlapsCounter.Conflict>();
186                toc.add(new TimeOverlapsCounter.Conflict(s, 3, e, sections.iterator().next(), e, sections.iterator().next()));
187                w[i] = pw.getWeight(assignment, e, null, toc);
188            }
189            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]));
190        }
191
192        System.out.println("Disbalanced sections (by 2 / 10 students):");
193        for (Request r: s.getRequests()) {
194            CourseRequest cr = (CourseRequest)r;
195            double[] w = new double[] {0.0, 0.0, 0.0};
196            for (int i = 0; i < cr.getCourses().size(); i++) {
197                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
198                Set<SctAssignment> sections = new HashSet<SctAssignment>();
199                Subpart x = new Subpart(0, "Lec", "Lec", cfg, null);
200                Section a = new Section(0, 10, "x", x, p, null);
201                new Section(1, 10, "y", x, p, null);
202                sections.add(a);
203                a.assigned(assignment, new Enrollment(s.getRequests().get(0), i, cfg, sections, assignment));
204                a.assigned(assignment, new Enrollment(s.getRequests().get(0), i, cfg, sections, assignment));
205                cfg.getContext(assignment).assigned(assignment, new Enrollment(s.getRequests().get(0), i, cfg, sections, assignment));
206                cfg.getContext(assignment).assigned(assignment, new Enrollment(s.getRequests().get(0), i, cfg, sections, assignment));
207                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
208                w[i] = pw.getWeight(assignment, e, null, null);
209            }
210            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]));
211        }
212        
213        System.out.println("Same sections:");
214        pw.iMPP = true;
215        for (Request r: s.getRequests()) {
216            CourseRequest cr = (CourseRequest)r;
217            double[] w = new double[] {0.0, 0.0, 0.0};
218            double dif = 0;
219            for (int i = 0; i < cr.getCourses().size(); i++) {
220                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
221                Set<SctAssignment> sections = new HashSet<SctAssignment>();
222                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
223                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
224                cr.setInitialAssignment(new Enrollment(cr, i, cfg, sections, assignment));
225                w[i] = pw.getWeight(assignment, e, null, null);
226                dif = pw.getDifference(e);
227            }
228            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]) + " (" + df.format(dif) + ")");
229        }
230        
231        System.out.println("Same choice sections:");
232        pw.iMPP = true;
233        for (Request r: s.getRequests()) {
234            CourseRequest cr = (CourseRequest)r;
235            double[] w = new double[] {0.0, 0.0, 0.0};
236            double dif = 0;
237            for (int i = 0; i < cr.getCourses().size(); i++) {
238                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
239                Set<SctAssignment> sections = new HashSet<SctAssignment>();
240                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
241                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
242                Set<SctAssignment> other = new HashSet<SctAssignment>();
243                other.add(new Section(1, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
244                cr.setInitialAssignment(new Enrollment(cr, i, cfg, other, assignment));
245                w[i] = pw.getWeight(assignment, e, null, null);
246                dif = pw.getDifference(e);
247            }
248            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]) + " (" + df.format(dif) + ")");
249        }
250        
251        System.out.println("Same time sections:");
252        for (Request r: s.getRequests()) {
253            CourseRequest cr = (CourseRequest)r;
254            double dif = 0;
255            double[] w = new double[] {0.0, 0.0, 0.0};
256            for (int i = 0; i < cr.getCourses().size(); i++) {
257                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
258                Set<SctAssignment> sections = new HashSet<SctAssignment>();
259                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
260                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
261                Set<SctAssignment> other = new HashSet<SctAssignment>();
262                other.add(new Section(1, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null, new Instructor(1, null, "Josef Novak", null)));
263                cr.setInitialAssignment(new Enrollment(cr, i, cfg, other, assignment));
264                w[i] = pw.getWeight(assignment, e, null, null);
265                dif = pw.getDifference(e);
266            }
267            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]) + " (" + df.format(dif) + ")");
268        }
269        
270        System.out.println("Same configuration sections:");
271        for (Request r: s.getRequests()) {
272            CourseRequest cr = (CourseRequest)r;
273            double[] w = new double[] {0.0, 0.0, 0.0};
274            double dif = 0;
275            for (int i = 0; i < cr.getCourses().size(); i++) {
276                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
277                Set<SctAssignment> sections = new HashSet<SctAssignment>();
278                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
279                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
280                cr.getSelectedChoices().add(new Choice(cfg));
281                cr.setInitialAssignment(null);
282                w[i] = pw.getWeight(assignment, e, null, null);
283                dif = pw.getDifference(e);
284            }
285            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]) + " (" + df.format(dif) + ")");
286        }
287        
288        System.out.println("Different time sections:");
289        Placement q = new Placement(null, new TimeLocation(1, 102, 12, 0, 0, null, null, new BitSet(), 10), new ArrayList<RoomLocation>());
290        for (Request r: s.getRequests()) {
291            CourseRequest cr = (CourseRequest)r;
292            double[] w = new double[] {0.0, 0.0, 0.0};
293            double dif = 0;
294            for (int i = 0; i < cr.getCourses().size(); i++) {
295                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
296                Set<SctAssignment> sections = new HashSet<SctAssignment>();
297                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
298                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
299                Set<SctAssignment> other = new HashSet<SctAssignment>();
300                other.add(new Section(1, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), q, null));
301                cr.setInitialAssignment(new Enrollment(cr, i, cfg, other, assignment));
302                w[i] = pw.getWeight(assignment, e, null, null);
303                dif = pw.getDifference(e);
304            }
305            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]) + " (" + df.format(dif) + ")");
306        }
307        
308        System.out.println("Two sections, one same choice, one same time:");
309        for (Request r: s.getRequests()) {
310            CourseRequest cr = (CourseRequest)r;
311            double[] w = new double[] {0.0, 0.0, 0.0};
312            double dif = 0;
313            for (int i = 0; i < cr.getCourses().size(); i++) {
314                Config cfg = new Config(0l, -1, "", cr.getCourses().get(i).getOffering());
315                Set<SctAssignment> sections = new HashSet<SctAssignment>();
316                sections.add(new Section(0, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
317                sections.add(new Section(1, 1, "y", new Subpart(1, "Rec", "Rec", cfg, null), p, null));
318                Enrollment e = new Enrollment(cr, i, cfg, sections, assignment);
319                Set<SctAssignment> other = new HashSet<SctAssignment>();
320                other.add(new Section(2, 1, "x", new Subpart(0, "Lec", "Lec", cfg, null), p, null));
321                other.add(new Section(3, 1, "y", new Subpart(1, "Rec", "Rec", cfg, null), p, null, new Instructor(1, null, "Josef Novak", null)));
322                cr.setInitialAssignment(new Enrollment(cr, i, cfg, other, assignment));
323                w[i] = pw.getWeight(assignment, e, null, null);
324                dif = pw.getDifference(e);
325            }
326            System.out.println(cr + ": " + df.format(w[0]) + "  " + df.format(w[1]) + "  " + df.format(w[2]) + " (" + df.format(dif) + ")");
327        }
328    }
329}