    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}