001package org.cpsolver.ifs.perturbations; 002 003import java.util.Collection; 004import java.util.Locale; 005import java.util.Map; 006import java.util.Set; 007 008import org.cpsolver.ifs.assignment.Assignment; 009import org.cpsolver.ifs.extension.Extension; 010import org.cpsolver.ifs.extension.ViolatedInitials; 011import org.cpsolver.ifs.model.Model; 012import org.cpsolver.ifs.model.Value; 013import org.cpsolver.ifs.model.Variable; 014import org.cpsolver.ifs.solution.Solution; 015import org.cpsolver.ifs.solver.Solver; 016import org.cpsolver.ifs.util.DataProperties; 017 018 019/** 020 * Default computation of perturbation penalty (minimal perturbation problem). <br> 021 * <br> 022 * A distance function can be defined with the help of perturbations. A 023 * perturbation is a variable that has a different value in the solutions of the 024 * initial and the new problem. Some perturbations must be present in each new 025 * solution. So called input perturbation means that a variable must have 026 * different values in the initial and changed problem because of some input 027 * changes (e.g., a course must be scheduled at a different time in the changed 028 * problem). The distance function can be defined as the number of additional 029 * perturbations. They are given by subtraction of the final number of 030 * perturbations and the number of input perturbations (variables without 031 * initial assignments). <br> 032 * <br> 033 * This implementation is easily extendable. It disassemble all the available 034 * cases into a comparison of the initial and the assigned value different each 035 * other. So, the only method which is needed to be changed is 036 * {@link DefaultPerturbationsCounter#getPenalty(Assignment, Value, Value)}. Its current 037 * implementation is: 038 * <pre> 039 * <code> 040 * protected double getPenalty(Value assignedValue, Value initialValue) { 041 * return 1.0; 042 * } 043 * </code> 044 * </pre> 045 * It is called only when assignedValue is different to initialValue. 046 * 047 * @see Solver 048 * @see Solution 049 * @see Variable 050 * 051 * @author Tomáš Müller 052 * @version IFS 1.3 (Iterative Forward Search)<br> 053 * Copyright (C) 2006 - 2014 Tomáš Müller<br> 054 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 055 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 056 * <br> 057 * This library is free software; you can redistribute it and/or modify 058 * it under the terms of the GNU Lesser General Public License as 059 * published by the Free Software Foundation; either version 3 of the 060 * License, or (at your option) any later version. <br> 061 * <br> 062 * This library is distributed in the hope that it will be useful, but 063 * WITHOUT ANY WARRANTY; without even the implied warranty of 064 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 065 * Lesser General Public License for more details. <br> 066 * <br> 067 * You should have received a copy of the GNU Lesser General Public 068 * License along with this library; if not see 069 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 070 * 071 * @param <V> Variable 072 * @param <T> Value 073 */ 074public class DefaultPerturbationsCounter<V extends Variable<V, T>, T extends Value<V, T>> implements PerturbationsCounter<V, T> { 075 private ViolatedInitials<V, T> iViolatedInitials = null; 076 protected static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00", new java.text.DecimalFormatSymbols(Locale.US)); 077 078 /** 079 * Constructor 080 * 081 * @param properties 082 * input configuration 083 */ 084 public DefaultPerturbationsCounter(DataProperties properties) { 085 } 086 087 /** Initialization */ 088 @Override 089 public void init(Solver<V, T> solver) { 090 for (Extension<V, T> extension : solver.getExtensions()) { 091 if (ViolatedInitials.class.isInstance(extension)) 092 iViolatedInitials = (ViolatedInitials<V, T>) extension; 093 } 094 } 095 096 @Override 097 public double getPerturbationPenalty(Assignment<V, T> assignment, Model<V, T> model) { 098 double penalty = 0.0; 099 for (V variable : model.variablesWithInitialValue()) { 100 T value = assignment.getValue(variable); 101 if (value != null && variable.getInitialAssignment() != null && !value.equals(variable.getInitialAssignment())) 102 penalty += getPenaltyD(assignment, value, variable.getInitialAssignment()); 103 } 104 return penalty; 105 } 106 107 @Override 108 public double getPerturbationPenalty(Assignment<V, T> assignment, Model<V, T> model, Collection<V> variables) { 109 double penalty = 0.0; 110 for (V variable : variables) { 111 T value = assignment.getValue(variable); 112 if (value != null && variable.getInitialAssignment() != null && !value.equals(variable.getInitialAssignment())) 113 penalty += getPenaltyD(assignment, value, variable.getInitialAssignment()); 114 } 115 return penalty; 116 } 117 118 protected ViolatedInitials<V, T> getViolatedInitials() { 119 return iViolatedInitials; 120 } 121 122 /** 123 * Computes perturbation penalty between assigned and initial value of the 124 * same lecture. It is called only when assignedValue is different to 125 * initialValue. 126 * 127 * @param assignment current assignment 128 * @param assignedValue 129 * value assigned to a varuable (null when variable is 130 * unassigned) 131 * @param initialValue 132 * initial value of the same varaible (always not null) 133 * @return penalty 134 */ 135 protected double getPenalty(Assignment<V, T> assignment, T assignedValue, T initialValue) { 136 return 1.0; 137 } 138 139 /** 140 * Case A: initial value of a different unassigned variable cannot be 141 * assigned (computed by {@link ViolatedInitials}) 142 * 143 * @param assignment current assignment 144 * @param selectedValue 145 * value which is going to be assigned to its variable 146 * @param initialValue 147 * value of a different variable, which is currently assigned but 148 * which need to be unassifned Different variable, which is 149 * unassigned and whose initial value is in conflict with the 150 * selected value. 151 * @return penalty 152 */ 153 protected double getPenaltyA(Assignment<V, T> assignment, T selectedValue, T initialValue) { 154 return getPenalty(assignment, null, initialValue); 155 } 156 157 /** 158 * Case B: initial value is unassigned from a conflicting variable. 159 * 160 * @param assignment current assignment 161 * @param selectedValue 162 * value which is going to be unassigned to its variable 163 * @param assignedValue 164 * value currently assigned to a conflicting variable (different 165 * from the one of selectedVariable) 166 * @param initialValue 167 * initial value of the conflicting variable of assignedValue 168 * @return penalty 169 */ 170 protected double getPenaltyB(Assignment<V, T> assignment, T selectedValue, T assignedValue, T initialValue) { 171 return getPenalty(assignment, assignedValue, initialValue); 172 } 173 174 /** 175 * Case C: non-initial value is unassigned from a conflicting variable. 176 * 177 * @param assignment current assignment 178 * @param selectedValue 179 * value which is going to be unassigned to its variable 180 * @param assignedValue 181 * value currently assigned to a conflicting variable (different 182 * from the one of selectedVariable) 183 * @param initialValue 184 * initial value of the conflicting variable of assignedValue 185 * @return penalty 186 */ 187 protected double getPenaltyC(Assignment<V, T> assignment, T selectedValue, T assignedValue, T initialValue) { 188 return -getPenalty(assignment, assignedValue, initialValue); 189 } 190 191 /** 192 * Case D: different than initial value is assigned to the variable 193 * 194 * @param assignment current assignment 195 * @param selectedValue 196 * value which is going to be unassigned to its variable 197 * @param initialValue 198 * initial value of the same variable 199 * @return penalty 200 */ 201 protected double getPenaltyD(Assignment<V, T> assignment, T selectedValue, T initialValue) { 202 return getPenalty(assignment, selectedValue, initialValue); 203 } 204 205 @Override 206 public double getPerturbationPenalty(Assignment<V, T> assignment, Model<V, T> model, T selectedValue, Collection<T> conflicts) { 207 double penalty = 0; 208 Set<T> violations = (getViolatedInitials() == null ? null : getViolatedInitials().getViolatedInitials(selectedValue)); 209 if (violations != null) 210 for (T aValue : violations) { 211 if (assignment.getValue(aValue.variable()) == null) 212 penalty += getPenaltyA(assignment, selectedValue, aValue); 213 } 214 if (conflicts != null) { 215 for (T conflictValue : conflicts) { 216 T initialValue = conflictValue.variable().getInitialAssignment(); 217 if (initialValue != null) { 218 if (initialValue.equals(conflictValue)) 219 penalty += getPenaltyB(assignment, selectedValue, conflictValue, initialValue); 220 else { 221 if (violations == null || !violations.contains(initialValue)) 222 penalty += getPenaltyC(assignment, selectedValue, conflictValue, initialValue); 223 } 224 } 225 } 226 } 227 if (selectedValue.variable().getInitialAssignment() != null && !selectedValue.equals(selectedValue.variable().getInitialAssignment())) 228 penalty += getPenaltyD(assignment, selectedValue, selectedValue.variable().getInitialAssignment()); 229 return penalty; 230 } 231 232 @Override 233 public void getInfo(Assignment<V, T> assignment, Model<V, T> model, Map<String, String> info) { 234 if (model.variablesWithInitialValue().size() > 0) 235 info.put("Perturbations: Total penalty", sDoubleFormat.format(getPerturbationPenalty(assignment, model))); 236 } 237 238 @Override 239 public void getInfo(Assignment<V, T> assignment, Model<V, T> model, Map<String, String> info, Collection<V> variables) { 240 if (model.variablesWithInitialValue().size() > 0) 241 info.put("Perturbations: Total penalty", sDoubleFormat.format(getPerturbationPenalty(assignment, model, variables))); 242 } 243}