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