001 package net.sf.cpsolver.ifs.model; 002 003 import java.util.*; 004 005 import net.sf.cpsolver.ifs.util.*; 006 007 /** 008 * Generic constraint. 009 * <br><br> 010 * Like in other traditional Constraint Logic Programming (CLP) frameworks, the input problem consists of variables, 011 * values and constraints. Each constraint is defined over a subset of the problem variables and it prohibits some 012 * combinations of values which these variables can simultaneously take. In usual CSP problems, all constraints are 013 * binary (or the problem is transformed into an equivalent problem with only binary constrains before the search is 014 * started) since most of the consistency and filtering techniques are designed only for binary constraints. 015 * In such a case, the procedure computing conflicting variables is rather simple and it returns an unambiguous set of 016 * variables. It enumerates all the constraints which contain the selected variable and which are not consistent with 017 * the selected value. It returns all the variables of such constraints, different from the selected variable. 018 * <br><br> 019 * On the other hand, most of real problems have plenty of multi-variable constraints, like, for instance, resource 020 * constraint in timetabling. Such resource constraint enforces the rule that none of the variables which are using 021 * the given resource can be overlapping in time (if the resource has capacity one) or that the amount of the resource 022 * used at a time does not exceed its capacity. It is not very useful to replace such resource constraint by a set of 023 * binary constraints (e.g., prohibiting two overlapping placements in time of two particular events using the same 024 * resource), since this approach usually ends up with thousands of constraints. Also, there is usually a much more 025 * effective consistency and/or filtering technique working with the original constraint (for instance, "cumulative" 026 * constraint is usually used for modelling resource constraints in CLP). 027 * <br><br> 028 * Using multi-variable constraints, the set of conflicting variables returned by the procedure computing conflicting 029 * variables can differ according to its implementation. For instance, we can have a constraint A+B=C where A and C 030 * is already assigned to A=3 and C=5. Then if the assignment B=3 is selected, either A or B or both A and B can be 031 * unassigned to make the problem {A=3, B=3, C=5} consistent with the constraint A+B=C. Intuitively, there should be 032 * minimal number of variables unassigned in each iteration step (we are trying to increase the number of the 033 * assigned variables during the search). Also, for many constraints, it is possible to find inconsistencies even 034 * when not all variables of the constraint are yet assigned. For instance, if there are two lectures using the 035 * same room at the same time, we know that one of them needs to be unassigned even when there are unassigned lectures 036 * which will also need to be placed in that room. 037 * <br><br> 038 * In the current implementation, each hard constraint needs to implement the procedure 039 * {@link Constraint#computeConflicts(Value, Set)} which returns all the already assigned values that are incompatible we 040 * the selected assignment (value which is to be assigned to its variable). This procedure is called for all constraints 041 * which contain the selected variable in an ordered manner. Furthermore, this order can be changed during the search. 042 * Moreover, the computed set of conflicting variables is passed to this {@link Constraint#computeConflicts(Value, Set)} 043 * procedure as a parameter, so the constraint can "see" what variables are already selected for unassignment by 044 * previously processed constraints. This way, we are not computing the very minimal set of conflicting variables, 045 * however, we allow for computing this set in an efficient way. It can be also tuned for a particular problem by 046 * changing the order of constraints. 047 * <br><br> 048 * Also note that each constraint can keep its notion about the assigned variables. For instance, the resource 049 * constraint of a particular room can memorize a look-up table stating what lecture is assigned in what time slot(s), 050 * so for the computation of the conflicting lectures it only looks through the appropriate fields of this table. The 051 * implementation is based on {@link Constraint#assigned(long,Value)} and {@link Constraint#unassigned(long,Value)} 052 * methods that are responsible to keeping the problem consistent with the constraint. Also note that this default 053 * consistency technique is defined on a problem level and it can be changed by a more dedicated one, implemented for a 054 * particular problem. 055 * 056 * @see Variable 057 * @see Model 058 * @see net.sf.cpsolver.ifs.solver.Solver 059 * 060 * @version 061 * IFS 1.1 (Iterative Forward Search)<br> 062 * Copyright (C) 2006 Tomáš Müller<br> 063 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 064 * Lazenska 391, 76314 Zlin, Czech Republic<br> 065 * <br> 066 * This library is free software; you can redistribute it and/or 067 * modify it under the terms of the GNU Lesser General Public 068 * License as published by the Free Software Foundation; either 069 * version 2.1 of the License, or (at your option) any later version. 070 * <br><br> 071 * This library is distributed in the hope that it will be useful, 072 * but WITHOUT ANY WARRANTY; without even the implied warranty of 073 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 074 * Lesser General Public License for more details. 075 * <br><br> 076 * You should have received a copy of the GNU Lesser General Public 077 * License along with this library; if not, write to the Free Software 078 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 079 */ 080 081 public abstract class Constraint { 082 private static IdGenerator iIdGenerator = new IdGenerator(); 083 084 protected long iId = -1; 085 086 private Vector iVariables = new FastVector(); 087 protected EnumerableCollection iAssignedVariables = new FastVector(); 088 private Model iModel = null; 089 protected Vector iConstraintListeners = null; 090 091 /** Constructor */ 092 public Constraint() { 093 iId = iIdGenerator.newId(); 094 } 095 096 /** The model which the constraint belongs to */ 097 public Model getModel() {return iModel; } 098 /** Sets the model which the constraint belongs to */ 099 public void setModel(Model model) { iModel=model; } 100 /** The list of variables of this constraint */ 101 public Vector variables() { return iVariables; } 102 /** The list of variables of this constraint that are assigned */ 103 public EnumerableCollection assignedVariables() { return iAssignedVariables; } 104 /** The number of variables of this constraint */ 105 public int countVariables() { return variables().size(); } 106 /** The number of variables of this constraint that are assigned */ 107 public int countAssignedVariables() { return assignedVariables().size(); } 108 109 /** Add a variable to this constraint */ 110 public void addVariable(Variable variable) { 111 iVariables.addElement(variable); 112 variable.addContstraint(this); 113 if (variable.getAssignment()!=null) { 114 this.assigned(0,variable.getAssignment()); 115 if (iAssignedVariables!=null) iAssignedVariables.addElement(variable); 116 } 117 } 118 /** Remove a variable from this constraint */ 119 public void removeVariable(Variable variable) { 120 if (variable.getAssignment()!=null) { 121 this.unassigned(0,variable.getAssignment()); 122 } 123 variable.removeContstraint(this); 124 iVariables.removeElement(variable); 125 if (iAssignedVariables!=null && iAssignedVariables.contains(variable)) iAssignedVariables.removeElement(variable); 126 } 127 128 /** The only method which has to be implemented by any constraint. It returns the 129 * values which needs to be unassigned in order to make this constraint consistent 130 * with the given value if it is assigned to its variable. The computed list of 131 * conflicting values is added to the given set of conflicts. 132 * @param value value to be assigned to its varaible 133 * @param conflicts resultant set of conflicting values 134 */ 135 public abstract void computeConflicts(Value value, Set conflicts); 136 137 /** Returns true if the given assignments are consistent respecting this constraint. 138 * This method is used by MAC (see {@link net.sf.cpsolver.ifs.extension.MacPropagation}). */ 139 public boolean isConsistent(Value value1, Value value2) { 140 return true; 141 } 142 143 /** Returns true if the given assignment is inconsistent with the existing assignments 144 * respecting this constraint. This method is used by MAC (see {@link net.sf.cpsolver.ifs.extension.MacPropagation}). 145 */ 146 public boolean inConflict(Value value) { 147 Set conflicts = new HashSet(); 148 computeConflicts(value, conflicts); 149 return !conflicts.isEmpty(); 150 } 151 152 /** Given value is to be assigned to its varable. In this method, the constraint should unassigns 153 * all varaibles which are in conflict with the given assignment because of this constraint. 154 */ 155 public void assigned(long iteration, Value value) { 156 HashSet conf = null; 157 if (isHard()) { 158 conf = new HashSet(); computeConflicts(value, conf); 159 } 160 if (iConstraintListeners!=null) 161 for (Enumeration e=iConstraintListeners.elements();e.hasMoreElements();) 162 ((ConstraintListener)e.nextElement()).constraintBeforeAssigned(iteration, this, value, conf); 163 if (conf!=null) { 164 for (Iterator i=conf.iterator(); i.hasNext(); ) { 165 Value conflictValue = (Value)i.next(); 166 conflictValue.variable().unassign(iteration); 167 } 168 } 169 if (iAssignedVariables!=null) iAssignedVariables.addElement(value.variable()); 170 if (iConstraintListeners!=null) 171 for (Enumeration e=iConstraintListeners.elements();e.hasMoreElements();) 172 ((ConstraintListener)e.nextElement()).constraintAfterAssigned(iteration, this, value, conf); 173 } 174 175 /** Given value is unassigned from its varable. 176 */ 177 public void unassigned(long iteration, Value value) { 178 if (iAssignedVariables!=null) iAssignedVariables.removeElement(value.variable()); 179 } 180 181 /** Adds a constraint listener */ 182 public void addConstraintListener(ConstraintListener listener) { 183 if (iConstraintListeners==null) iConstraintListeners=new FastVector(); 184 iConstraintListeners.addElement(listener); 185 } 186 /** Removes a constraint listener */ 187 public void removeConstraintListener(ConstraintListener listener) { 188 if (iConstraintListeners==null) iConstraintListeners=new FastVector(); 189 iConstraintListeners.removeElement(listener); 190 } 191 /** Returns the list of registered constraint listeners */ 192 public Vector constraintListeners() { 193 return iConstraintListeners; 194 } 195 196 /** Unique id */ 197 public long getId() { return iId;} 198 /** Constraint's name -- for printing purposes */ 199 public String getName() { return String.valueOf(iId); } 200 /** Constraint's description -- for printing purposes */ 201 public String getDescription() { return null; } 202 public int hashCode() { return (int)iId; } 203 /** Returns true if the constraint is hard. Only hard constraints are allowed to 204 * unassign a variable when there is a conflict with a value that is being assigned */ 205 public boolean isHard() { return true; } 206 207 /** Compare two constraints for equality ({@link Constraint#getId()} is used)*/ 208 public boolean equals(Object o) { 209 if (o==null || !(o instanceof Constraint)) return false; 210 return getId()==((Constraint)o).getId(); 211 } 212 }