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