1 /* 2 * GCC.java 3 * This file is part of JaCoP. 4 * <p> 5 * JaCoP is a Java Constraint Programming solver. 6 * <p> 7 * Copyright (C) 2008 Jocelyne Lotfi 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.api.SatisfiedPresent; 34 import org.jacop.api.Stateful; 35 import org.jacop.api.UsesQueueVariable; 36 import org.jacop.core.*; 37 38 import java.util.*; 39 import java.util.concurrent.atomic.AtomicInteger; 40 import java.util.stream.Stream; 41 42 /** 43 * GCC constraint counts the number of occurences of given 44 * values in x variables. The counters are specified by y's. 45 * The occurence of all values in the domain of xs is counted. 46 * <p> 47 * We would like to thank Irit Katriel for making the code of GCC in C she wrote 48 * available to us. 49 * 50 * @author Jocelyne Lotfi and Radoslaw Szymanek. 51 * @version 4.8 52 */ 53 54 public class GCC extends Constraint implements UsesQueueVariable, Stateful, SatisfiedPresent { 55 56 /** 57 * TODO An improvement to increase the incrementality even further. 58 * <p> 59 * 1. The first matching uses minimal values. Remember which minimal value has changed which 60 * removed from the domain the value which was used in the matching. 61 * Reuse from old matching 1 all values smaller than the minimal which has changed. 62 * <p> 63 * Similar principle applies to matching 2 (skip the positions (variables) until 64 * the first index for which m1 did change or for which the m2 value is no longer in the 65 * domain. 66 * <p> 67 * <p> 68 * 2. Use IndexDomainView instead of local solution. 69 * <p> 70 * <p> 71 * 3. boolean variable first - is it only once in the consistency function? Then this functionality 72 * can be moved out of the while(newPropagation), if it should be executed every time consistency 73 * is executed then (it should be setup to true somewhere). 74 */ 75 76 boolean firstConsistencyCheck = true; 77 78 static AtomicInteger idNumber = new AtomicInteger(0); 79 80 /** 81 * The array which stores the first computed matching, which may not take into account 82 * the lower bound of count variables. 83 */ 84 private int[] match1; 85 86 /** 87 * The array which stores the second computed matching, which may not take into account 88 * the upper bound of count variables. 89 */ 90 private int[] match2; 91 92 /** 93 * The array which stores the third proper matching, constructed from the first one 94 * and second one so both lower and upper bounds are respected. 95 */ 96 private int[] match3; 97 98 private int[] match1XOrder; 99 private int[] match2XOrder; 100 private int[] nbOfMatchPerY; 101 102 private int[] compOfY; 103 104 private XDomain[] xDomain; 105 private int[][] yDomain; 106 107 private int xSize; 108 private int ySize; 109 110 private ArrayDeque<Integer> S1; 111 private ArrayDeque<Component> S2; 112 private PriorityQueue<XDomain> pFirst, pSecond; 113 private PriorityQueue<Integer> pCount; 114 115 private final static boolean debug = false; 116 117 private int[] domainHash; 118 119 private Map<IntVar, Integer> xNodesHash; 120 private Set<IntVar> xVariableToChange; 121 122 TimeStamp<Integer> stamp; 123 124 private int stampValue; 125 int firstConsistencyLevel; 126 127 /** 128 * It specifies variables x whose values are counted. 129 */ 130 public IntVar[] x; 131 132 /** 133 * It species variables counters for counting occurences of each possible value from the 134 * intial domain of x variables. 135 */ 136 protected IntVar[] counters; 137 138 private Comparator<XDomain> compareLowerBound = (o1, o2) -> { 139 if (o1.min() < o2.min()) 140 return -1; 141 else if (o1.min() > o2.min()) 142 return 1; 143 return 0; 144 }; 145 146 private Comparator<XDomain> sortPriorityMinOrder = (o1, o2) -> { 147 if (o1.max() < o2.max()) 148 return -1; 149 else if (o1.max() > o2.max()) 150 return 1; 151 152 return 0; 153 }; 154 155 private Comparator<Integer> sortPriorityMaxOrder = (e1, e2) -> -e1.compareTo(e2); 156 157 /** 158 * It constructs global cardinality constraint. 159 * 160 * @param x variables which values are counted. 161 * @param counters variables which count the values. 162 */ GCC(IntVar[] x, IntVar[] counters)163 public GCC(IntVar[] x, IntVar[] counters) { 164 165 checkInputForNullness(new String[] {"x", "counters"}, x, counters); 166 checkInputForDuplicationSkipSingletons("x", x); 167 168 this.queueIndex = 1; 169 numberId = idNumber.incrementAndGet(); 170 171 counters = removeZeroCounters(x, counters); 172 173 xSize = x.length; 174 ySize = counters.length; 175 176 this.x = new IntVar[xSize]; 177 this.counters = new IntVar[ySize]; 178 179 System.arraycopy(x, 0, this.x, 0, xSize); 180 System.arraycopy(counters, 0, this.counters, 0, ySize); 181 182 this.xDomain = new XDomain[xSize]; 183 this.yDomain = new int[2][ySize]; 184 185 // rest of the init 186 match1 = new int[xSize]; 187 match2 = new int[xSize]; 188 match3 = new int[xSize]; 189 match1XOrder = new int[xSize]; 190 match2XOrder = new int[xSize]; 191 192 nbOfMatchPerY = new int[ySize]; 193 compOfY = new int[ySize]; 194 195 S1 = new ArrayDeque<>(); 196 S2 = new ArrayDeque<>(); 197 pFirst = new PriorityQueue<>(10, sortPriorityMinOrder); 198 pSecond = new PriorityQueue<>(10, sortPriorityMinOrder); 199 pCount = new PriorityQueue<>(10, sortPriorityMaxOrder); 200 201 xNodesHash = Var.createEmptyPositioning(); 202 xVariableToChange = new HashSet<>(); 203 204 setScope(Stream.concat(Arrays.stream(x), Arrays.stream(counters))); 205 206 } 207 208 private Set<IntVar> zeroCounters; 209 210 /** 211 * Fix suggested by Radek: a set that keeps track of the variables that have changed and need to be revisited in the consistency method 212 */ 213 private Set<Var> changedVariables = new HashSet<>(); 214 removeZeroCounters(IntVar[] x, IntVar[] counters)215 private IntVar[] removeZeroCounters(IntVar[] x, IntVar[] counters) { 216 217 IntVar[] result; 218 219 // here I will put normalization 220 IntervalDomain d = new IntervalDomain(); 221 222 for (IntVar aX : x) 223 d = (IntervalDomain) d.union(aX.domain); 224 225 // I check the consistency of the x and y variable 226 if (d.getSize() != counters.length && (d.max() - d.min() + 1) != counters.length) 227 // if there are more y variable than x variable there is a mistake of conseption 228 // as the rest of y variables are 0 in any case. The problem is to know which y variable 229 // should not be here. With normalization we assume that it is the last ones in the 230 // list but it is an assumption, it's better to throw an exception there and let the 231 // user determine what is correct. 232 throw new IllegalArgumentException("GCC failure : join domain of x variables doesn't cover all count variables"); 233 234 // no changes required 235 if (d.getSize() == counters.length) 236 return counters; 237 238 // zero counters encountered. 239 result = new IntVar[d.getSize()]; 240 zeroCounters = new HashSet<>(); 241 242 int i = 0; 243 for (int k = d.min(); k <= d.max(); k++) 244 if (d.contains(k)) 245 result[i++] = counters[k - d.min()]; 246 else 247 zeroCounters.add(counters[k - d.min()]); 248 249 return result; 250 } 251 252 /** 253 * It constructs global cardinality constraint. 254 * 255 * @param x variables which values are counted. 256 * @param counters variables which count the values. 257 */ GCC(List<? extends IntVar> x, List<? extends IntVar> counters)258 public GCC(List<? extends IntVar> x, List<? extends IntVar> counters) { 259 260 this(x.toArray(new IntVar[x.size()]), counters.toArray(new IntVar[counters.size()])); 261 262 } 263 removeLevel(int level)264 @Override public void removeLevel(int level) { 265 if (level == firstConsistencyLevel) 266 firstConsistencyCheck = true; 267 } 268 consistency(Store store)269 @Override public void consistency(Store store) { 270 271 // the stamp is here to represent the number of x variable still 272 // not singleton and that need to be pruned (the rest is set and 273 // doesn't need to be pass though the whole calculation of matching 274 // and SCC's). The same trick can not be apply to the y as the order 275 // matter a lot. 276 277 // I take out all the xNodes that are singleton and I put them 278 // after the stamp value 279 if (firstConsistencyCheck) { 280 281 if (zeroCounters != null) 282 for (IntVar zeroCounter : zeroCounters) 283 zeroCounter.domain.in(store.level, zeroCounter, 0, 0); 284 285 int k = 0; 286 287 stamp.update(xSize); 288 289 while (k < stamp.value()) { 290 if (x[k].singleton()) { 291 if (stamp.value() > 0) { 292 stamp.update(stamp.value() - 1); 293 putToTheEnd(x, k); 294 } 295 } else { 296 // no incrementation if there is a modification in the 297 // xNodes table. The variable in the i position is no more 298 // the same and need to be check also. 299 k++; 300 } 301 } 302 firstConsistencyCheck = false; 303 firstConsistencyLevel = store.level; 304 305 assert checkXorder() : "Inconsistent X variable order: " + Arrays.toString(this.x); 306 307 } 308 309 // no need to rerun the consistancy function as the reduction on x domains doesn't affect 310 // the matching and the y count is base on the matching and doesn't affect it. So 311 // rerunning the constraint doesn't bring anything new. We can suppose that y counting 312 // achieve bound consistancy. 313 314 315 do { 316 317 store.propagationHasOccurred = false; 318 319 // Fix suggested by Radek (moved from queueVariable) 320 Set<Var> changedVariablesCopy = this.changedVariables; 321 this.changedVariables = new HashSet<Var>(); 322 for (Var var : changedVariablesCopy) { 323 // if v is singleton and is an X variable 324 if (var.singleton() && xNodesHash.containsKey(var)) { 325 // if 326 if (xNodesHash.get(var) < stamp.value()) { // changing '<=' to '<' (KK) 327 if (debug) 328 System.out.println(" in xVariableToChange: " + var); 329 if (stamp.value() > 0) { 330 stamp.update(stamp.value() - 1); 331 putToTheEnd(x, xNodesHash.get(var)); 332 } 333 } 334 } 335 } 336 337 assert checkXorder() : "Inconsistent X variable order: " + Arrays.toString(this.x); 338 339 if (debug) { 340 System.out.println("XNodes"); 341 for (int i = 0; i < xSize; i++) 342 System.out.println(x[i]); 343 System.out.println("stamp before " + stamp.value()); 344 } 345 346 stampValue = stamp.value(); 347 348 if (debug) { 349 System.out.println("stamp after " + stampValue); 350 System.out.println("XDomain"); 351 } 352 // put in the xDomain all xNodes that are not singleton 353 354 for (int i = 0; i < stampValue; i++) { 355 xDomain[i].setDomain(findPosition(x[i].min(), domainHash), findPosition(x[i].max(), domainHash)); 356 357 xDomain[i].twin = x[i]; 358 if (debug) 359 System.out.println(xDomain[i]); 360 } 361 if (debug) 362 System.out.println("YDomain"); 363 364 // put all yNodes in yDomain 365 for (int i = 0; i < ySize; i++) { 366 yDomain[0][i] = counters[i].min(); 367 yDomain[1][i] = counters[i].max(); 368 369 if (debug) 370 System.out.println(yDomain[i]); 371 } 372 373 if (debug) 374 System.out.println("take out singleton xNodes"); 375 376 // check all xNodes and if singleton change the yDomain value 377 // to count down the xNode already link to this yNode 378 for (int i = 0; i < xSize; i++) { 379 if (x[i].singleton()) { 380 //Change, check. 381 int value = findPosition(x[i].value(), domainHash); 382 if (yDomain[0][value] > 0) { 383 yDomain[0][value]--; 384 } 385 yDomain[1][value]--; 386 if (yDomain[1][value] < 0) 387 throw Store.failException; 388 } 389 } 390 391 if (debug) { 392 System.out.println("pass in consistency"); 393 System.out.println("YDomain"); 394 for (int i = 0; i < ySize; i++) 395 System.out.println(yDomain[i]); 396 } 397 398 sortXByDomainMin(); 399 400 FindGeneralizedMatching(); 401 SCCs(); 402 // I do the countConcistancy before the x pruning so the 403 // change in x variable doesn't affect the pruning of y variable 404 countBoundConsistency(store); 405 406 // do the pruning 407 for (int j = 0; j < stampValue; j++) { 408 409 assert ((match3[j] >= 0) && (match3[j] < ySize)); 410 assert ((compOfY[match3[j]] >= 0) && (compOfY[match3[j]] <= ySize)); 411 412 int cutMin = xDomain[j].min(); 413 int cutMax = xDomain[j].max(); 414 415 if (debug) 416 System.out.println("cutmax " + cutMax); 417 418 while (compOfY[match3[j]] != compOfY[cutMin]) 419 cutMin++; 420 421 while (compOfY[match3[j]] != compOfY[cutMax]) { 422 cutMax--; 423 } 424 425 int id = xNodesHash.get(xDomain[j].twin); 426 427 if (debug) 428 System.out.println("do pruning [" + x[id].min() + "," + x[id].max() + "] => [" + cutMin + "," + cutMax + "]"); 429 430 xDomain[j].setDomain(cutMin, cutMax); 431 IntVar v = x[id]; 432 v.domain.in(store.level, v, domainHash[cutMin], domainHash[cutMax]); 433 434 } 435 436 // check if the solution is still valid now the prunning done 437 // useful if max bound reduction lead to a non-existing number and 438 // the prunning is so report to the next upper bound (not necessary fitting) 439 for (int i = 0; i < xSize; i++) { 440 if (x[i].singleton()) { 441 // Change, check. 442 int value = findPosition(x[i].value(), domainHash); 443 yDomain[1][value]--; 444 if (yDomain[1][value] < 0) { 445 if (debug) 446 System.out.println("failure in putting back yNodes domain"); 447 throw Store.failException; 448 } 449 } 450 } 451 452 453 } while (store.propagationHasOccurred); 454 455 } 456 457 /** 458 * A method to be called in asserts that checks whether all grounded X variables are correctly put at the end of the list 459 * 460 * @return false if the X variable order is inconsistent 461 */ checkXorder()462 private boolean checkXorder() { 463 464 for (int i = this.stamp.value() - 1; i >= 0; i--) 465 if (this.x[i].singleton()) 466 return false; 467 468 for (int i = this.stamp.value(); i < this.x.length; i++) 469 if (!this.x[i].singleton()) 470 return false; 471 472 return true; 473 } 474 getDefaultConsistencyPruningEvent()475 @Override public int getDefaultConsistencyPruningEvent() { 476 return IntDomain.BOUND; 477 } 478 impose(Store store)479 @Override public void impose(Store store) { 480 481 stamp = new TimeStamp<>(store, xSize); 482 483 // first I will put all the xNodes in a hashTable to be able to use 484 // it with the queueVariable function 485 // KK, 2015-10-18 486 // only non ground variables need to be added 487 // no duplicates allowed 488 Var.addPositionMapping(xNodesHash, x, true, this.getClass()); 489 490 // here I will put normalization 491 IntervalDomain d = new IntervalDomain(); 492 493 for (int i = 0; i < xSize; i++) 494 d = (IntervalDomain) d.union(x[i].domain); 495 496 // I check the consistency of the x and y variable 497 if (d.getSize() != ySize) 498 // if there are more y variable than x variable there is a mistake of conseption 499 // as the rest of y variables are 0 in any case. The problem is to know which y variable 500 // should not be here. With normalization we assume that it is the last ones in the 501 // list but it is an assumption, it's better to throw an exception there and let the 502 // user determine what is correct. 503 throw new IllegalArgumentException("GCC failure : join domain of x variables doesn't cover all count variables"); 504 505 domainHash = new int[d.getSize()]; 506 IntervalDomainValueEnumeration venum = new IntervalDomainValueEnumeration(d); 507 int i = 0; 508 do { 509 510 Integer j = venum.nextElement(); 511 domainHash[i++] = j; 512 513 } while (venum.hasMoreElements()); 514 515 for (i = 0; i < xSize; i++) 516 this.xDomain[i] = new XDomain(x[i], findPosition(x[i].min(), domainHash), findPosition(x[i].max(), domainHash)); 517 518 for (i = 0; i < ySize; i++) { 519 this.yDomain[0][i] = counters[i].min(); 520 this.yDomain[1][i] = counters[i].max(); 521 } 522 super.impose(store); 523 524 } 525 queueVariable(int level, Var var)526 @Override public void queueVariable(int level, Var var) { 527 if (debug) 528 System.out.println("in queue variable " + var + " level " + level); 529 530 // Fix suggested by Radek: the queueVariable function should store the variables that are changing in a HashSet 531 this.changedVariables.add(var); 532 } 533 satisfied()534 @Override public boolean satisfied() { 535 536 if (!grounded()) 537 return false; 538 539 int count[] = new int[domainHash.length]; 540 541 for (IntVar xVar : x) { 542 int xValue = xVar.value(); 543 int position = 0; 544 for (; position < count.length && domainHash[position] != xValue; position++) 545 ; 546 assert (position < count.length); 547 count[position]++; 548 } 549 550 for (int i = 0; i < counters.length; i++) 551 if (counters[i].value() != count[i]) 552 return false; 553 554 return true; 555 } 556 557 @Override public String toString() { 558 559 StringBuilder toString = new StringBuilder(id()); 560 561 toString.append(" : GCC (["); 562 for (int i = 0; i < xSize - 1; i++) 563 toString.append(x[i].toString()).append(", "); 564 toString.append(x[xSize - 1].toString()); 565 toString.append("], ["); 566 for (int j = 0; j < ySize - 1; j++) 567 toString.append(counters[j].toString()).append(", "); 568 toString.append(counters[ySize - 1].toString()).append("])"); 569 570 return toString.toString(); 571 572 } 573 574 575 // private methods 576 577 //-----------------------FIND_GENERALIZED_MATCHING--------------------------------// 578 579 private void FindGeneralizedMatching() { 580 581 Arrays.fill(nbOfMatchPerY, 0); 582 583 if (debug) { 584 System.out.println("XDomain"); 585 for (int i = 0; i < stampValue; i++) 586 System.out.println(i + " [" + xDomain[i].min() + "-" + xDomain[i].max() + "]"); 587 } 588 // first pass 589 firstPass(); 590 591 // check we are in the good ranges for match1 and match1XOrder 592 assert (checkFirstPass()); 593 594 secondPass(); 595 596 assert (checkSecondPass()); 597 598 thirdPass(); 599 600 assert (checkThirdPass()); 601 602 } 603 604 private boolean checkFirstPass() { 605 606 for (int j = 0; j < stampValue; j++) { 607 assert (match1[j] >= 0); 608 assert (match1[j] < ySize); 609 assert (match1XOrder[j] >= 0); 610 assert (match1XOrder[j] < stampValue); 611 } 612 613 return true; 614 } 615 616 private boolean checkSecondPass() { 617 618 for (int j = 1; j < stampValue; j++) 619 assert (match2[match2XOrder[j]] >= match2[match2XOrder[j - 1]]); 620 621 return true; 622 } 623 624 private boolean checkThirdPass() { 625 626 for (int j = 0; j < stampValue; j++) { 627 assert (xDomain[j].min() <= match3[j]); 628 assert (xDomain[j].max() >= match3[j]); 629 } 630 631 return true; 632 } 633 634 private void firstPass() { 635 636 pFirst.clear(); 637 // int j = 0; 638 int xIndex = 0; 639 int maxY; 640 int match1XOrderIndex = 0; 641 int top; 642 643 for (int i = 0; i < ySize; i++) { 644 // first we add all the x which min domain is y 645 while ((xIndex < stampValue) && xDomain[xIndex].min() == i) { 646 // add a new element with index to get it back after the good element 647 // and the max of the domain of xNode to sort them. 648 xDomain[xIndex].index = xIndex; 649 pFirst.add(xDomain[xIndex]); 650 xIndex++; 651 } 652 int u = 0; 653 // second we add the maximum of these xs possible to the current y 654 maxY = yDomain[1][i]; 655 while (!pFirst.isEmpty() && u < maxY) { 656 top = (pFirst.remove()).index; // index of the first element of pFirst 657 match1[top] = i; 658 u++; 659 // match1XOrder gives the order in which xs where in the priority queue 660 // that sorted them by domain max by group of domain min. 661 // That is there are first sorted by domain min and after by domain max. 662 match1XOrder[match1XOrderIndex] = top; 663 match1XOrderIndex++; 664 665 if (xDomain[top].max() < i) { 666 if (debug) 667 System.out.println("failure first pass"); 668 669 throw Store.failException; 670 // it was checked that the min == i so max cannot be under min. Well, yes there are cases 671 // where it's useful. 672 } 673 // j++; 674 } 675 } 676 677 // do we have to check that the queue is empty ? If not it means that an x was not paired. 678 // I add the test on the queue. It is possible if the maxY = 0 and is the last one visited 679 // otherwise the element in the queue can be used by the next yNode. 680 if (!pFirst.isEmpty()) { 681 682 if (debug) 683 System.out.println("failure the queue is not empty"); 684 685 throw Store.failException; 686 687 } 688 689 if (debug) { 690 691 System.out.print("match1Xorder : "); 692 for (int aMatch1XOrder : match1XOrder) 693 System.out.print(aMatch1XOrder + " "); 694 695 System.out.println(""); 696 System.out.print("match1 : "); 697 698 for (int aMatch1 : match1) 699 System.out.print(aMatch1 + " "); 700 701 System.out.println(""); 702 } 703 704 } 705 706 private void secondPass() { 707 708 pSecond.clear(); 709 // int j = 0; 710 int top; 711 int xIndex = 0; 712 int minY; 713 int match2XOrderIndex = 0; 714 int order; 715 716 for (int i = 0; i < ySize; i++) { 717 // I should iterate on the match1XOrder instead of the normal order 718 // we take all the xs that where matched with yi in the first pass. 719 while ((xIndex < stampValue) && (match1[match1XOrder[xIndex]] == i)) { 720 order = match1XOrder[xIndex]; 721 xDomain[order].index = order; 722 pSecond.add(xDomain[order]); 723 xIndex++; 724 } 725 minY = yDomain[0][i]; 726 for (int l = 0; l < minY; l++) { 727 if (pSecond.isEmpty()) { 728 // failure need to be expressed 729 if (debug) 730 System.out.println("failure second pass"); 731 732 throw Store.failException; 733 } 734 top = pSecond.remove().index; 735 //change, check. 736 match2[top] = i; 737 738 match2XOrder[match2XOrderIndex] = top; 739 match2XOrderIndex++; 740 nbOfMatchPerY[i]++; 741 // j++; 742 } 743 while (!pSecond.isEmpty() && ((pSecond.element().max()) < i + 1)) { 744 top = pSecond.remove().index; 745 // change, check. 746 match2[top] = i; 747 match2XOrder[match2XOrderIndex] = top; 748 match2XOrderIndex++; 749 nbOfMatchPerY[i]++; 750 // j++; 751 } 752 } 753 754 755 if (debug) { 756 757 System.out.print("match2Xorder : "); 758 759 for (int aMatch2XOrder : match2XOrder) 760 System.out.print(aMatch2XOrder + " "); 761 762 System.out.println(""); 763 764 System.out.print("match2 : "); 765 for (int aMatch2 : match2) 766 System.out.print(aMatch2 + " "); 767 768 System.out.println(""); 769 } 770 771 } 772 773 private void thirdPass() { 774 775 int xIndex = stampValue - 1; 776 int e; 777 int x = 0; 778 779 System.arraycopy(match2, 0, match3, 0, stampValue); 780 781 for (int i = ySize - 1; i >= 0; i--) { 782 while ((xIndex >= 0) && (match2[match2XOrder[xIndex]] > i)) 783 xIndex--; 784 785 e = nbOfMatchPerY[i] - yDomain[1][i]; // excess of y mates 786 while (e > 0) { 787 788 assert (match2[match2XOrder[xIndex]] == i); 789 790 while (xIndex >= 0) { 791 x = match2XOrder[xIndex]; 792 if (match1[x] == i) 793 xIndex--; 794 else 795 break; 796 } 797 798 assert (match1[x] < i); 799 assert (match2[x] == i); 800 801 match3[x] = match1[x]; 802 nbOfMatchPerY[i]--; 803 nbOfMatchPerY[match1[x]]++; 804 xIndex--; 805 e--; 806 } 807 } 808 if (debug) { 809 System.out.print("match3 : "); 810 for (int aMatch3 : match3) 811 System.out.print(aMatch3 + " "); 812 System.out.println(""); 813 } 814 } 815 816 //--------------------------------SCCs-------------------------------------// 817 818 private void SCCs() { 819 820 int sccNb, C; 821 int maxYReachedFromS, maxYReachesS; 822 int minYReachedFromS, minYReachesS; 823 int[] compReachesLeft = new int[ySize]; 824 int[] compReachesRight = new int[ySize]; 825 int[] yReachesLeft = new int[ySize]; 826 int[] yReachesRight = new int[ySize]; 827 828 for (int i = 0; i < ySize; i++) 829 compOfY[i] = i; 830 831 sccNb = SCCsWithoutS(compReachesLeft, compReachesRight, yReachesLeft, yReachesRight); 832 // now compReaches(Left, Right) and compOfY contain the left and right most y per comp and to which comp a y belong 833 if (debug) { 834 System.out.println("sccNb : " + sccNb); 835 System.out.println("compReachesLeft "); 836 for (int aCompReachesLeft : compReachesLeft) 837 System.out.print(aCompReachesLeft + " "); 838 839 System.out.println(""); 840 System.out.println("compReachesRight "); 841 for (int aCompReachesRight : compReachesRight) 842 System.out.print(aCompReachesRight + " "); 843 844 System.out.println(""); 845 System.out.println("compOfY "); 846 for (int aCompOfY : compOfY) 847 System.out.print(aCompOfY + " "); 848 849 System.out.println(""); 850 } 851 boolean[] reachedFromS = new boolean[sccNb]; 852 boolean[] reachesS = new boolean[sccNb]; 853 854 // reachedFromS and reachesS need to contain only value false. 855 // by default satisfied by Java, therefore the two lines below are not needed. 856 // Arrays.fill(reachedFromS, false); 857 // Arrays.fill(reachesS, false); 858 859 // init reachedFromS and reachesS 860 int comp; 861 for (int i = 0; i < ySize; i++) { 862 comp = compOfY[i]; 863 assert (comp >= 0); 864 assert (comp <= sccNb); 865 866 // if there are stictly more match to y than the minimum required 867 // an edge exist from s to y 868 if (yDomain[0][i] < nbOfMatchPerY[i]) 869 reachedFromS[comp] = true; 870 871 // if there are strictly less match to y than the maximum possible 872 // an edge exist from y to s 873 if (yDomain[1][i] > nbOfMatchPerY[i]) 874 reachesS[comp] = true; 875 } 876 877 maxYReachedFromS = -1; 878 maxYReachesS = -1; 879 880 for (int i = 0; i < ySize; i++) { 881 C = compOfY[i]; 882 883 assert (C >= 0); 884 assert (C <= sccNb); 885 886 // if the max y that can be reached is greater than the current y, 887 // that the comp it belongs to can be reached from s. 888 if (maxYReachedFromS >= i) 889 reachedFromS[C] = true; 890 891 // if it's reached we can extand the max y reached to the compReachesRight of the component 892 if (reachedFromS[C]) 893 maxYReachedFromS = Math.max(maxYReachedFromS, compReachesRight[C]); 894 895 // same in the other way : if the ReachesLeft of the comp is under the max y that 896 // reaches S this comp reaches S 897 if (compReachesLeft[C] <= maxYReachesS) 898 reachesS[C] = true; 899 900 // if it's reachesS we can extand the max Y that reaches it to the current y. 901 if (reachesS[C]) 902 maxYReachesS = Math.max(maxYReachesS, i); 903 904 } 905 906 // same as before but for minimum 907 908 minYReachedFromS = ySize; 909 minYReachesS = ySize; 910 911 for (int i = ySize - 1; i >= 0; i--) { 912 C = compOfY[i]; 913 assert (C >= 0); 914 assert (C <= sccNb); 915 if (minYReachedFromS <= i) 916 reachedFromS[C] = true; 917 918 if (reachedFromS[C]) 919 minYReachedFromS = Math.min(minYReachedFromS, compReachesLeft[C]); 920 921 if (compReachesRight[C] >= minYReachesS) 922 reachesS[C] = true; 923 924 if (reachesS[C]) 925 minYReachesS = Math.min(minYReachesS, i); 926 927 } 928 929 // merge all comp that are strongly connected through s and 930 // give this new comp a new number 931 932 for (int i = 0; i < ySize; i++) 933 934 if (reachesS[compOfY[i]] && reachedFromS[compOfY[i]]) 935 compOfY[i] = sccNb; 936 937 if (debug) { 938 System.out.println("compOfY after S "); 939 for (int aCompOfY : compOfY) 940 System.out.print(aCompOfY + " "); 941 942 System.out.println(""); 943 } 944 } 945 946 private int SCCsWithoutS(int[] compReachesLeft, int[] compReachesRight, int[] yReachesLeft, int[] yReachesRight) { 947 948 Component C, C1; 949 int sccNb = 0; 950 S1.clear(); 951 S2.clear(); 952 953 ReachedFromY(yReachesLeft, yReachesRight); 954 955 // init all componant as containing only one y and set these component 956 // reachesLeft and reachesRight to y reachesLeft and Right 957 for (int y = 0; y < ySize; y++) { 958 compReachesLeft[y] = yReachesLeft[y]; 959 compReachesRight[y] = yReachesRight[y]; 960 } 961 962 for (int y = 0; y < ySize; y++) { 963 // set comp to (root, rightmostY, maxX) 964 C = new Component(y, y, yReachesRight[y]); 965 966 if (S2.isEmpty()) { 967 S1.push(y); 968 S2.push(C); 969 continue; 970 } 971 972 // once S2 not empty 973 // this first part treat the case we have a new component. 974 // (c1.max < C.root) 975 976 while ((!S2.isEmpty()) && ((S2.peek().maxX < C.root))) { 977 compReachesLeft[sccNb] = ySize; 978 compReachesRight[sccNb] = -1; 979 980 assert (!S1.isEmpty()); 981 982 C1 = S2.pop(); 983 while (!S1.isEmpty() && S1.peek() >= C1.root && S1.peek() <= C1.rightmostY) { 984 int popY; 985 assert (!S1.isEmpty()); 986 popY = S1.pop(); 987 compOfY[popY] = sccNb; 988 compReachesLeft[sccNb] = Math.min(compReachesLeft[sccNb], yReachesLeft[popY]); 989 compReachesRight[sccNb] = Math.max(compReachesRight[sccNb], yReachesRight[popY]); 990 } 991 sccNb++; 992 } 993 994 assert (S2.isEmpty() || S2.peek().maxX >= C.root); 995 996 // this second part treat the case the new c1 is in fact attainable by the current component 997 998 while (!S2.isEmpty() && yReachesLeft[y] <= S2.peek().rightmostY) { 999 assert (!S2.isEmpty()); 1000 C1 = S2.pop(); 1001 C.maxX = Math.max(C.maxX, C1.maxX); 1002 C.root = C1.root; // as they are taken in order from left to right c1.root is always < C.root 1003 C.rightmostY = y; // same remark 1004 } 1005 1006 assert (S2.isEmpty() || ((yReachesLeft[y] > S2.peek().rightmostY) && (S2.peek().maxX >= C.root))); 1007 1008 S1.push(y); 1009 S2.push(C); 1010 } // end for 1011 1012 1013 // for every component still on the pile update compOfY, compReachesLeft and Right, and sccNb 1014 while (!S2.isEmpty()) { 1015 assert (!S1.isEmpty()); 1016 C = S2.pop(); 1017 compReachesLeft[sccNb] = ySize; 1018 compReachesRight[sccNb] = -1; 1019 1020 while (!S1.isEmpty() && S1.peek() >= C.root && S1.peek() <= C.rightmostY) { 1021 int y; 1022 assert (!S1.isEmpty()); 1023 y = S1.pop(); 1024 compOfY[y] = sccNb; 1025 compReachesLeft[sccNb] = Math.min(compReachesLeft[sccNb], yReachesLeft[y]); 1026 compReachesRight[sccNb] = Math.max(compReachesRight[sccNb], yReachesRight[y]); 1027 } 1028 sccNb++; 1029 } 1030 1031 assert (S1.isEmpty() && S2.isEmpty()); 1032 return sccNb; 1033 } 1034 1035 private void ReachedFromY(int[] yReachesLeft, int[] yReachesRight) { 1036 1037 1038 for (int i = 0; i < ySize; i++) { 1039 yReachesLeft[i] = i; 1040 yReachesRight[i] = i; 1041 } 1042 1043 int i; 1044 // we check what is the minimum ymin and the maximum ymax reachable by y. 1045 // For that we check every x linked to y to keep the minimal and maximal domain 1046 // bondaries of these xs. 1047 for (int j = 0; j < stampValue; j++) { 1048 i = match3[j]; 1049 assert (i >= 0); 1050 assert (i < ySize); 1051 yReachesLeft[i] = Math.min(yReachesLeft[i], xDomain[j].min()); 1052 yReachesRight[i] = Math.max(yReachesRight[i], xDomain[j].max()); 1053 } 1054 if (debug) { 1055 System.out.println("yReachesLeft "); 1056 for (i = 0; i < yReachesLeft.length; i++) 1057 System.out.print(yReachesLeft[i] + " "); 1058 1059 System.out.println(""); 1060 System.out.println("yReachesRight "); 1061 for (i = 0; i < yReachesRight.length; i++) 1062 System.out.print(yReachesRight[i] + " "); 1063 1064 System.out.println(""); 1065 } 1066 } 1067 1068 //------------------------USED_FOR_REDUCING_DOMAIN--------------------------// 1069 1070 1071 private void putToTheEnd(IntVar[] list, int element) { 1072 // I swap the element with the last one before the stamp 1073 // which have nothing to do in the no-more-seen variables 1074 IntVar v1 = list[element]; 1075 int stampValue = stamp.value(); 1076 list[element] = list[stampValue]; 1077 // update the index of the moved element which was behind the stamp value 1078 xNodesHash.put(list[stampValue], element); 1079 // and update the one put to the end 1080 xNodesHash.put(v1, stampValue); 1081 list[stampValue] = v1; 1082 } 1083 1084 //---------------------------COUNT_BOUND_CONCISTENCY-----------------------// 1085 1086 private void countBoundConsistency(Store store) { 1087 int[] max_u = new int[ySize]; 1088 Arrays.fill(max_u, ySize - 1); 1089 1090 int[] min_l = new int[ySize]; 1091 Arrays.fill(min_l, 0); 1092 1093 upperCount(max_u); 1094 lowerCount(min_l); 1095 1096 if (debug) { 1097 System.out.println("max_u "); 1098 for (int aMax_u : max_u) 1099 System.out.print(aMax_u + " "); 1100 1101 System.out.println(""); 1102 System.out.println("min_l "); 1103 for (int aMin_l : min_l) 1104 System.out.print(aMin_l + " "); 1105 1106 System.out.println(""); 1107 } 1108 // do the pruning of the domain 1109 for (int i = 0; i < ySize; i++) { 1110 if (debug) 1111 System.out 1112 .println("do pruning [" + counters[i].min() + "," + counters[i].max() + "] => [" + min_l[i] + "," + max_u[i] + "]"); 1113 1114 if (yDomain[1][i] != max_u[i] || yDomain[0][i] != min_l[i]) { 1115 yDomain[0][i] = min_l[i]; 1116 yDomain[1][i] = max_u[i]; 1117 } 1118 } 1119 // add the rest of nodes not treated in this pass that was already singleton 1120 if (debug) 1121 System.out.println("increase yDomain with xNodes singleton"); 1122 1123 for (int i = 0; i < xSize; i++) { 1124 if (x[i].singleton()) { 1125 //Change, check. 1126 int value = findPosition(x[i].value(), domainHash); 1127 yDomain[1][value]++; 1128 yDomain[0][value]++; 1129 } 1130 } 1131 1132 if (debug) 1133 System.out.println("set yNodes"); 1134 1135 for (int i = 0; i < ySize; i++) 1136 counters[i].domain.in(store.level, counters[i], yDomain[0][i], yDomain[1][i]); 1137 1138 } 1139 1140 private void upperCount(int[] max_u) { 1141 1142 int xIndex, x; 1143 pCount.clear(); 1144 xIndex = stampValue - 1; 1145 for (int i = ySize - 1; i >= 0; i--) { 1146 while (xIndex >= 0) { 1147 x = match2XOrder[xIndex]; 1148 if (match2[x] == i) { 1149 pCount.add(match1[x]); 1150 xIndex--; 1151 } else 1152 break; 1153 } 1154 max_u[i] = Math.min(yDomain[1][i], pCount.size()); 1155 for (int l = 0; l < yDomain[0][i]; l++) { 1156 assert (!pCount.isEmpty()); 1157 pCount.remove(); 1158 } 1159 1160 // well see how it works for the second part of the condition 1161 while (!pCount.isEmpty() && (pCount.peek() == i)) { 1162 pCount.remove(); 1163 } 1164 } 1165 } 1166 1167 private void lowerCount(int[] min_l) { 1168 int xIndex, count, x; 1169 pCount.clear(); 1170 xIndex = stampValue - 1; 1171 for (int i = ySize - 1; i >= 0; i--) { 1172 count = 0; 1173 while (xIndex >= 0) { 1174 x = match2XOrder[xIndex]; 1175 if (match2[x] == i) { 1176 pCount.add(match1[x]); 1177 xIndex--; 1178 } else 1179 break; 1180 1181 } 1182 1183 for (int l = 0; l < yDomain[0][i]; l++) { 1184 assert (!pCount.isEmpty()); 1185 pCount.remove(); 1186 count++; 1187 } 1188 1189 while ((!pCount.isEmpty()) && (pCount.peek() == i)) { 1190 pCount.remove(); 1191 count++; 1192 } 1193 1194 min_l[i] = count; 1195 while ((!pCount.isEmpty()) && (count < yDomain[1][i])) { 1196 pCount.remove(); 1197 count++; 1198 } 1199 } 1200 } 1201 1202 // ----------------------------SORT_AND_COMPARATOR--------------------------// 1203 1204 private void sortXByDomainMin() { 1205 // I need to sort only the part concern, otherwise old values still after 1206 // the stamp value will interfer with the sorting 1207 Arrays.sort(xDomain, 0, stampValue, compareLowerBound); 1208 1209 } 1210 1211 //-----------------------INNER CLASSES-----------------------------------// 1212 private static class Component { 1213 1214 int root; 1215 int rightmostY; 1216 int maxX; 1217 1218 public Component(int root, int rightmostY, int maxX) { 1219 this.root = root; 1220 this.rightmostY = rightmostY; 1221 this.maxX = maxX; 1222 } 1223 } 1224 1225 1226 private static class XDomain extends IntervalDomain { 1227 Var twin; 1228 int index; 1229 1230 XDomain(Var twin, int min, int max) { 1231 super(min, max); 1232 this.twin = twin; 1233 } 1234 } 1235 1236 private int findPosition(int value, int[] values) { 1237 1238 int left = 0; 1239 int right = values.length - 1; 1240 1241 int position = (left + right) >> 1; 1242 1243 if (debug) { 1244 System.out.println("Looking for " + value); 1245 for (int v : values) 1246 System.out.print("val " + v); 1247 System.out.println(""); 1248 } 1249 1250 while (!(left + 1 >= right)) { 1251 1252 if (debug) 1253 System.out.println("left " + left + " right " + right + " position " + position); 1254 1255 if (values[position] > value) { 1256 right = position; 1257 } else { 1258 left = position; 1259 } 1260 1261 position = (left + right) >> 1; 1262 1263 } 1264 1265 if (values[left] == value) 1266 return left; 1267 1268 if (values[right] == value) 1269 return right; 1270 1271 return -1; 1272 1273 } 1274 } 1275