001    package net.sf.cpsolver.studentsct.heuristics.selection;
002    
003    import java.util.Enumeration;
004    import java.util.Iterator;
005    import java.util.Vector;
006    
007    import org.apache.log4j.Logger;
008    
009    import net.sf.cpsolver.ifs.solver.Solver;
010    import net.sf.cpsolver.ifs.util.DataProperties;
011    import net.sf.cpsolver.studentsct.StudentPreferencePenalties;
012    import net.sf.cpsolver.studentsct.model.Config;
013    import net.sf.cpsolver.studentsct.model.Course;
014    import net.sf.cpsolver.studentsct.model.CourseRequest;
015    import net.sf.cpsolver.studentsct.model.Enrollment;
016    import net.sf.cpsolver.studentsct.model.Request;
017    import net.sf.cpsolver.studentsct.model.Section;
018    import net.sf.cpsolver.studentsct.model.Student;
019    import net.sf.cpsolver.studentsct.model.Subpart;
020    
021    /**
022     * Section given student using branch & bound algorithm with no unassignments allowed.
023     * 
024     * <br><br>
025     * Parameters:
026     * <br>
027     * <table border='1'><tr><th>Parameter</th><th>Type</th><th>Comment</th></tr>
028     * <tr><td>Sectioning.UseStudentPreferencePenalties</td><td>{@link Boolean}</td><td>If true, {@link StudentPreferencePenalties} are used</td></tr>
029     * <tr><td>Sectioning.Distribution</td><td>{@link Integer}</td><td>When student preference penalties are used, defines which distribution is to be used (one
030     * of {@link StudentPreferencePenalties#sDistTypePreference}, {@link StudentPreferencePenalties#sDistTypePreferenceQuadratic}, {@link StudentPreferencePenalties#sDistTypePreferenceReverse},
031     * {@link StudentPreferencePenalties#sDistTypeUniform})</td></tr>
032     * <tr><td>Sectioning.UseOnlinePenalties</td><td>{@link Boolean}</td><td>If true, online sectioning penalties computed based on held/expected space are used.</td></tr>
033     * <tr><td>Sectioning.Epsilon</td><td>{@link Double}</td><td>When both online penalties and student preference penalties are used: a solution based on online penalties is computed first,
034     * this solution (and the given epsilon) is then used to setup bounds on online penalties for the solution that minimizes student preference penalties.
035     * Limit on online penalty is computed as (1+Section.Epsilon)*{@link Selection#getPenalty()}, i.e., only sections with penalty equal or below this limit can be used -- 
036     * among these the solution that minimizes student preference penalties is computed.</td></tr>
037     * </table>
038     * <br><br>
039     * 
040     * @version
041     * StudentSct 1.1 (Student Sectioning)<br>
042     * Copyright (C) 2007 Tomáš Müller<br>
043     * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
044     * Lazenska 391, 76314 Zlin, Czech Republic<br>
045     * <br>
046     * This library is free software; you can redistribute it and/or
047     * modify it under the terms of the GNU Lesser General Public
048     * License as published by the Free Software Foundation; either
049     * version 2.1 of the License, or (at your option) any later version.
050     * <br><br>
051     * This library is distributed in the hope that it will be useful,
052     * but WITHOUT ANY WARRANTY; without even the implied warranty of
053     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
054     * Lesser General Public License for more details.
055     * <br><br>
056     * You should have received a copy of the GNU Lesser General Public
057     * License along with this library; if not, write to the Free Software
058     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
059    */
060    
061    public class OnlineSelection extends BranchBoundSelection {
062        private static Logger sLog = Logger.getLogger(OnlineSelection.class);
063        private int iDistributionType = -1;
064        private double iEpsilon = 0.05;
065        private boolean iUsePenalties = true;
066        private boolean iUseStudentPrefPenalties = false;
067        private BranchBoundSelection iBranchBound = null;
068        
069        /**
070         * Constructor
071         * @param properties configuration
072         */
073        public OnlineSelection(DataProperties properties) {
074            super(properties);
075            iUseStudentPrefPenalties = properties.getPropertyBoolean("Sectioning.UseStudentPreferencePenalties", iUseStudentPrefPenalties);
076            iDistributionType = properties.getPropertyInt("Sectioning.Distribution", StudentPreferencePenalties.sDistTypePreference);
077            iEpsilon = properties.getPropertyDouble("Sectioning.Epsilon", iEpsilon);
078            iUsePenalties = properties.getPropertyBoolean("Sectioning.UseOnlinePenalties", iUsePenalties);
079            if (iUseStudentPrefPenalties && !properties.containsPropery("Sectioning.UseOnlinePenalties")) iUsePenalties = false;
080            if (iUsePenalties || !iUseStudentPrefPenalties)
081                iBranchBound = new BranchBoundSelection(properties);
082            iMinimizePenalty=true;
083        }
084        
085        public void init(Solver solver) {
086            init(solver, "Online...");
087        }
088        
089        /** Use student preference penalties */
090        public boolean isUseStudentPrefPenalties() {
091            return iUseStudentPrefPenalties;
092        }
093        
094        /** Use online penalties */
095        public boolean isUsePenalties() {
096            return iUsePenalties;
097        }
098    
099        /** Set online sectioning penalties to all sections of all courses of the given student */
100        private static void setPenalties(Student student) {
101            for (Enumeration e=student.getRequests().elements();e.hasMoreElements();) {
102                Request request = (Request)e.nextElement();
103                if (!(request instanceof CourseRequest)) continue;
104                CourseRequest courseRequest = (CourseRequest)request;
105                for (Enumeration f=courseRequest.getCourses().elements();f.hasMoreElements();) {
106                    Course course = (Course)f.nextElement();
107                    for (Enumeration g=course.getOffering().getConfigs().elements();g.hasMoreElements();) {
108                        Config config = (Config)g.nextElement();
109                        for (Enumeration h=config.getSubparts().elements();h.hasMoreElements();) {
110                            Subpart subpart = (Subpart)h.nextElement();
111                            for (Enumeration i=subpart.getSections().elements();i.hasMoreElements();) {
112                                Section section = (Section)i.nextElement();
113                                section.setPenalty(section.getOnlineSectioningPenalty());
114                            }
115                        }
116                    }
117                }
118                courseRequest.clearCache();
119            }
120        }
121        
122        /** Update online sectioning info after the given student is sectioned */
123        public void updateSpace(Student student) {
124            for (Enumeration e=student.getRequests().elements();e.hasMoreElements();) {
125                Request request = (Request)e.nextElement();
126                if (!(request instanceof CourseRequest)) continue;
127                CourseRequest courseRequest = (CourseRequest)request;
128                if (courseRequest.getAssignment()==null) return; //not enrolled --> no update
129                Enrollment enrollment = (Enrollment)courseRequest.getAssignment();
130                for (Iterator i=enrollment.getAssignments().iterator();i.hasNext();) {
131                    Section section = (Section)i.next();
132                    section.setSpaceHeld(section.getSpaceHeld()-courseRequest.getWeight());
133                    //sLog.debug("  -- space held for "+section+" decreased by 1 (to "+section.getSpaceHeld()+")");
134                }
135                Vector feasibleEnrollments = new Vector();
136                for (Enumeration g=courseRequest.values().elements();g.hasMoreElements();) {
137                    Enrollment enrl = (Enrollment)g.nextElement();
138                    boolean overlaps = false;
139                    for (Enumeration h=courseRequest.getStudent().getRequests().elements();h.hasMoreElements();) {
140                        CourseRequest otherCourseRequest = (CourseRequest)h.nextElement();
141                        if (otherCourseRequest.equals(courseRequest)) continue;
142                        Enrollment otherErollment = (Enrollment)otherCourseRequest.getAssignment();
143                        if (otherErollment==null) continue;
144                        if (enrl.isOverlapping(otherErollment)) {
145                            overlaps = true; break;
146                        }
147                    }
148                    if (!overlaps)
149                        feasibleEnrollments.add(enrl);
150                }
151                double decrement = courseRequest.getWeight() / feasibleEnrollments.size();
152                for (Enumeration g=feasibleEnrollments.elements();g.hasMoreElements();) {
153                    Enrollment feasibleEnrollment = (Enrollment)g.nextElement();
154                    for (Iterator i=feasibleEnrollment.getAssignments().iterator();i.hasNext();) {
155                        Section section = (Section)i.next();
156                        section.setSpaceExpected(section.getSpaceExpected()-decrement);
157                        //sLog.debug("  -- space expected for "+section+" decreased by "+decrement+" (to "+section.getSpaceExpected()+")");
158                    }
159                }
160            }
161        }
162        
163        /**
164         * Branch & bound selection for a student
165         */
166        public Selection getSelection(Student student) {
167            if (iUsePenalties) setPenalties(student);
168            Selection selection = null;
169            if (iBranchBound!=null) selection = iBranchBound.getSelection(student);
170            if (iUseStudentPrefPenalties) selection = new EpsilonSelection(student, selection); 
171            return selection;
172        }
173        
174        /**
175         * Branch & bound selection for a student
176         */
177        public class EpsilonSelection extends BranchBoundSelection.Selection {
178            private StudentPreferencePenalties iPenalties = null;
179            private Selection iSelection = null;
180            
181            /**
182             * Constructor
183             * @param student selected student
184             */
185            public EpsilonSelection(Student student, Selection selection) {
186                super(student);
187                iPenalties = new StudentPreferencePenalties(iDistributionType);
188                iSelection = selection;
189            }
190            
191            /**
192             * Execute branch & bound, return the best found schedule for the selected student.
193             */
194            public BranchBoundNeighbour select() {
195                BranchBoundNeighbour onlineSelection = null;
196                if (iSelection!=null) {
197                    onlineSelection = iSelection.select();
198                    if (sDebug) sLog.debug("Online: "+onlineSelection);
199                }
200                BranchBoundNeighbour neighbour = super.select();
201                if (neighbour!=null) return neighbour;
202                if (onlineSelection!=null) return onlineSelection;
203                return null;
204            }
205            
206            /** Assignment penalty */
207            protected double getAssignmentPenalty(int i) {
208                return iPenalties.getPenalty(iAssignment[i]) + iDistConfWeight*getNrDistanceConflicts(i);
209            }
210            
211            public boolean isAllowed(int idx, Enrollment enrollment) {
212                if (iSelection==null || iSelection.getBestAssignment()==null || iSelection.getBestAssignment()[idx]==null) return true;
213                double bestPenalty = iSelection.getBestAssignment()[idx].getPenalty();
214                double limit = (iEpsilon<0?Math.max(0, bestPenalty):(bestPenalty<0?1-iEpsilon:1+iEpsilon)*bestPenalty);
215                if (enrollment.getPenalty()>limit) {
216                    if (sDebug) sLog.debug("  -- enrollment "+enrollment+" was filtered out " +
217                            "(penalty="+enrollment.getPenalty()+", best="+bestPenalty+", limit="+limit+")");
218                    return false;
219                }
220                return true;
221            }
222            
223            /** First conflicting enrollment */
224            public Enrollment firstConflict(int idx, Enrollment enrollment) {
225                Enrollment conflict = super.firstConflict(idx, enrollment);
226                if (conflict!=null) return conflict;
227                return (isAllowed(idx, enrollment)?null:enrollment);
228            }
229    
230            /** Student preference penalties */
231            public StudentPreferencePenalties getPenalties() {
232                return iPenalties;
233            }
234        }    
235    }