1 /* 2 * ElementVariable.java 3 * This file is part of JaCoP. 4 * <p> 5 * JaCoP is a Java Constraint Programming solver. 6 * <p> 7 * Copyright (C) 2000-2008 Krzysztof Kuchcinski and Radoslaw Szymanek 8 * <p> 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU Affero General Public License as published by 11 * the Free Software Foundation, either version 3 of the License, or 12 * (at your option) any later version. 13 * <p> 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU Affero General Public License for more details. 18 * <p> 19 * Notwithstanding any other provision of this License, the copyright 20 * owners of this work supplement the terms of this License with terms 21 * prohibiting misrepresentation of the origin of this work and requiring 22 * that modified versions of this work be marked in reasonable ways as 23 * different from the original version. This supplement of the license 24 * terms is in accordance with Section 7 of GNU Affero General Public 25 * License version 3. 26 * <p> 27 * You should have received a copy of the GNU Affero General Public License 28 * along with this program. If not, see <http://www.gnu.org/licenses/>. 29 */ 30 31 32 package org.jacop.constraints; 33 34 import org.jacop.api.SatisfiedPresent; 35 import org.jacop.api.Stateful; 36 import org.jacop.api.UsesQueueVariable; 37 import org.jacop.core.*; 38 39 import java.util.*; 40 import java.util.concurrent.atomic.AtomicInteger; 41 import java.util.stream.Stream; 42 43 /** 44 * ElementVariable constraint defines a relation 45 * list[index - indexOffset] = value. 46 * <p> 47 * The first element of the list corresponds to index - indexOffset = 1. 48 * By default indexOffset is equal 0 so first value within a list corresponds to index equal 1. 49 * <p> 50 * If index has a domain from 0 to list.length-1 then indexOffset has to be equal -1 to 51 * make addressing of list array starting from 1. 52 * 53 * @author Krzysztof Kuchcinski and Radoslaw Szymanek 54 * @version 4.8 55 */ 56 57 public class ElementVariable extends Constraint implements UsesQueueVariable, Stateful, SatisfiedPresent { 58 59 static AtomicInteger idNumber = new AtomicInteger(0); 60 61 boolean firstConsistencyCheck = true; 62 int firstConsistencyLevel; 63 64 /** 65 * It specifies variable index within an element constraint list[index - indexOffset] = value. 66 */ 67 public IntVar index; 68 69 /** 70 * It specifies variable value within an element constraint list[index - indexOffset] = value. 71 */ 72 public IntVar value; 73 74 /** 75 * It specifies indexOffset within an element constraint list[index - indexOffset] = value. 76 */ 77 public final int indexOffset; 78 79 /** 80 * It specifies list of variables within an element constraint list[index - indexOffset] = value. 81 * The list is addressed by positive integers ({@code >=1}) if indexOffset is equal to 0. 82 */ 83 public IntVar list[]; 84 85 boolean indexHasChanged = false; 86 87 IntDomain indexRange; 88 89 LinkedHashSet<IntVar> variableQueue = new LinkedHashSet<IntVar>(); 90 91 Map<IntVar, Integer> mapping = Var.createEmptyPositioning(); 92 93 Map<IntVar, List<Integer>> duplicates = Var.createEmptyPositioning(); 94 95 /** 96 * It constructs an element constraint. 97 * 98 * @param index variable index 99 * @param list list of variables from which an index-th element is taken 100 * @param value a value of the index-th element from list 101 * @param indexOffset shift applied to index variable. 102 */ ElementVariable(IntVar index, IntVar[] list, IntVar value, int indexOffset)103 public ElementVariable(IntVar index, IntVar[] list, IntVar value, int indexOffset) { 104 105 checkInputForNullness(new String[] {"index", "value"}, new Object[] {index, value}); 106 checkInputForNullness("list", list); 107 108 queueIndex = 2; 109 110 this.indexOffset = indexOffset; 111 this.numberId = idNumber.incrementAndGet(); 112 this.index = index; 113 this.value = value; 114 this.list = Arrays.copyOf(list, list.length); 115 this.indexRange = new IntervalDomain(1 + this.indexOffset, list.length + this.indexOffset); 116 117 setScope(Stream.concat(Stream.of(index), Stream.concat(Arrays.stream(list), Stream.of(value)))); 118 119 } 120 121 /** 122 * It constructs an element constraint. 123 * 124 * @param index variable index 125 * @param list list of variables from which an index-th element is taken 126 * @param value a value of the index-th element from list 127 */ ElementVariable(IntVar index, List<? extends IntVar> list, IntVar value)128 public ElementVariable(IntVar index, List<? extends IntVar> list, IntVar value) { 129 this(index, list.toArray(new IntVar[list.size()]), value, 0); 130 } 131 132 /** 133 * It constructs an element constraint. 134 * 135 * @param index variable index 136 * @param list list of variables from which an index-th element is taken 137 * @param value a value of the index-th element from list 138 * @param indexOffset shift applied to index variable. 139 */ ElementVariable(IntVar index, List<? extends IntVar> list, IntVar value, int indexOffset)140 public ElementVariable(IntVar index, List<? extends IntVar> list, IntVar value, int indexOffset) { 141 this(index, list.toArray(new IntVar[list.size()]), value, indexOffset); 142 } 143 144 /** 145 * It constructs an element constraint. 146 * 147 * @param index variable index 148 * @param list list of variables from which an index-th element is taken 149 * @param value a value of the index-th element from list 150 */ ElementVariable(IntVar index, IntVar[] list, IntVar value)151 public ElementVariable(IntVar index, IntVar[] list, IntVar value) { 152 this(index, list, value, 0); 153 } 154 removeLevel(int level)155 @Override public void removeLevel(int level) { 156 if (level == firstConsistencyLevel) 157 firstConsistencyCheck = true; 158 indexHasChanged = false; 159 valueHasChanged = false; 160 variableQueue.clear(); 161 } 162 163 // For each variable from the list it specifies the values it supports 164 IntDomain[] supports; 165 166 private boolean valueHasChanged; 167 168 Random generator = new Random(2); 169 consistency(Store store)170 @Override public void consistency(Store store) { 171 172 if (index.singleton()) { 173 // index is singleton. 174 175 int position = index.value() - 1 - indexOffset; 176 value.domain.in(store.level, value, list[position].domain); 177 list[position].domain.in(store.level, list[position], value.domain); 178 179 } else { 180 // index is not singleton. 181 182 183 if (firstConsistencyCheck) { 184 185 index.domain.in(store.level, index, indexRange); 186 187 IntDomain valDomain = new IntervalDomain(); 188 for (ValueEnumeration e = index.domain.valueEnumeration(); e.hasMoreElements(); ) { 189 int position = e.nextElement() - 1 - indexOffset; 190 valDomain.addDom(list[position].domain); 191 } 192 value.domain.in(store.level, value, valDomain); 193 194 firstConsistencyCheck = false; 195 firstConsistencyLevel = store.level; 196 valueHasChanged = true; 197 indexHasChanged = true; 198 for (IntVar var : list) 199 variableQueue.add(var); 200 201 supports = new IntDomain[list.length]; 202 IntDomain temp = value.domain.cloneLight(); 203 for (int i = list.length - 1; i >= 0; i--) { 204 if (!temp.isEmpty()) { 205 supports[i] = temp.intersect(list[i].domain); 206 if (!supports[i].isEmpty()) 207 temp = temp.subtract(supports[i]); 208 } else 209 supports[i] = new IntervalDomain(); 210 } 211 } 212 213 // IntDomain valDomain = new IntervalDomain(); 214 int valMin = IntDomain.MaxInt, valMax = IntDomain.MinInt; 215 for (ValueEnumeration e = index.domain.valueEnumeration(); e.hasMoreElements(); ) { 216 int position = e.nextElement() - 1 - indexOffset; 217 // valDomain.addDom(list[position].domain); 218 int min = list[position].domain.min(); 219 int max = list[position].domain.max(); 220 valMin = (valMin > min) ? min : valMin; 221 valMax = (valMax < max) ? max : valMax; 222 } 223 value.domain.in(store.level, value, valMin, valMax); 224 // value.domain.in(store.level, value, valDomain); 225 226 // Consequtive execution of the consistency function. 227 228 if (valueHasChanged) { 229 230 for (ValueEnumeration e = index.domain.valueEnumeration(); e.hasMoreElements(); ) { 231 232 int position = e.nextElement() - 1 - indexOffset; 233 234 if (!list[position].domain.isIntersecting(value.domain)) { 235 236 index.domain.inComplement(store.level, index, position + 1 + indexOffset); 237 238 list[position].removeConstraint(this); 239 240 } 241 } 242 } 243 244 if (indexHasChanged) { 245 246 IntDomain nextValueDomain = new IntervalDomain(); 247 int checkTrigger = value.getSize() - 1; 248 boolean propagation = true; 249 for (ValueEnumeration e = index.domain.valueEnumeration(); e.hasMoreElements(); ) { 250 nextValueDomain.unionAdapt(list[e.nextElement() - 1 - indexOffset].dom()); 251 if (nextValueDomain.getSize() > checkTrigger) { 252 if (nextValueDomain.contains(value.domain)) { 253 propagation = false; 254 break; 255 } else 256 checkTrigger = nextValueDomain.getSize(); 257 } 258 } 259 260 if (propagation) 261 value.domain.in(store.level, value, nextValueDomain); 262 263 } 264 265 if (!variableQueue.isEmpty()) { 266 267 Iterator<IntVar> itr = variableQueue.iterator(); 268 269 // TODO, what if one variable occurs multiple times in list? Only one 270 // occurence in the list can be active, the other ones have to be ignored. 271 272 while (itr.hasNext()) { 273 274 IntVar changedVar = itr.next(); 275 int position = mapping.get(changedVar); 276 277 // reason about possible changes to value variable. 278 if (!supports[position].isEmpty()) { 279 // changed variable supports some values in Value variable. 280 IntDomain lostSupports = supports[position].subtract(changedVar.domain); 281 lostSupports.intersectAdapt(value.domain); 282 if (!lostSupports.isEmpty()) { 283 for (ValueEnumeration enumer = lostSupports.valueEnumeration(); enumer.hasMoreElements(); ) { 284 int lostSupport = enumer.nextElement(); 285 int endingPosition = generator.nextInt(list.length - 1); 286 int nextSupportPosition = -1; 287 for (int i = endingPosition + 1; ; ) { 288 if (i == list.length) 289 i = 0; 290 if (list[i].domain.contains(lostSupport)) { 291 nextSupportPosition = i; 292 break; 293 } 294 if (i == endingPosition) 295 break; 296 i++; 297 } 298 if (nextSupportPosition != -1) { 299 supports[nextSupportPosition].unionAdapt(lostSupport); 300 supports[position].subtractAdapt(lostSupport); 301 } else { 302 value.domain.inComplement(store.level, value, lostSupport); 303 } 304 305 } 306 } 307 } 308 309 // reason about possible changes to index variable. 310 if (!changedVar.domain.isIntersecting(value.domain)) { 311 312 index.domain.inComplement(store.level, index, position + 1 + indexOffset); 313 list[position].removeConstraint(this); 314 315 List<Integer> array = duplicates.get(changedVar); 316 if (array != null) 317 for (int additionalPosition : array) 318 index.domain.inComplement(store.level, index, additionalPosition + 1 + indexOffset); 319 320 } 321 } 322 323 } 324 325 if (indexHasChanged && index.singleton()) { 326 // index is singleton. 327 328 int position = index.value() - 1 - indexOffset; 329 value.domain.in(store.level, value, list[position].domain); 330 list[position].domain.in(store.level, list[position], value.domain); 331 } 332 333 indexHasChanged = false; 334 valueHasChanged = false; 335 variableQueue.clear(); 336 337 } 338 } 339 getDefaultConsistencyPruningEvent()340 @Override public int getDefaultConsistencyPruningEvent() { 341 return IntDomain.ANY; 342 } 343 impose(Store store)344 @Override public void impose(Store store) { 345 346 super.impose(store); 347 348 for (int i = 0; i < list.length; i++) { 349 Integer oldInteger = mapping.put(list[i], i); 350 if (oldInteger != null) { 351 List<Integer> array = duplicates.get(list[i]); 352 if (array != null) 353 array.add(i); 354 else { 355 array = new ArrayList<Integer>(); 356 array.add(i); 357 duplicates.put(list[i], array); 358 } 359 } 360 } 361 362 } 363 queueVariable(int level, Var var)364 @Override public void queueVariable(int level, Var var) { 365 366 if (var == index) { 367 indexHasChanged = true; 368 return; 369 } 370 371 if (var == value) { 372 valueHasChanged = true; 373 return; 374 } 375 376 variableQueue.add((IntVar) var); 377 378 } 379 satisfied()380 @Override public boolean satisfied() { 381 boolean sat = value.singleton(); 382 if (sat) { 383 int v = value.min(); 384 ValueEnumeration e = index.domain.valueEnumeration(); 385 while (sat && e.hasMoreElements()) { 386 IntVar fdv = list[e.nextElement() - 1 - indexOffset]; 387 sat = fdv.singleton() && fdv.min() == v; 388 } 389 } 390 return sat; 391 } 392 toString()393 @Override public String toString() { 394 395 StringBuffer result = new StringBuffer(id()); 396 397 result.append(" : elementVariable").append("( ").append(index).append(", ["); 398 399 for (int i = 0; i < list.length; i++) { 400 result.append(list[i]); 401 402 if (i < list.length - 1) 403 result.append(", "); 404 } 405 406 result.append("], ").append(value).append(" )"); 407 408 return result.toString(); 409 410 } 411 412 } 413