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}