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 }