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 }