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 * @author Tomáš Müller 029 * @version ExamTT 1.3 (Examination Timetabling)<br> 030 * Copyright (C) 2008 - 2014 Tomáš Müller<br> 031 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 032 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 033 * <br> 034 * This library is free software; you can redistribute it and/or modify 035 * it under the terms of the GNU Lesser General Public License as 036 * published by the Free Software Foundation; either version 3 of the 037 * License, or (at your option) any later version. <br> 038 * <br> 039 * This library is distributed in the hope that it will be useful, but 040 * WITHOUT ANY WARRANTY; without even the implied warranty of 041 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 042 * Lesser General Public License for more details. <br> 043 * <br> 044 * You should have received a copy of the GNU Lesser General Public 045 * License along with this library; if not see 046 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 047 */ 048public class DistributionPenalty extends ExamCriterion { 049 protected Integer iSoftDistributions = null; 050 051 public DistributionPenalty() { 052 setValueUpdateType(ValueUpdateType.NoUpdate); 053 } 054 055 @Override 056 public void setModel(Model<Exam, ExamPlacement> model) { 057 super.setModel(model); 058 iSoftDistributions = ((ExamModel)model).getProperties().getPropertyInteger("Exam.SoftDistributions", null); 059 if (iSoftDistributions != null && model.getCriterion(DistributionViolation.class) == null) { 060 DistributionViolation dv = new DistributionViolation(); 061 model.addCriterion(dv); 062 } 063 } 064 065 @Override 066 public String getWeightName() { 067 return "Exams.DistributionWeight"; 068 } 069 070 @Override 071 public String getXmlWeightName() { 072 return "distributionWeight"; 073 } 074 075 @Override 076 public double getWeightDefault(DataProperties config) { 077 return 1.0; 078 } 079 080 @Override 081 public double getValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value, Set<ExamPlacement> conflicts) { 082 int penalty = 0; 083 ExamPlacement original = assignment.getValue(value.variable()); 084 for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) { 085 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight())) 086 continue; 087 penalty += dc.countViolations(assignment, value) * dc.getWeight(); 088 if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight(); 089 } 090 return penalty; 091 } 092 093 @Override 094 public boolean isRoomCriterion() { return true; } 095 096 /** 097 * Room related distribution penalty, i.e., sum weights of violated 098 * distribution constraints 099 */ 100 @Override 101 public double getRoomValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) { 102 int penalty = 0; 103 ExamPlacement original = assignment.getValue(value.variable()); 104 for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) { 105 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isRoomRelated()) 106 continue; 107 penalty += dc.countViolations(assignment, value) * dc.getWeight(); 108 if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight(); 109 } 110 return penalty; 111 } 112 113 @Override 114 public double getValue(Assignment<Exam, ExamPlacement> assignment, Collection<Exam> variables) { 115 int penalty = 0; 116 Set<ExamDistributionConstraint> added = new HashSet<ExamDistributionConstraint>(); 117 for (Exam exam: variables) { 118 for (ExamDistributionConstraint dc : exam.getDistributionConstraints()) { 119 if (added.add(dc)) { 120 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight())) 121 continue; 122 penalty += dc.countViolations(assignment) * dc.getWeight(); 123 } 124 } 125 } 126 return penalty; 127 } 128 129 @Override 130 public boolean isPeriodCriterion() { return true; } 131 132 public void inc(Assignment<Exam, ExamPlacement> assignment, double value, ExamDistributionConstraint dc) { 133 if (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) { 134 getModel().getCriterion(DistributionViolation.class).inc(assignment, value / dc.getWeight()); 135 } else { 136 super.inc(assignment, value); 137 } 138 } 139 140 /** 141 * Period related distribution penalty, i.e., sum weights of violated 142 * distribution constraints 143 */ 144 @Override 145 public double getPeriodValue(Assignment<Exam, ExamPlacement> assignment, ExamPlacement value) { 146 int penalty = 0; 147 ExamPlacement original = assignment.getValue(value.variable()); 148 for (ExamDistributionConstraint dc : value.variable().getDistributionConstraints()) { 149 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight()) || !dc.isPeriodRelated()) 150 continue; 151 penalty += dc.countViolations(assignment, value) * dc.getWeight(); 152 if (original != null) penalty -= dc.countViolations(assignment, original) * dc.getWeight(); 153 } 154 return penalty; 155 } 156 157 @Override 158 protected double[] computeBounds(Assignment<Exam, ExamPlacement> assignment) { 159 double[] bounds = new double[] { 0.0, 0.0 }; 160 for (ExamDistributionConstraint dc : ((ExamModel)getModel()).getDistributionConstraints()) { 161 if (dc.isHard() || (iSoftDistributions != null && iSoftDistributions == dc.getWeight())) 162 continue; 163 bounds[1] += dc.getWeight() * dc.variables().size() * (dc.variables().size() - 1) / 2; 164 } 165 return bounds; 166 } 167 168 @Override 169 public String toString(Assignment<Exam, ExamPlacement> assignment) { 170 return (getValue(assignment) <= 0.0 ? "" : "DP:" + sDoubleFormat.format(getValue(assignment))); 171 } 172}