1 /* 2 * Alldiff.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 package org.jacop.constraints; 32 33 import org.jacop.core.IntDomain; 34 import org.jacop.core.IntVar; 35 import org.jacop.core.Store; 36 import org.jacop.core.Var; 37 38 import java.util.Arrays; 39 import java.util.Comparator; 40 import java.util.LinkedHashSet; 41 import java.util.List; 42 import java.util.concurrent.atomic.AtomicInteger; 43 44 45 /** 46 * Alldiff constraint assures that all FDVs has different values. It uses bounds 47 * consistency technique as described in the paper by Alejandro Lopez-Ortiz, Claude-Guy 48 * Quimper, John Tromp, Peter van Beek, "A fast and simple algorithm for bounds 49 * consistency of the alldifferent constraint", Proceedings of the 18th international 50 * joint conference on Artificial intelligence (IJCAI'03), Pages 245-250. Before using 51 * bounds consistency it calls consistency method for ground variables. 52 * <p> 53 * It extends basic functionality of Alldifferent constraint. 54 * 55 * @author Krzysztof Kuchcinski and Radoslaw Szymanek 56 * @version 4.8 57 */ 58 59 public class Alldiff extends Alldifferent { 60 61 static AtomicInteger idNumber = new AtomicInteger(0); 62 63 // it stores the store locally so all the private functions which 64 // are part of the consistency function can throw failure exception 65 // without passing store argument every time their function is called. 66 Store store; 67 68 private Comparator<Element> maxVariable = (o1, o2) -> (o1.var.max() - o2.var.max()); 69 70 private Comparator<Element> minVariable = (o1, o2) -> (o1.var.min() - o2.var.min()); 71 72 private int[] t; // holds the critical capacity pointers; that is, t[i] points to the 73 // predecessor of i in the bounds list. 74 private int[] d; // holds the differences between critical capacities; that is d[i] is 75 // the difference of capacities between bounds[i] and its predecessor 76 // element in the list bounds[t[i]]. 77 private int[] h; // holds the Hall interval pointers; that is, if h[i] < i then the 78 // half-open interval [bounds[h[i]],bounds[i]) is contained in a Hall 79 // interval, and otherwise holds a pointer to the Hall interval it 80 // belongs to. This Hall interval is represented by a tree, with the 81 // root containing the value of its right end. 82 private int[] bounds; // is a sorted array of all min’s and max’s. 83 84 private int nb; // holds the number of unique bounds. 85 86 private Element[] minsorted, maxsorted; 87 Alldiff()88 protected Alldiff() { 89 } 90 91 /** 92 * It constructs the alldiff constraint for the supplied variable. 93 * 94 * @param variables variables which are constrained to take different values. 95 */ Alldiff(IntVar[] variables)96 public Alldiff(IntVar[] variables) { 97 98 checkInputForNullness("x", variables); 99 100 this.numberId = idNumber.incrementAndGet(); 101 this.list = Arrays.copyOf(variables, variables.length); 102 this.queueIndex = 2; 103 104 int n = list.length; 105 t = new int[2 * n + 2]; 106 d = new int[2 * n + 2]; 107 h = new int[2 * n + 2]; 108 bounds = new int[2 * n + 2]; 109 110 minsorted = new Element[n]; 111 maxsorted = new Element[n]; 112 for (int i = 0; i < n; i++) { 113 Element el = new Element(); 114 el.var = list[i]; 115 minsorted[i] = el; 116 maxsorted[i] = el; 117 } 118 119 setScope(variables); 120 121 } 122 123 124 /** 125 * It constructs the alldiff constraint for the supplied variable. 126 * 127 * @param variables variables which are constrained to take different values. 128 */ Alldiff(List<? extends IntVar> variables)129 public Alldiff(List<? extends IntVar> variables) { 130 this(variables.toArray(new IntVar[variables.size()])); 131 } 132 getDefaultConsistencyPruningEvent()133 @Override public int getDefaultConsistencyPruningEvent() { 134 return IntDomain.BOUND; 135 } 136 impose(Store store)137 @Override public void impose(Store store) { 138 139 if (list.length == 0) 140 return; 141 142 super.impose(store); 143 this.store = store; 144 145 } 146 consistency(Store store)147 @Override public void consistency(Store store) { 148 149 if (store.currentQueue == queueIndex) { 150 151 int groundPos = grounded.value(); 152 while (!variableQueue.isEmpty()) { 153 154 LinkedHashSet<IntVar> fdvs = variableQueue; 155 variableQueue = new LinkedHashSet<>(); 156 157 for (IntVar Q : fdvs) 158 if (Q.singleton()) { 159 int qPos = positionMapping.get(Q); 160 if (qPos > groundPos) { 161 list[qPos] = list[groundPos]; 162 list[groundPos] = Q; 163 positionMapping.put(Q, groundPos); 164 positionMapping.put(list[qPos], qPos); 165 groundPos++; 166 for (int i = groundPos; i < list.length; i++) 167 list[i].domain.inComplement(store.level, list[i], Q.min()); 168 } else if (qPos == groundPos) { 169 groundPos++; 170 for (int i = groundPos; i < list.length; i++) 171 list[i].domain.inComplement(store.level, list[i], Q.min()); 172 } 173 } 174 } 175 grounded.update(groundPos); 176 177 if (queueIndex + 1 < store.queueNo) { 178 store.changed[queueIndex + 1].add(this); 179 return; 180 } 181 182 } 183 184 // do { 185 // store.propagationHasOccurred = false; 186 187 init(); 188 updateLB(); 189 updateUB(); 190 191 // } while (store.propagationHasOccurred); 192 } 193 init()194 private void init() { 195 int n = list.length; 196 Arrays.sort(minsorted, 0, n, minVariable); 197 Arrays.sort(maxsorted, 0, n, maxVariable); 198 199 int min = minsorted[0].var.min(); 200 int max = maxsorted[0].var.max() + 1; 201 int last = min - 2; 202 int nb = 0; 203 bounds[0] = last; 204 int i = 0, j = 0; 205 while (true) { 206 if (i < n && min <= max) { 207 if (min != last) 208 bounds[++nb] = last = min; 209 210 minsorted[i].minrank = nb; 211 if (++i < n) 212 min = minsorted[i].var.min(); 213 214 } else { 215 if (max != last) 216 bounds[++nb] = last = max; 217 218 maxsorted[j].maxrank = nb; 219 if (++j == n) 220 break; 221 222 max = maxsorted[j].var.max() + 1; 223 } 224 } 225 this.nb = nb; 226 bounds[nb + 1] = bounds[nb] + 2; 227 } 228 updateLB()229 private void updateLB() { 230 231 for (int i = 1; i <= nb + 1; i++) { 232 t[i] = h[i] = i - 1; 233 d[i] = bounds[i] - bounds[i - 1]; 234 } 235 for (int i = 0; i < this.list.length; i++) { 236 int x = maxsorted[i].minrank; 237 int y = maxsorted[i].maxrank; 238 int z = pathmax(t, x + 1); 239 int j = t[z]; 240 241 if (--d[z] == 0) { 242 t[z] = z + 1; 243 z = pathmax(t, t[z]); 244 t[z] = j; 245 } 246 pathset(t, x + 1, z, z); 247 if (d[z] < bounds[z] - bounds[y]) { 248 throw store.failException; 249 } 250 if (h[x] > x) { 251 int w = pathmax(h, h[x]); 252 maxsorted[i].var.domain.inMin(store.level, maxsorted[i].var, bounds[w]); 253 pathset(h, x, w, w); 254 } 255 if (d[z] == bounds[z] - bounds[y]) { 256 pathset(h, h[y], j - 1, y); 257 h[y] = j - 1; 258 } 259 } 260 } 261 updateUB()262 private void updateUB() { 263 264 for (int i = 0; i <= nb; i++) { 265 t[i] = h[i] = i + 1; 266 d[i] = bounds[i + 1] - bounds[i]; 267 } 268 for (int i = this.list.length - 1; i >= 0; i--) { 269 int x = minsorted[i].maxrank; 270 int y = minsorted[i].minrank; 271 int z = pathmin(t, x - 1); 272 int j = t[z]; 273 if (--d[z] == 0) { 274 t[z] = z - 1; 275 z = pathmin(t, t[z]); 276 t[z] = j; 277 } 278 pathset(t, x - 1, z, z); 279 if (d[z] < bounds[y] - bounds[z]) { 280 throw store.failException; 281 } 282 if (h[x] < x) { 283 int w = pathmin(h, h[x]); 284 minsorted[i].var.domain.inMax(store.level, minsorted[i].var, bounds[w] - 1); 285 pathset(h, x, w, w); 286 } 287 if (d[z] == bounds[y] - bounds[z]) { 288 pathset(h, h[y], j + 1, y); 289 h[y] = j + 1; 290 } 291 } 292 } 293 pathset(int[] v, int start, int end, int to)294 private void pathset(int[] v, int start, int end, int to) { 295 int next = start; 296 int prev = next; 297 while (prev != end) { 298 next = v[prev]; 299 v[prev] = to; 300 prev = next; 301 } 302 } 303 pathmin(int[] v, int i)304 private int pathmin(int[] v, int i) { 305 while (v[i] < i) 306 i = v[i]; 307 308 return i; 309 } 310 pathmax(int[] v, int i)311 private int pathmax(int[] v, int i) { 312 while (v[i] > i) 313 i = v[i]; 314 315 return i; 316 } 317 toString()318 @Override public String toString() { 319 320 StringBuilder result = new StringBuilder(id()); 321 result.append(" : Alldiff(["); 322 323 for (int i = 0; i < list.length; i++) { 324 result.append(list[i]); 325 if (i < list.length - 1) 326 result.append(", "); 327 } 328 329 result.append("])"); 330 331 return result.toString(); 332 333 } 334 335 // Overwritten as QueueForwardQueue checks that constraint has declared this method. queueVariable(int level, Var var)336 @Override public void queueVariable(int level, Var var) { 337 super.queueVariable(level, var); 338 } 339 340 private static class Element { 341 private IntVar var; 342 private int minrank, maxrank; 343 } 344 345 } 346 347