001package net.sf.cpsolver.exam.criteria; 002 003import java.util.Set; 004 005import net.sf.cpsolver.exam.criteria.additional.DistributionViolation; 006import net.sf.cpsolver.exam.model.Exam; 007import net.sf.cpsolver.exam.model.ExamDistributionConstraint; 008import net.sf.cpsolver.exam.model.ExamModel; 009import net.sf.cpsolver.exam.model.ExamPlacement; 010import net.sf.cpsolver.ifs.solver.Solver; 011import 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 Tomáš Müller<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 */ 043public 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}