001package org.cpsolver.exam.criteria; 002 003import java.util.Collection; 004import java.util.HashSet; 005import java.util.Set; 006 007import org.cpsolver.exam.criteria.additional.DistributionViolation; 008import org.cpsolver.exam.model.Exam; 009import org.cpsolver.exam.model.ExamDistributionConstraint; 010import org.cpsolver.exam.model.ExamModel; 011import org.cpsolver.exam.model.ExamPlacement; 012import org.cpsolver.ifs.assignment.Assignment; 013import org.cpsolver.ifs.model.Model; 014import org.cpsolver.ifs.util.DataProperties; 015 016 017/** 018 * Distribution penalty. I.e., sum weights of violated distribution 019 * constraints. 020 * <br><br> 021 * A weight of violated distribution soft constraints (see 022 * {@link ExamDistributionConstraint}) can be set by problem property 023 * Exams.RoomDistributionWeight, or in the input xml file, property 024 * roomDistributionWeight. 025 * 026 * <br> 027 * 028 * @version ExamTT 1.3 (Examination Timetabling)<br> 029 * Copyright (C) 2008 - 2014 Tomáš Müller<br> 030 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 031 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 032 * <br> 033 * This library is free software; you can redistribute it and/or modify 034 * it under the terms of the GNU Lesser General Public License as 035 * published by the Free Software Foundation; either version 3 of the 036 * License, or (at your option) any later version. <br> 037 * <br> 038 * This library is distributed in the hope that it will be useful, but 039 * WITHOUT ANY WARRANTY; without even the implied warranty of 040 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 041 * Lesser General Public License for more details. <br> 042 * <br> 043 * You should have received a copy of the GNU Lesser General Public 044 * License along with this library; if not see 045 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 046 */ 047public class DistributionPenalty extends ExamCriterion { 048 protected Integer iSoftDistributions = null; 049 050 public DistributionPenalty() { 051 setValueUpdateType(ValueUpdateType.NoUpdate); 052 } 053 054 @Override 055 public void setModel(Model<Exam, ExamPlacement> model) { 056 super.setModel(model); 057 iSoftDistributions = ((ExamModel)model).getProperties().getPropertyInteger("Exam.SoftDistributions", null); 058 if (iSoftDistributions != null && model.getCriterion(DistributionViolation.class) == null) { 059 DistributionViolation dv = new DistributionViolation(); 060 model.addCriterion(dv); 061 } 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(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value, Set<ExamPlacement> conflicts) { 081 int penalty = 0; 082 ExamPlacement original = assignment.getValue(value.variable()); 083 for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) { 084 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight())) 085 continue; 086 penalty += dc.countViolations(assignment, value) * dc.getWeight(); 087 if (original != null) penalty -= dc.countViolations(assignment, original) * 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(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) { 101 int penalty = 0; 102 ExamPlacement original = assignment.getValue(value.variable()); 103 for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) { 104 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isRoomRelated()) 105 continue; 106 penalty += dc.countViolations(assignment, value) * dc.getWeight(); 107 if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight(); 108 } 109 return penalty; 110 } 111 112 @Override 113 public double getValue(Assignment<Exam, ExamPlacement> assignment, Collection<Exam> variables) { 114 int penalty = 0; 115 Set<ExamDistributionConstraint> added = new HashSet<ExamDistributionConstraint>(); 116 for (Exam exam: variables) { 117 for (ExamDistributionConstraint dc : exam.getDistributionConstraints()) { 118 if (added.add(dc)) { 119 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight())) 120 continue; 121 penalty += dc.countViolations(assignment) * dc.getWeight(); 122 } 123 } 124 } 125 return penalty; 126 } 127 128 @Override 129 public boolean isPeriodCriterion() { return true; } 130 131 public void inc(Assignment<Exam, ExamPlacement> assignment, double value, ExamDistributionConstraint dc) { 132 if (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) { 133 getModel().getCriterion(DistributionViolation.class).inc(assignment, value / dc.getWeight()); 134 } else { 135 super.inc(assignment, value); 136 } 137 } 138 139 /** 140 * Period related distribution penalty, i.e., sum weights of violated 141 * distribution constraints 142 */ 143 @Override 144 public double getPeriodValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) { 145 int penalty = 0; 146 ExamPlacement original = assignment.getValue(value.variable()); 147 for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) { 148 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isPeriodRelated()) 149 continue; 150 penalty += dc.countViolations(assignment, value) * dc.getWeight(); 151 if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight(); 152 } 153 return penalty; 154 } 155 156 @Override 157 protected double[] computeBounds(Assignment<Exam, ExamPlacement> assignment) { 158 double[] bounds = new double[] { 0.0, 0.0 }; 159 for (ExamDistributionConstraint dc : ((ExamModel)getModel()).getDistributionConstraints()) { 160 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight())) 161 continue; 162 bounds[1] += dc.getWeight() * dc.variables().size() * (dc.variables().size() - 1) / 2; 163 } 164 return bounds; 165 } 166 167 @Override 168 public String toString(Assignment<Exam, ExamPlacement> assignment) { 169 return (getValue(assignment) <= 0.0 ? "" : "DP:" + sDoubleFormat.format(getValue(assignment))); 170 } 171}