001    package net.sf.cpsolver.exam.criteria;
002    
003    import java.util.Map;
004    import java.util.Set;
005    
006    import net.sf.cpsolver.exam.model.Exam;
007    import net.sf.cpsolver.exam.model.ExamModel;
008    import net.sf.cpsolver.exam.model.ExamPeriod;
009    import net.sf.cpsolver.exam.model.ExamPlacement;
010    import net.sf.cpsolver.exam.model.ExamStudent;
011    import net.sf.cpsolver.ifs.solver.Solver;
012    import net.sf.cpsolver.ifs.util.DataProperties;
013    
014    /**
015     * Number of back-to-back distance student conflicts. I.e., number of
016     * cases when an exam is attended by a student that attends some other
017     * exam at the previous {@link ExamPeriod#prev()} or following
018     * {@link ExamPeriod#next()} period and the distance
019     * {@link ExamPlacement#getDistanceInMeters(ExamPlacement)} between these two exams
020     * is greater than {@link ExamModel#getBackToBackDistance()}. Distance
021     * back-to-back conflicts are only considered between consecutive periods
022     * that are of the same day.
023     * <br><br>
024     * Distance back-to-back student conflict weight can be set by problem
025     * property Exams.DistanceBackToBackConflictWeight, or in the
026     * input xml file, property distanceBackToBackConflictWeight.
027     * 
028     * <br>
029     * 
030     * @version ExamTT 1.2 (Examination Timetabling)<br>
031     *          Copyright (C) 2008 - 2012 Tomas Muller<br>
032     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
033     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
034     * <br>
035     *          This library is free software; you can redistribute it and/or modify
036     *          it under the terms of the GNU Lesser General Public License as
037     *          published by the Free Software Foundation; either version 3 of the
038     *          License, or (at your option) any later version. <br>
039     * <br>
040     *          This library is distributed in the hope that it will be useful, but
041     *          WITHOUT ANY WARRANTY; without even the implied warranty of
042     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
043     *          Lesser General Public License for more details. <br>
044     * <br>
045     *          You should have received a copy of the GNU Lesser General Public
046     *          License along with this library; if not see
047     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
048     */
049    public class StudentDistanceBackToBackConflicts extends ExamCriterion {
050        private double iBackToBackDistance = -1;
051        
052        @Override
053        public boolean init(Solver<Exam, ExamPlacement> solver) {
054            boolean ret = super.init(solver);
055            iBackToBackDistance = solver.getProperties().getPropertyDouble("Exams.BackToBackDistance", iBackToBackDistance);
056            return ret;
057        }
058        
059        @Override
060        public String getWeightName() {
061            return "Exams.DistanceBackToBackConflictWeight";
062        }
063        
064        @Override
065        public String getXmlWeightName() {
066            return "distanceBackToBackConflictWeight";
067        }
068        
069        @Override
070        public double getWeightDefault(DataProperties config) {
071            return 25.0;
072        }
073        
074        /**
075         * Back-to-back distance. Can be set by
076         * problem property Exams.BackToBackDistance, or in the input xml file,
077         * property backToBackDistance)
078         */
079        public double getBackToBackDistance() {
080            return iBackToBackDistance;
081        }
082        
083        /**
084         * Back-to-back distance. Can be set by
085         * problem property Exams.BackToBackDistance, or in the input xml file,
086         * property backToBackDistance)
087         */
088        public void setBackToBackDistance(double backToBackDistance) {
089            iBackToBackDistance = backToBackDistance;
090        }
091    
092        @Override
093        public void getXmlParameters(Map<String, String> params) {
094            params.put(getXmlWeightName(), String.valueOf(getWeight()));
095            params.put("backToBackDistance", String.valueOf(getBackToBackDistance()));
096        }
097        
098        @Override
099        public void setXmlParameters(Map<String, String> params) {
100            try {
101                setWeight(Double.valueOf(params.get(getXmlWeightName())));
102            } catch (NumberFormatException e) {} catch (NullPointerException e) {}
103            try {
104                setBackToBackDistance(Double.valueOf(params.get("backToBackDistance")));
105            } catch (NumberFormatException e) {} catch (NullPointerException e) {}
106        }
107        
108        @Override
109        public double getValue(ExamPlacement value, Set<ExamPlacement> conflicts) {
110            Exam exam = value.variable();
111            if (getBackToBackDistance() < 0) return 0;
112            int penalty = 0;
113            for (ExamStudent s : exam.getStudents()) {
114                if (value.getPeriod().prev() != null) {
115                    if (value.getPeriod().prev().getDay() == value.getPeriod().getDay()) {
116                        for (Exam x : s.getExams(value.getPeriod().prev())) {
117                            if (x.equals(exam))
118                                continue;
119                            if (value.getDistanceInMeters(x.getAssignment()) > getBackToBackDistance())
120                                penalty++;
121                        }
122                    }
123                }
124                if (value.getPeriod().next() != null) {
125                    if (value.getPeriod().next().getDay() == value.getPeriod().getDay()) {
126                        for (Exam x : s.getExams(value.getPeriod().next())) {
127                            if (x.equals(exam))
128                                continue;
129                            if (value.getDistanceInMeters(x.getAssignment()) > getBackToBackDistance())
130                                penalty++;
131                        }
132                    }
133                }
134            }
135            return penalty;
136        }
137        
138        @Override
139        public String getName() {
140            return "Distance Back-To-Back Conflicts";
141        }
142        
143        @Override
144        public void getInfo(Map<String, String> info) {
145            if (getBackToBackDistance() >= 0.0 && getValue() != 0.0)
146                info.put(getName(), sDoubleFormat.format(getValue()));
147        }
148        
149        @Override
150        public String toString() {
151            return (getValue() <= 0.0 ? "" : "BTBd:" + sDoubleFormat.format(getValue()));
152        }
153    
154        @Override
155        public boolean isPeriodCriterion() { return false; }
156    }