001    package net.sf.cpsolver.exam.criteria;
002    
003    import java.util.Set;
004    
005    import net.sf.cpsolver.exam.criteria.additional.DistributionViolation;
006    import net.sf.cpsolver.exam.model.Exam;
007    import net.sf.cpsolver.exam.model.ExamDistributionConstraint;
008    import net.sf.cpsolver.exam.model.ExamModel;
009    import net.sf.cpsolver.exam.model.ExamPlacement;
010    import net.sf.cpsolver.ifs.solver.Solver;
011    import net.sf.cpsolver.ifs.util.DataProperties;
012    
013    /**
014     * Distribution penalty. I.e., sum weights of violated distribution
015     * constraints.
016     * <br><br>
017     * A weight of violated distribution soft constraints (see
018     * {@link ExamDistributionConstraint}) can be set by problem property
019     * Exams.RoomDistributionWeight, or in the input xml file, property
020     * roomDistributionWeight.
021     * 
022     * <br>
023     * 
024     * @version ExamTT 1.2 (Examination Timetabling)<br>
025     *          Copyright (C) 2008 - 2012 Tomas Muller<br>
026     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
027     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
028     * <br>
029     *          This library is free software; you can redistribute it and/or modify
030     *          it under the terms of the GNU Lesser General Public License as
031     *          published by the Free Software Foundation; either version 3 of the
032     *          License, or (at your option) any later version. <br>
033     * <br>
034     *          This library is distributed in the hope that it will be useful, but
035     *          WITHOUT ANY WARRANTY; without even the implied warranty of
036     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
037     *          Lesser General Public License for more details. <br>
038     * <br>
039     *          You should have received a copy of the GNU Lesser General Public
040     *          License along with this library; if not see
041     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
042     */
043    public class DistributionPenalty extends ExamCriterion {
044        protected Integer iSoftDistributions = null;
045        
046        public DistributionPenalty() {
047            iValueUpdateType = ValueUpdateType.NoUpdate; 
048        }
049        
050        
051        @Override
052        public boolean init(Solver<Exam, ExamPlacement> solver) {
053            if (super.init(solver)) {
054                iSoftDistributions = solver.getProperties().getPropertyInteger("Exam.SoftDistributions", null);
055                if (iSoftDistributions != null) {
056                    DistributionViolation dv = new DistributionViolation();
057                    getModel().addCriterion(dv);
058                    return dv.init(solver);
059                }
060            }
061            return true;
062        }
063        
064        @Override
065        public String getWeightName() {
066            return "Exams.DistributionWeight";
067        }
068        
069        @Override
070        public String getXmlWeightName() {
071            return "distributionWeight";
072        }
073        
074        @Override
075        public double getWeightDefault(DataProperties config) {
076            return 1.0;
077        }
078    
079        @Override
080        public double getValue(ExamPlacement value, Set<ExamPlacement> conflicts) {
081            int penalty = 0;
082            for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) {
083                if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()))
084                    continue;
085                boolean sat = dc.isSatisfied(value);
086                if (sat != dc.isSatisfied())
087                    penalty += (sat ? -dc.getWeight() : dc.getWeight());
088            }
089            return penalty;
090        }
091        
092        @Override
093        public boolean isRoomCriterion() { return true; }
094        
095        /**
096         * Room related distribution penalty, i.e., sum weights of violated
097         * distribution constraints
098         */
099        @Override
100        public double getRoomValue(ExamPlacement value) {
101            int penalty = 0;
102            for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) {
103                if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isRoomRelated())
104                    continue;
105                boolean sat = dc.isSatisfied(value);
106                if (sat != dc.isSatisfied())
107                    penalty += (sat ? -dc.getWeight() : dc.getWeight());
108            }
109            return penalty;
110        }
111    
112        
113        @Override
114        public boolean isPeriodCriterion() { return true; }
115        
116        @Override
117        public void inc(double value) {
118            if (iSoftDistributions != null && iSoftDistributions == value) {
119                getModel().getCriterion(DistributionViolation.class).inc(1.0);
120            } else if (iSoftDistributions != null && iSoftDistributions == -value) {
121                getModel().getCriterion(DistributionViolation.class).inc(-1.0);
122            } else {
123                super.inc(value);
124            }
125        }
126        
127        /**
128         * Period related distribution penalty, i.e., sum weights of violated
129         * distribution constraints
130         */
131        @Override
132        public double getPeriodValue(ExamPlacement value) {
133            int penalty = 0;
134            for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) {
135                if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isPeriodRelated())
136                    continue;
137                boolean sat = dc.isSatisfied(value);
138                if (sat != dc.isSatisfied())
139                    penalty += (sat ? -dc.getWeight() : dc.getWeight());
140            }
141            return penalty;
142        }
143        
144        @Override
145        protected double[] computeBounds() {
146            double[] bounds = new double[] { 0.0, 0.0 };
147            for (ExamDistributionConstraint dc : ((ExamModel)getModel()).getDistributionConstraints()) {
148                if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()))
149                    continue;
150                bounds[1] += dc.getWeight();
151            }
152            return bounds;
153        }
154    
155        @Override
156        public String toString() {
157            return (getValue() <= 0.0 ? "" : "DP:" + sDoubleFormat.format(getValue()));
158        }
159    }