001package org.cpsolver.instructor.constraints; 002 003import java.util.Set; 004 005import org.cpsolver.coursett.Constants; 006import org.cpsolver.ifs.assignment.Assignment; 007import org.cpsolver.ifs.assignment.context.AssignmentConstraintContext; 008import org.cpsolver.ifs.assignment.context.ConstraintWithContext; 009import org.cpsolver.instructor.criteria.SameInstructor; 010import org.cpsolver.instructor.model.TeachingAssignment; 011import org.cpsolver.instructor.model.TeachingRequest; 012 013/** 014 * Same Instructor Constraint. Teaching requests linked with this constraint must/should have the same 015 * instructor assigned. If discouraged/prohibited, every pair of teaching requests should/must have a different 016 * instructor. 017 * 018 * @author Tomáš Müller 019 * @version IFS 1.3 (Instructor Sectioning)<br> 020 * Copyright (C) 2016 Tomáš Müller<br> 021 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 022 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 023 * <br> 024 * This library is free software; you can redistribute it and/or modify 025 * it under the terms of the GNU Lesser General Public License as 026 * published by the Free Software Foundation; either version 3 of the 027 * License, or (at your option) any later version. <br> 028 * <br> 029 * This library is distributed in the hope that it will be useful, but 030 * WITHOUT ANY WARRANTY; without even the implied warranty of 031 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 032 * Lesser General Public License for more details. <br> 033 * <br> 034 * You should have received a copy of the GNU Lesser General Public 035 * License along with this library; if not see 036 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 037 */ 038public class SameInstructorConstraint extends ConstraintWithContext<TeachingRequest.Variable, TeachingAssignment, SameInstructorConstraint.Context> { 039 private Long iId; 040 private String iName; 041 private int iPreference = 0; 042 private boolean iRequired = false, iProhibited = false; 043 044 /** 045 * Constructor 046 * @param id constraint id 047 * @param name constrain (link) name 048 * @param preference preference (R for required, P for prohibited, etc.) 049 */ 050 public SameInstructorConstraint(Long id, String name, String preference) { 051 iId = id; 052 iName = name; 053 iPreference = Constants.preference2preferenceLevel(preference); 054 if (Constants.sPreferenceRequired.equals(preference)) { 055 iRequired = true; 056 } else if (Constants.sPreferenceProhibited.equals(preference)) { 057 iProhibited = true; 058 } 059 } 060 061 /** 062 * Constraint id that was provided in the constructor 063 * @return constraint id 064 */ 065 public Long getConstraintId() { return iId; } 066 067 @Override 068 public String getName() { return iName; } 069 070 @Override 071 public String toString() { return "Same Instructor " + getName(); } 072 073 /** 074 * Is required? 075 * @return true if the constraint is required 076 */ 077 public boolean isRequired() { return iRequired; } 078 079 /** 080 * Is prohibited? 081 * @return true if the constraint is prohibited 082 */ 083 public boolean isProhibited() { return iProhibited; } 084 085 /** 086 * Constraint preference that was provided in the constructor 087 * @return constraint preference 088 */ 089 public int getPreference() { return iPreference; } 090 091 @Override 092 public boolean isHard() { 093 return isRequired() || isProhibited(); 094 } 095 096 /** 097 * Does a pair of teaching assignments satisfy this constraint? 098 * @param assignment current assignment 099 * @param a1 first teaching assignment 100 * @param a2 second teaching assignment 101 * @return True if the two assignments (of this constraint) have the same instructor (prohibited/preferred case) or a different instructor (prohibited/discouraged case). 102 */ 103 public boolean isSatisfiedPair(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, TeachingAssignment a1, TeachingAssignment a2) { 104 if (isRequired() || (!isProhibited() && getPreference() <= 0)) 105 return a1.getInstructor().equals(a2.getInstructor()); 106 else if (isProhibited() || (!isRequired() && getPreference() > 0)) 107 return !a1.getInstructor().equals(a2.getInstructor()); 108 return true; 109 } 110 111 @Override 112 public void computeConflicts(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, TeachingAssignment value, Set<TeachingAssignment> conflicts) { 113 if (isHard()) { 114 for (TeachingRequest.Variable request: variables()) { 115 if (request.equals(value.variable())) continue; 116 117 TeachingAssignment conflict = assignment.getValue(request); 118 if (conflict == null) continue; 119 120 if (!isSatisfiedPair(assignment, value, conflict)) 121 conflicts.add(conflict); 122 } 123 } 124 } 125 126 /** 127 * Current constraint preference (if soft) 128 * @param assignment current assignment 129 * @param value proposed change 130 * @return change in the current preference value of this constraint 131 */ 132 public int getCurrentPreference(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, TeachingAssignment value) { 133 if (isHard()) return 0; // no preference 134 if (countAssignedVariables(assignment) + (assignment.getValue(value.variable()) == null ? 1 : 0) < 2) return 0; // not enough variables 135 int nrViolatedPairsAfter = 0; 136 int nrViolatedPairsBefore = 0; 137 for (TeachingRequest.Variable v1 : variables()) { 138 for (TeachingRequest.Variable v2 : variables()) { 139 if (v1.getId() >= v2.getId()) continue; 140 TeachingAssignment p1 = (v1.equals(value.variable()) ? null : assignment.getValue(v1)); 141 TeachingAssignment p2 = (v2.equals(value.variable()) ? null : assignment.getValue(v2)); 142 if (p1 != null && p2 != null && !isSatisfiedPair(assignment, p1, p2)) 143 nrViolatedPairsBefore ++; 144 if (v1.equals(value.variable())) p1 = value; 145 if (v2.equals(value.variable())) p2 = value; 146 if (p1 != null && p2 != null && !isSatisfiedPair(assignment, p1, p2)) 147 nrViolatedPairsAfter ++; 148 } 149 } 150 return (nrViolatedPairsAfter > 0 ? Math.abs(iPreference) * nrViolatedPairsAfter : 0) - (nrViolatedPairsBefore > 0 ? Math.abs(iPreference) * nrViolatedPairsBefore : 0); 151 } 152 153 /** 154 * Current constraint preference (if soft) 155 * @param assignment current assignment 156 * @return number of violated pairs weighted by the absolute value of the preference 157 */ 158 public int getCurrentPreference(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 159 if (isHard()) return 0; // no preference 160 if (countAssignedVariables(assignment) < 2) return 0; // not enough variable 161 int nrViolatedPairs = 0; 162 for (TeachingRequest.Variable v1 : variables()) { 163 TeachingAssignment p1 = assignment.getValue(v1); 164 if (p1 == null) continue; 165 for (TeachingRequest.Variable v2 : variables()) { 166 TeachingAssignment p2 = assignment.getValue(v2); 167 if (p2 == null || v1.getId() >= v2.getId()) continue; 168 if (!isSatisfiedPair(assignment, p1, p2)) nrViolatedPairs++; 169 } 170 } 171 return (nrViolatedPairs > 0 ? Math.abs(iPreference) * nrViolatedPairs : 0); 172 } 173 174 @Override 175 public Context createAssignmentContext(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 176 return new Context(assignment); 177 } 178 179 /** 180 * Same Instructor Constraint Context. This context keeps the last preference value and updates the {@link SameInstructor} criterion. 181 * 182 */ 183 public class Context implements AssignmentConstraintContext<TeachingRequest.Variable, TeachingAssignment> { 184 private int iLastPreference = 0; 185 186 public Context(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 187 updateCriterion(assignment); 188 } 189 190 @Override 191 public void assigned(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, TeachingAssignment value) { 192 updateCriterion(assignment); 193 } 194 195 @Override 196 public void unassigned(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment, TeachingAssignment value) { 197 updateCriterion(assignment); 198 } 199 200 /** 201 * Update the current preference value 202 * @param assignment current assignment 203 */ 204 private void updateCriterion(Assignment<TeachingRequest.Variable, TeachingAssignment> assignment) { 205 if (!isHard()) { 206 getModel().getCriterion(SameInstructor.class).inc(assignment, -iLastPreference); 207 iLastPreference = getCurrentPreference(assignment); 208 getModel().getCriterion(SameInstructor.class).inc(assignment, iLastPreference); 209 } 210 } 211 212 /** 213 * Current preference value (see {@link SameInstructorConstraint#getCurrentPreference(Assignment)}) 214 * @return current preference value 215 */ 216 public int getPreference() { return iLastPreference; } 217 } 218}