001    package net.sf.cpsolver.coursett.constraint;
002    
003    import java.util.HashSet;
004    import java.util.Set;
005    
006    import net.sf.cpsolver.coursett.criteria.StudentConflict;
007    import net.sf.cpsolver.coursett.model.Lecture;
008    import net.sf.cpsolver.coursett.model.Placement;
009    import net.sf.cpsolver.coursett.model.Student;
010    import net.sf.cpsolver.coursett.model.TimetableModel;
011    import net.sf.cpsolver.ifs.criteria.Criterion;
012    import net.sf.cpsolver.ifs.model.BinaryConstraint;
013    import net.sf.cpsolver.ifs.util.DistanceMetric;
014    import net.sf.cpsolver.ifs.util.ToolBox;
015    
016    /**
017     * Join student enrollment constraint. <br>
018     * This constraint is placed between all pairs of classes where there is at
019     * least one student attending both classes. It represents a number of student
020     * conflicts (number of joined enrollments), if the given two classes overlap in
021     * time. <br>
022     * Also, it dynamically maintains the counter of all student conflicts. It is a
023     * soft constraint.
024     * 
025     * 
026     * @version CourseTT 1.2 (University Course Timetabling)<br>
027     *          Copyright (C) 2006 - 2010 Tomas Muller<br>
028     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
029     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
030     * <br>
031     *          This library is free software; you can redistribute it and/or modify
032     *          it under the terms of the GNU Lesser General Public License as
033     *          published by the Free Software Foundation; either version 3 of the
034     *          License, or (at your option) any later version. <br>
035     * <br>
036     *          This library is distributed in the hope that it will be useful, but
037     *          WITHOUT ANY WARRANTY; without even the implied warranty of
038     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
039     *          Lesser General Public License for more details. <br>
040     * <br>
041     *          You should have received a copy of the GNU Lesser General Public
042     *          License along with this library; if not see
043     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
044     */
045    
046    public class JenrlConstraint extends BinaryConstraint<Lecture, Placement> {
047        private double iJenrl = 0.0;
048        private double iPriority = 0.0;
049        private Set<Student> iStudents = new HashSet<Student>();
050        private boolean iAdded = false;
051    
052        /**
053         * Constructor
054         */
055        public JenrlConstraint() {
056            super();
057        }
058    
059        @Override
060        public void computeConflicts(Placement value, Set<Placement> conflicts) {
061        }
062    
063        @Override
064        public boolean inConflict(Placement value) {
065            return false;
066        }
067    
068        @Override
069        public boolean isConsistent(Placement value1, Placement value2) {
070            return true;
071        }
072    
073        @Override
074        public void unassigned(long iteration, Placement value) {
075            super.unassigned(iteration, value);
076            if (iAdded) {
077                iAdded = false;
078                (first()).removeActiveJenrl(this);
079                (second()).removeActiveJenrl(this);
080            }
081        }
082    
083        /**
084         * Returns true if the given placements are overlapping or they are
085         * back-to-back and too far for students.
086         */
087        public static boolean isInConflict(Placement p1, Placement p2, DistanceMetric m) {
088            return StudentConflict.distance(m, p1, p2) || StudentConflict.overlaps(p1, p2);
089        }
090    
091        @Override
092        public void assigned(long iteration, Placement value) {
093            super.assigned(iteration, value);
094            if (second() == null || first().getAssignment() == null || second().getAssignment() == null)
095                return;
096            if (isInConflict(first().getAssignment(), second().getAssignment(), getDistanceMetric())) {
097                iAdded = true;
098                (first()).addActiveJenrl(this);
099                (second()).addActiveJenrl(this);
100            }
101        }
102    
103        /**
104         * Number of joined enrollments if the given value is assigned to the given
105         * variable
106         */
107        public long jenrl(Lecture variable, Placement value) {
108            Lecture anotherLecture = (first().equals(variable) ? second() : first());
109            if (anotherLecture.getAssignment() == null)
110                return 0;
111            return (isInConflict(anotherLecture.getAssignment(), value, getDistanceMetric()) ? Math.round(iJenrl) : 0);
112        }
113        
114        private DistanceMetric getDistanceMetric() {
115            return ((TimetableModel)getModel()).getDistanceMetric();
116        }
117    
118        /** True if the given two lectures overlap in time */
119        public boolean isInConflict() {
120            return iAdded;
121        }
122    
123        /**
124         * Increment the number of joined enrollments (during student final
125         * sectioning)
126         */
127        public void incJenrl(Student student) {
128            double jenrlWeight = student.getJenrlWeight(first(), second());
129            iJenrl += jenrlWeight;
130            Double conflictPriority = student.getConflictingPriorty(first(), second());
131            if (conflictPriority != null) iPriority += conflictPriority * jenrlWeight;
132            iStudents.add(student);
133            for (Criterion<Lecture, Placement> criterion: getModel().getCriteria())
134                if (criterion instanceof StudentConflict)
135                    ((StudentConflict)criterion).incJenrl(this, jenrlWeight, conflictPriority);
136        }
137    
138        public double getJenrlWeight(Student student) {
139            return student.getJenrlWeight(first(), second());
140        }
141    
142        /**
143         * Decrement the number of joined enrollments (during student final
144         * sectioning)
145         */
146        public void decJenrl(Student student) {
147            double jenrlWeight = student.getJenrlWeight(first(), second());
148            iJenrl -= jenrlWeight;
149            Double conflictPriority = student.getConflictingPriorty(first(), second());
150            if (conflictPriority != null) iPriority -= conflictPriority * jenrlWeight;
151            iStudents.remove(student);
152            for (Criterion<Lecture, Placement> criterion: getModel().getCriteria())
153                if (criterion instanceof StudentConflict)
154                    ((StudentConflict)criterion).incJenrl(this, -jenrlWeight, conflictPriority);
155        }
156    
157        /** Number of joined enrollments (during student final sectioning) */
158        public long getJenrl() {
159            return Math.round(iJenrl);
160        }
161    
162        public double jenrl() {
163            return iJenrl;
164        }
165    
166        public double priority() {
167            return iPriority;
168        }
169    
170        public int getNrStudents() {
171            return iStudents.size();
172        }
173        
174        public Set<Student> getStudents() {
175            return iStudents;
176        }
177    
178        @Override
179        public boolean isHard() {
180            return false;
181        }
182    
183        @Override
184        public String toString() {
185            return "Joint Enrollment between " + first().getName() + " and " + second().getName();
186        }
187    
188        public boolean areStudentConflictsHard() {
189            return StudentConflict.hard(first(), second());
190        }
191    
192        public boolean areStudentConflictsDistance() {
193            return StudentConflict.distance(getDistanceMetric(), first().getAssignment(), second().getAssignment());
194        }
195    
196        public boolean areStudentConflictsCommitted() {
197            return StudentConflict.committed(first(), second());
198        }
199    
200        public boolean areStudentConflictsDistance(Placement value) {
201            return StudentConflict.distance(getDistanceMetric(), value, another(value.variable()).getAssignment());
202        }
203    
204        public boolean isOfTheSameProblem() {
205            return ToolBox.equals(first().getSolverGroupId(), second().getSolverGroupId());
206        }
207    
208    }