    001package org.cpsolver.studentsct.online.selection;
    002
    003import java.util.List;
    004
    005import org.cpsolver.coursett.model.RoomLocation;
    006import org.cpsolver.coursett.model.TimeLocation;
    007import org.cpsolver.ifs.assignment.Assignment;
    008import org.cpsolver.ifs.util.DataProperties;
    009import org.cpsolver.ifs.util.ToolBox;
    010import org.cpsolver.studentsct.model.Choice;
    011import org.cpsolver.studentsct.model.Course;
    012import org.cpsolver.studentsct.model.CourseRequest;
    013import org.cpsolver.studentsct.model.Enrollment;
    014import org.cpsolver.studentsct.model.Request;
    015import org.cpsolver.studentsct.model.Section;
    016import org.cpsolver.studentsct.weights.StudentWeights;
    017
    018/**
    019 * Re-scheduling variant of {@link StudentWeights} model. It is based on the
    020 * {@link StudentSchedulingAssistantWeights}. This model is used when a single
    021 * course of a student is rescheduled. Following criteria are included:
    022 * <ul>
    023 * <li>StudentWeights.SameChoiceFactor .. penalization of selecting a different
    024 * choice (see {@link Choice})
    025 * <li>StudentWeights.SameRoomsFactor .. penalization of selecting different
    026 * room
    027 * <li>StudentWeights.SameTimeFactor .. penalization of selecting different time
    028 * <li>StudentWeights.SameNameFactor .. penalization of selecting different
    029 * section (section name / external id does not match)
    030 * </ul>
    031 *
    032 * @version StudentSct 1.3 (Student Sectioning)<br>
    033 *          Copyright (C) 2014 Tomáš Müller<br>
    034 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
    035 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
    036 * <br>
    037 *          This library is free software; you can redistribute it and/or modify
    038 *          it under the terms of the GNU Lesser General Public License as
    039 *          published by the Free Software Foundation; either version 3 of the
    040 *          License, or (at your option) any later version. <br>
    041 * <br>
    042 *          This library is distributed in the hope that it will be useful, but
    043 *          WITHOUT ANY WARRANTY; without even the implied warranty of
    044 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    045 *          Lesser General Public License for more details. <br>
    046 * <br>
    047 *          You should have received a copy of the GNU Lesser General Public
    048 *          License along with this library; if not see <a
    049 *          href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>.
    050 *
    051 */
    052public class ResectioningWeights extends StudentSchedulingAssistantWeights {
    053    private double iSameChoiceFactor = 0.125;
    054    private double iSameRoomsFactor = 0.007;
    055    private double iSameTimeFactor = 0.070;
    056    private double iSameNameFactor = 0.014;
    057    private LastSectionProvider iLastSectionProvider = null;
    058
    059    public ResectioningWeights(DataProperties properties) {
    060        super(properties);
    061        iSameChoiceFactor = properties.getPropertyDouble("StudentWeights.SameChoiceFactor", iSameChoiceFactor);
    062        iSameRoomsFactor = properties.getPropertyDouble("StudentWeights.SameRoomsFactor", iSameRoomsFactor);
    063        iSameTimeFactor = properties.getPropertyDouble("StudentWeights.SameTimeFactor", iSameTimeFactor);
    064        iSameNameFactor = properties.getPropertyDouble("StudentWeights.SameNameFactor", iSameNameFactor);
    065    }
    066
    067    public void setLastSectionProvider(LastSectionProvider lastSectionProvider) {
    068        iLastSectionProvider = lastSectionProvider;
    069    }
    070
    071    @Override
    072    public double getWeight(Assignment<Request, Enrollment> assignment, Enrollment enrollment) {
    073        double weight = super.getWeight(assignment, enrollment);
    074
    075        if (enrollment.isCourseRequest() && enrollment.getAssignments() != null && iLastSectionProvider != null) {
    076            int sameChoice = 0;
    077            int sameTime = 0;
    078            int sameRooms = 0;
    079            int sameName = 0;
    080            for (Section section : enrollment.getSections()) {
    081                if (iLastSectionProvider.sameLastChoice(section))
    082                    sameChoice++;
    083                if (iLastSectionProvider.sameLastTime(section))
    084                    sameTime++;
    085                if (iLastSectionProvider.sameLastRoom(section))
    086                    sameRooms++;
    087                if (iLastSectionProvider.sameLastName(section, enrollment.getCourse()))
    088                    sameName++;
    089            }
    090            CourseRequest cr = (CourseRequest) enrollment.getRequest();
    091            if (sameChoice == 0 && !cr.getSelectedChoices().isEmpty()) {
    092                for (Section section : enrollment.getSections()) {
    093                    if (cr.isSelected(section)) {
    094                        sameChoice++;
    095                        continue;
    096                    }
    097                }
    098            }
    099            double size = enrollment.getAssignments().size();
    100            double sameChoiceFraction = (size - sameChoice) / size;
    101            double sameTimeFraction = (size - sameTime) / size;
    102            double sameRoomsFraction = (size - sameRooms) / size;
    103            double sameNameFraction = (size - sameName) / size;
    104            double base = getBaseWeight(assignment, enrollment);
    105            weight -= sameChoiceFraction * base * iSameChoiceFactor;
    106            weight -= sameTimeFraction * base * iSameTimeFactor;
    107            weight -= sameRoomsFraction * base * iSameRoomsFactor;
    108            weight -= sameNameFraction * base * iSameNameFactor;
    109        }
    110
    111        return weight;
    112    }
    113
    114    public static boolean isSame(Enrollment e1, Enrollment e2) {
    115        if (e1.getSections().size() != e2.getSections().size())
    116            return false;
    117        s1: for (Section s1 : e1.getSections()) {
    118            for (Section s2 : e2.getSections())
    119                if (s1.sameChoice(s2))
    120                    continue s1;
    121            return false;
    122        }
    123        return true;
    124    }
    125
    126    public static boolean sameRooms(Section s, List<RoomLocation> rooms) {
    127        if (s.getRooms() == null && rooms == null)
    128            return true;
    129        if (s.getRooms() == null || rooms == null)
    130            return false;
    131        return s.getRooms().size() == rooms.size() && s.getRooms().containsAll(rooms);
    132    }
    133
    134    public static boolean sameTime(Section s, TimeLocation t) {
    135        if (s.getTime() == null && t == null)
    136            return true;
    137        if (s.getTime() == null || t == null)
    138            return false;
    139        return s.getTime().getStartSlot() == t.getStartSlot() && s.getTime().getLength() == t.getLength()
    140                && s.getTime().getDayCode() == t.getDayCode()
    141                && ToolBox.equals(s.getTime().getDatePatternName(), t.getDatePatternName());
    142    }
    143
    144    public static boolean sameName(Long courseId, Section s1, Section s2) {
    145        return s1.getName(courseId).equals(s2.getName(courseId));
    146    }
    147
    148    public static boolean sameChoice(Section section, Choice choice) {
    149        return choice.sameChoice(section);
    150    }
    151
    152    /**
    153     * Compare the old assignment with the current one
    154     */
    155    public static interface LastSectionProvider {
    156        /**
    157         * Check the choice (see {@link Choice})
    158         *
    159         * @param current
    160         *            current section
    161         * @return true if the choice matches
    162         */
    163        public boolean sameLastChoice(Section current);
    164
    165        /**
    166         * Check the time
    167         *
    168         * @param current
    169         *            current section
    170         * @return true if the time matches
    171         */
    172        public boolean sameLastTime(Section current);
    173
    174        /**
    175         * Check the room
    176         *
    177         * @param current
    178         *            current section
    179         * @return true if the room matches
    180         */
    181        public boolean sameLastRoom(Section current);
    182
    183        /**
    184         * Check section name (external id)
    185         *
    186         * @param current
    187         *            current section
    188         * @param course
    189         *            current course
    190         * @return true if the section name matches
    191         */
    192        public boolean sameLastName(Section current, Course course);
    193    }
    194}