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