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