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