├── CONTRIBUTING ├── LICENSE ├── README.md └── src ├── main └── java │ └── kiwi │ ├── Solver.java │ ├── constraint │ ├── AllDifferent.java │ ├── DifferentVal.java │ ├── DifferentVar.java │ ├── LowerEqualVal.java │ ├── LowerEqualVar.java │ └── Sum.java │ ├── example │ └── NQueens.java │ ├── modeling │ ├── Constraints.java │ ├── Heuristics.java │ └── Views.java │ ├── propagation │ ├── PropagationQueue.java │ └── Propagator.java │ ├── search │ ├── BinaryVarVal.java │ ├── DFSearch.java │ ├── Decision.java │ ├── Heuristic.java │ ├── Objective.java │ └── SearchStats.java │ ├── trail │ ├── Change.java │ ├── README.md │ ├── Trail.java │ ├── TrailedBoolean.java │ └── TrailedInt.java │ ├── util │ ├── Action.java │ ├── Array.java │ ├── Stack.java │ ├── StackInt.java │ └── Tuple.java │ └── variable │ ├── IntVar.java │ ├── IntVarImpl.java │ ├── IntVarOffset.java │ ├── IntVarOpposite.java │ └── IntVarSingleton.java └── test └── java └── kiwi ├── example └── NQueensTest.java └── variable ├── IntVarImplTest.java ├── IntVarOffsetTest.java ├── IntVarOppositeTest.java ├── IntVarSingletonTest.java └── IntVarTest.java /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are a 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to any Google project must be accompanied by a Contributor License 9 | Agreement. This is necessary because you own the copyright to your changes, even 10 | after your contribution becomes part of this project. So this agreement simply 11 | gives us permission to use and redistribute your contributions as part of the 12 | project. Head over to to see your current 13 | agreements on file or to sign a new one. 14 | 15 | You generally only need to submit a CLA once, so if you've already submitted one 16 | (even if it was for a different project), you probably don't need to do it 17 | again. 18 | 19 | ## Code reviews 20 | 21 | All submissions, including submissions by project members, require review. We 22 | use GitHub pull requests for this purpose. Consult [GitHub Help] for more 23 | information on using pull requests. 24 | 25 | [GitHub Help]: https://help.github.com/articles/about-pull-requests/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kiwi-Solver 2 | 3 | Kiwi is a minimalist and extendable Constraint Programming (CP) solver specifically designed for education. 4 | 5 | The particularities of Kiwi stand in its generic trailing state restoration mechanism, its propagator-based propagation algorithm, and its modulable use of variables and heuristics. 6 | 7 | By developing Kiwi, the author does not aim to create an alternative to full-featured constraint solvers but rather to provide readers with a basic architecture that will (hopefully) help them to understand the core mechanisms hidden under the hood of constraint solvers, to develop their own extended constraint solver, or to test innovative ideas. 8 | 9 | ## Disclaimer 10 | 11 | This repository contains a Java version of Kiwi — the [first version of Kiwi](https://arxiv.org/abs/1705.00047) was implemented in Scala by rhartert during his PhD. The original source code of Kiwi was the result of rethinking and simplifying the architecture of the open-source [OscaR](https://bitbucket.org/oscarlib/oscar/wiki/Home) solver to achieve what the author believes to be a good trade-off between performance, clarity, and conciseness. 12 | -------------------------------------------------------------------------------- /src/main/java/kiwi/Solver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi; 17 | 18 | import java.util.function.Predicate; 19 | 20 | import kiwi.propagation.PropagationQueue; 21 | import kiwi.propagation.Propagator; 22 | import kiwi.search.DFSearch; 23 | import kiwi.search.Heuristic; 24 | import kiwi.search.Objective; 25 | import kiwi.search.SearchStats; 26 | import kiwi.trail.Trail; 27 | import kiwi.util.Action; 28 | import kiwi.variable.IntVar; 29 | import kiwi.variable.IntVarImpl; 30 | import kiwi.variable.IntVarSingleton; 31 | 32 | public class Solver { 33 | 34 | private final Trail trail; 35 | private final PropagationQueue pQueue; 36 | private final DFSearch search; 37 | 38 | private boolean feasible = true; 39 | 40 | public Solver() { 41 | this.trail = new Trail(); 42 | this.pQueue = new PropagationQueue(); 43 | this.search = new DFSearch(pQueue, trail); 44 | this.feasible = true; 45 | } 46 | 47 | public boolean isFeasible() { 48 | return feasible; 49 | } 50 | 51 | public Trail trail() { 52 | return trail; 53 | } 54 | 55 | public void setObjective(Objective obj) { 56 | this.search.setObjective(obj); 57 | } 58 | 59 | public void onSolution(Action action) { 60 | search.addSolutionAction(action); 61 | } 62 | 63 | public SearchStats solve(Heuristic heuristic, Predicate stopCondition) { 64 | return search.search(heuristic, stopCondition); 65 | } 66 | 67 | public SearchStats solve(Heuristic heuristic) { 68 | return solve(heuristic, s -> false); 69 | } 70 | 71 | public IntVar intVar(int min, int max) { 72 | return new IntVarImpl(pQueue, trail, min, max); 73 | } 74 | 75 | public IntVar intVar(int value) { 76 | return new IntVarSingleton(pQueue, trail, value); 77 | } 78 | 79 | public IntVar intVar(int[] values) { 80 | return new IntVarImpl(pQueue, trail, values); 81 | } 82 | 83 | public boolean add(Propagator propagator) { 84 | feasible = feasible && propagator.setup() && pQueue.propagate(); 85 | return feasible; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/kiwi/constraint/AllDifferent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.constraint; 17 | 18 | import kiwi.propagation.Propagator; 19 | import kiwi.trail.TrailedInt; 20 | import kiwi.variable.IntVar; 21 | 22 | public class AllDifferent extends Propagator { 23 | 24 | private final IntVar[] unassigned; 25 | private final TrailedInt nUnassignedT; 26 | 27 | public AllDifferent(IntVar[] variables) { 28 | unassigned = variables.clone(); 29 | nUnassignedT = new TrailedInt(unassigned[0].trail(), variables.length); 30 | } 31 | 32 | public boolean setup() { 33 | for (int i = 0; i < unassigned.length; i++) { 34 | unassigned[i].watchAssign(this); 35 | } 36 | return propagate(); 37 | } 38 | 39 | public boolean propagate() { 40 | int nUnassigned = nUnassignedT.getValue(); 41 | if (nUnassigned == 1) 42 | return true; 43 | boolean reduce = true; 44 | while (reduce) { 45 | reduce = false; 46 | for (int i = nUnassigned - 1; i >= 0; i--) { 47 | IntVar variable = unassigned[i]; 48 | if (variable.isAssigned()) { 49 | // 50 | nUnassigned--; 51 | unassigned[i] = unassigned[nUnassigned]; 52 | unassigned[nUnassigned] = variable; 53 | // 54 | int value = variable.min(); 55 | for (int j = 0; j < nUnassigned; j++) { 56 | IntVar var = unassigned[j]; 57 | if (var.contains(value)) { 58 | if (!var.remove(value)) 59 | return false; 60 | reduce |= var.isAssigned(); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | nUnassignedT.setValue(nUnassigned); 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/kiwi/constraint/DifferentVal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.constraint; 17 | 18 | import kiwi.propagation.Propagator; 19 | import kiwi.variable.IntVar; 20 | 21 | public class DifferentVal extends Propagator { 22 | 23 | private final IntVar x; 24 | private final int k; 25 | 26 | public DifferentVal(IntVar x, int k) { 27 | this.x = x; 28 | this.k = k; 29 | } 30 | 31 | @Override 32 | public boolean setup() { 33 | return x.remove(k); 34 | } 35 | 36 | @Override 37 | public boolean propagate() { 38 | return true; 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/kiwi/constraint/DifferentVar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.constraint; 17 | 18 | import kiwi.propagation.Propagator; 19 | import kiwi.variable.IntVar; 20 | 21 | public class DifferentVar extends Propagator { 22 | 23 | private final IntVar x; 24 | private final IntVar y; 25 | 26 | public DifferentVar(IntVar x, IntVar y) { 27 | this.idempotent = true; 28 | this.x = x; 29 | this.y = y; 30 | } 31 | 32 | public boolean setup() { 33 | x.watchAssign(this); 34 | y.watchAssign(this); 35 | return propagate(); 36 | } 37 | 38 | public boolean propagate() { 39 | if (x.isAssigned()) { 40 | return y.remove(x.min()); 41 | } 42 | if (y.isAssigned()) { 43 | return x.remove(y.min()); 44 | } 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/kiwi/constraint/LowerEqualVal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.constraint; 17 | 18 | import kiwi.propagation.Propagator; 19 | import kiwi.variable.IntVar; 20 | 21 | public class LowerEqualVal extends Propagator { 22 | 23 | private final IntVar x; 24 | private final int k; 25 | 26 | public LowerEqualVal(IntVar x, int k, boolean strict) { 27 | this.x = x; 28 | this.k = strict ? k+1 : k; 29 | } 30 | 31 | @Override 32 | public boolean setup() { 33 | return x.updateMax(k); 34 | } 35 | 36 | @Override 37 | public boolean propagate() { 38 | return true; 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/kiwi/constraint/LowerEqualVar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.constraint; 17 | 18 | import kiwi.propagation.Propagator; 19 | import kiwi.variable.IntVar; 20 | 21 | public class LowerEqualVar extends Propagator { 22 | 23 | private final IntVar x; 24 | private final IntVar y; 25 | private final int k; 26 | 27 | public LowerEqualVar(IntVar leftVar, IntVar rightVar, boolean strict) { 28 | this.x = leftVar; 29 | this.y = rightVar; 30 | this.k = strict ? 1 : 0; 31 | } 32 | 33 | @Override 34 | public boolean setup() { 35 | x.watchBounds(this); 36 | y.watchBounds(this); 37 | return propagate(); 38 | } 39 | 40 | @Override 41 | public boolean propagate() { 42 | return x.updateMax(y.max() - k) && y.updateMin(x.min() + k); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/kiwi/constraint/Sum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.constraint; 17 | 18 | import kiwi.propagation.Propagator; 19 | import kiwi.trail.TrailedInt; 20 | import kiwi.variable.IntVar; 21 | 22 | public class Sum extends Propagator { 23 | 24 | private final IntVar sum; 25 | private final IntVar[] assigned; 26 | private final TrailedInt nAssignedT; 27 | private final TrailedInt sumAssignedT; 28 | 29 | public Sum(IntVar[] variables, IntVar sum, int offset) { 30 | this.sum = sum; 31 | this.assigned = variables.clone(); 32 | this.nAssignedT = new TrailedInt(sum.trail(), 0); 33 | this.sumAssignedT = new TrailedInt(sum.trail(), offset); 34 | } 35 | 36 | public boolean setup() { 37 | sum.watchBounds(this); 38 | for (int i = 0; i < assigned.length; i++) { 39 | assigned[i].watchBounds(this); 40 | } 41 | return propagate(); 42 | } 43 | 44 | public boolean propagate() { 45 | // Cache trailed values for fast access and update. 46 | int nAssigned = nAssignedT.getValue(); 47 | int sumAssigned = sumAssignedT.getValue(); 48 | 49 | // Repeat until the propagator reaches its fixed-point. 50 | boolean reduce = true; 51 | while (reduce) { 52 | reduce = false; 53 | 54 | int sumTermsMin = sumAssigned; 55 | int sumTermsMax = sumAssigned; 56 | int maxDiff = 0; 57 | 58 | // Update the set of assigned variables and compute the sum 59 | // of the minimum and maximum values of all the variables. 60 | for (int i = nAssigned; i < assigned.length; i++) { 61 | IntVar term = assigned[i]; 62 | int min = term.min(); 63 | int max = term.max(); 64 | sumTermsMin += min; 65 | sumTermsMax += max; 66 | int diff = max - min; 67 | if (diff == 0) { 68 | sumAssigned += min; 69 | // The term is assigned we thus include it in the 70 | // range [0, nAssigned[. 71 | assigned[i] = assigned[nAssigned]; 72 | assigned[nAssigned] = term; 73 | nAssigned++; 74 | continue; 75 | } 76 | maxDiff = Math.max(maxDiff, diff); 77 | } 78 | 79 | // We update the sum variable to be contained in the range 80 | // made by the sum of all the minimum and all the maximum. 81 | if (!sum.updateMin(sumTermsMin)) 82 | return false; 83 | if (!sum.updateMax(sumTermsMax)) 84 | return false; 85 | 86 | // Note that the domain of the sum variable can be smaller than 87 | // the range [sumTermsMin, sumTermsMax]. 88 | int sumMax = sum.max(); 89 | int sumMin = sum.min(); 90 | 91 | if (sumTermsMax - maxDiff < sumMin) { 92 | for (int i = nAssigned; i < assigned.length; i++) { 93 | IntVar term = assigned[i]; 94 | int oldMin = term.min(); 95 | int newMin = sumMin - sumTermsMax + term.max(); 96 | if (newMin > oldMin) { 97 | if (!term.updateMin(newMin)) 98 | return false; 99 | reduce |= newMin != term.min(); 100 | } 101 | } 102 | } 103 | 104 | if (sumTermsMin - maxDiff > sumMax) { 105 | for (int i = nAssigned; i < assigned.length; i++) { 106 | IntVar term = assigned[i]; 107 | int oldMax = term.max(); 108 | int newMax = sumMax - sumTermsMin + term.min(); 109 | if (newMax < oldMax) { 110 | if (!term.updateMax(newMax)) 111 | return false; 112 | reduce |= newMax != term.max(); 113 | } 114 | } 115 | } 116 | } 117 | 118 | // Save the updated trailed values. 119 | nAssignedT.setValue(nAssigned); 120 | sumAssignedT.setValue(sumAssigned); 121 | return true; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/kiwi/example/NQueens.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.example; 17 | 18 | import static kiwi.modeling.Constraints.allDifferent; 19 | import static kiwi.modeling.Heuristics.binaryFirstFail; 20 | import static kiwi.modeling.Views.offset; 21 | 22 | import java.util.Arrays; 23 | 24 | import kiwi.Solver; 25 | import kiwi.variable.IntVar; 26 | 27 | public class NQueens { 28 | 29 | public static void main(String[] args) { 30 | 31 | Solver solver = new Solver(); 32 | 33 | int n = 10; 34 | IntVar[] queens = new IntVar[n]; 35 | IntVar[] queensUp = new IntVar[n]; 36 | IntVar[] queensDown = new IntVar[n]; 37 | 38 | for (int i = 0; i < n; i++) { 39 | queens[i] = solver.intVar(0, n - 1); 40 | queensUp[i] = offset(queens[i], i); 41 | queensDown[i] = offset(queens[i], -i); 42 | } 43 | 44 | solver.add(allDifferent(queens)); 45 | solver.add(allDifferent(queensUp)); 46 | solver.add(allDifferent(queensDown)); 47 | 48 | solver.onSolution(() -> { 49 | System.out.println("Solution: " + Arrays.toString(queens)); 50 | }); 51 | 52 | solver.solve(binaryFirstFail(queens)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/kiwi/modeling/Constraints.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.modeling; 17 | 18 | import kiwi.Solver; 19 | import kiwi.constraint.AllDifferent; 20 | import kiwi.constraint.DifferentVal; 21 | import kiwi.constraint.DifferentVar; 22 | import kiwi.constraint.LowerEqualVal; 23 | import kiwi.constraint.LowerEqualVar; 24 | import kiwi.constraint.Sum; 25 | import kiwi.propagation.Propagator; 26 | import kiwi.variable.IntVar; 27 | 28 | public class Constraints { 29 | 30 | public static Propagator lowerEqual(IntVar x, IntVar y) { 31 | return new LowerEqualVar(x, y, false); 32 | } 33 | 34 | public static Propagator lowerEqual(IntVar x, int k) { 35 | return new LowerEqualVal(x, k, false); 36 | } 37 | 38 | public static Propagator lower(IntVar x, IntVar y) { 39 | return new LowerEqualVar(x, y, true); 40 | } 41 | 42 | public static Propagator lower(IntVar x, int k) { 43 | return new LowerEqualVal(x, k, true); 44 | } 45 | 46 | public static Propagator greaterEqual(IntVar x, IntVar y) { 47 | return new LowerEqualVar(x, y, false); 48 | } 49 | 50 | public static Propagator greaterEqual(IntVar x, int k) { 51 | return new LowerEqualVal(Views.opposite(x), -k, false); 52 | } 53 | 54 | public static Propagator greater(IntVar x, IntVar y) { 55 | return new LowerEqualVar(x, y, true); 56 | } 57 | 58 | public static Propagator greater(IntVar x, int k) { 59 | return new LowerEqualVal(Views.opposite(x), -k, true); 60 | } 61 | 62 | public static Propagator different(IntVar x, IntVar y) { 63 | return new DifferentVar(x, y); 64 | } 65 | 66 | public static Propagator different(IntVar x, int k) { 67 | return new DifferentVal(x, k); 68 | } 69 | 70 | public static Propagator allDifferent(IntVar[] variables) { 71 | return new AllDifferent(variables); 72 | } 73 | 74 | public IntVar sum(Solver solver, IntVar[] variables, int k) { 75 | int min = k; 76 | int max = k; 77 | for (int i = 0; i < variables.length; i++) { 78 | // TODO check overflow 79 | min += variables[i].min(); 80 | max += variables[i].max(); 81 | } 82 | final IntVar result = solver.intVar(min, max); 83 | solver.add(sum(variables, result, k)); // should not fail. 84 | return result; 85 | } 86 | 87 | public static Propagator sum(IntVar[] variables, IntVar sum, int k) { 88 | return new Sum(variables, sum, k); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/kiwi/modeling/Heuristics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.modeling; 17 | 18 | import java.util.function.IntUnaryOperator; 19 | 20 | import kiwi.search.BinaryVarVal; 21 | import kiwi.search.Heuristic; 22 | import kiwi.variable.IntVar; 23 | 24 | public class Heuristics { 25 | 26 | public static Heuristic binaryFirstFail(IntVar[] vars) { 27 | return new BinaryVarVal(vars, i -> vars[i].size(), i -> vars[i].min()); 28 | } 29 | 30 | public static Heuristic binary(IntVar[] vars, IntUnaryOperator varCost, 31 | IntUnaryOperator valSelector) { 32 | return new BinaryVarVal(vars, varCost, valSelector); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/kiwi/modeling/Views.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.modeling; 17 | 18 | import java.util.HashMap; 19 | 20 | import kiwi.util.Tuple; 21 | import kiwi.variable.IntVar; 22 | import kiwi.variable.IntVarOffset; 23 | import kiwi.variable.IntVarOpposite; 24 | 25 | public class Views { 26 | 27 | // Contain all the opposite views. 28 | private static HashMap oppositeViews = new HashMap<>(); 29 | 30 | // Contain all the offset views. 31 | private static HashMap, IntVar> offsetViews = new HashMap<>(); 32 | 33 | public static IntVar opposite(IntVar x) { 34 | IntVar view = oppositeViews.get(x); 35 | if (view == null) { 36 | view = new IntVarOpposite(x); 37 | oppositeViews.put(x, view); 38 | oppositeViews.put(view, x); 39 | } 40 | return view; 41 | } 42 | 43 | public static IntVar offset(IntVar x, int k) { 44 | Tuple t = new Tuple<>(x, k); 45 | IntVar view = offsetViews.get(x); 46 | if (view == null) { 47 | view = new IntVarOffset(x, k); 48 | offsetViews.put(t, view); 49 | t = new Tuple<>(view, -k); 50 | offsetViews.put(t, x); 51 | } 52 | return view; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/kiwi/propagation/PropagationQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.propagation; 17 | 18 | import java.util.ArrayDeque; 19 | 20 | /** PropagQueue */ 21 | public class PropagationQueue { 22 | 23 | // The propagation queue that contains all the propagators waiting for 24 | // propagation. A propagator is considered to be enqueued only if its 25 | // enqueued boolean is set to true. 26 | private final ArrayDeque queue = new ArrayDeque(); 27 | 28 | /** 29 | * Enqueues the propagator for propagation. 30 | * 31 | *

32 | * This method does nothing if the propagator is already enqueued. 33 | *

34 | * 35 | * @param propagator the propagator to be scheduled for propagation. 36 | */ 37 | public void enqueue(Propagator propagator) { 38 | if (!propagator.enqueued) { 39 | queue.addLast(propagator); 40 | propagator.enqueued = true; 41 | } 42 | } 43 | 44 | /** 45 | * Propagates the pending propagators. 46 | * 47 | *

48 | * Propagate all the propagators contained in the propagation queue. 49 | * Propagation is likely to enqueue additional propagators while it is 50 | * running. The propagation stops either when the queue is empty, or if a 51 | * propagator failed (meaning that the problem is infeasible). 52 | *

53 | * 54 | *

55 | * The method returns true if the propagation succeeded or false if a 56 | * propagator failed. The queue is empty at the end of the propagation and all 57 | * propagators contained in the queue have their enqueued boolean set to 58 | * false. 59 | *

60 | * 61 | * @return true if propagation succeeded, false otherwise. 62 | */ 63 | public boolean propagate() { 64 | boolean feasible = true; 65 | while (!queue.isEmpty()) { 66 | Propagator propagator = queue.removeFirst(); 67 | // Dequeue the propagator only if it is not idempotent. This allows the 68 | // propagator to enqueue itself back in the propagation queue if it 69 | // changed the domain of at least one of its variable. 70 | propagator.enqueued = propagator.idempotent; 71 | // Propagate only if the problem is still feasible. 72 | feasible = feasible && propagator.propagate(); 73 | // Dequeue the propagator no matter what. 74 | propagator.enqueued = false; 75 | } 76 | return feasible; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/kiwi/propagation/Propagator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.propagation; 17 | 18 | /** 19 | * Superclass to be instantiated by any propagator. 20 | */ 21 | public abstract class Propagator { 22 | 23 | /** 24 | * Indicates if the propagator is contained in the propagation queue. 25 | */ 26 | protected boolean enqueued; 27 | 28 | /** 29 | * Indicates if the propagator might need to be called again just after 30 | * propagation. 31 | */ 32 | protected boolean idempotent; 33 | 34 | /** 35 | * Initializes the propagator and performs its initial propagation 36 | * 37 | * @return false if the propagation failed. 38 | */ 39 | public abstract boolean setup(); 40 | 41 | /** 42 | * Propagates the last domain changes 43 | * 44 | * @return false if the propagation failed. 45 | */ 46 | public abstract boolean propagate(); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/kiwi/search/BinaryVarVal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.search; 17 | 18 | import java.util.function.IntUnaryOperator; 19 | 20 | import kiwi.trail.TrailedInt; 21 | import kiwi.util.Array; 22 | import kiwi.util.Stack; 23 | import kiwi.variable.IntVar; 24 | 25 | public class BinaryVarVal implements Heuristic { 26 | 27 | private final IntVar[] variables; 28 | 29 | private final int[] unassigned; 30 | private final TrailedInt nUnassignedT; 31 | private final IntUnaryOperator varCost; 32 | private final IntUnaryOperator valSelector; 33 | 34 | public BinaryVarVal(IntVar[] variables, IntUnaryOperator varCost, IntUnaryOperator valSelector) { 35 | this.variables = variables; 36 | this.unassigned = Array.makeInt(variables.length, i -> i); 37 | this.nUnassignedT = new TrailedInt(variables[0].trail(), variables.length); 38 | this.varCost = varCost; 39 | this.valSelector = valSelector; 40 | } 41 | 42 | public boolean pushNextDecisions(Stack decisions) { 43 | int varId = selectVar(); 44 | if (varId == -1) { 45 | return true; 46 | } 47 | IntVar variable = variables[varId]; 48 | int value = valSelector.applyAsInt(varId); 49 | decisions.push(() -> variable.remove(value)); 50 | decisions.push(() -> variable.assign(value)); 51 | return false; 52 | } 53 | 54 | private int selectVar() { 55 | int minId = -1; 56 | int minCost = Integer.MAX_VALUE; 57 | int nUnassigned = nUnassignedT.getValue(); 58 | if (nUnassigned == 1) 59 | return unassigned[0]; 60 | for (int i = nUnassigned - 1; i >= 0; i--) { 61 | int varId = unassigned[i]; 62 | if (variables[varId].isAssigned()) { 63 | nUnassigned--; 64 | unassigned[i] = unassigned[nUnassigned]; 65 | unassigned[nUnassigned] = varId; 66 | } else { 67 | int cost = varCost.applyAsInt(varId); 68 | if (cost < minCost) { 69 | minId = varId; 70 | minCost = cost; 71 | } else if (cost == minCost && varId < minId) { 72 | minId = varId; 73 | } 74 | } 75 | } 76 | nUnassignedT.setValue(nUnassigned); 77 | return minId; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/kiwi/search/DFSearch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.search; 17 | 18 | import java.util.function.Predicate; 19 | 20 | import kiwi.propagation.PropagationQueue; 21 | import kiwi.trail.Trail; 22 | import kiwi.util.Action; 23 | import kiwi.util.Stack; 24 | 25 | public class DFSearch { 26 | 27 | private final PropagationQueue pQueue; 28 | private final Trail trail; 29 | 30 | private final Stack decisions = new Stack<>(); 31 | private final Stack solutionActions = new Stack<>(); 32 | 33 | private Objective objective = null; 34 | 35 | public DFSearch(PropagationQueue pQueue, Trail trail) { 36 | this.pQueue = pQueue; 37 | this.trail = trail; 38 | } 39 | 40 | public void addSolutionAction(Action action) { 41 | solutionActions.push(action); 42 | } 43 | 44 | public void foundSolution(SearchStats stats) { 45 | stats.nSolutions++; 46 | solutionActions.forEach(action -> action.execute()); 47 | if (objective != null) { 48 | objective.tighten(); 49 | } 50 | } 51 | 52 | public void setObjective(Objective obj) { 53 | this.objective = obj; 54 | } 55 | 56 | private boolean propagate() { 57 | // Propagate the objective only if it is not null. 58 | boolean feasible = objective == null || objective.propagate(); 59 | // Propagate the propagators only if the problem is still feasible. 60 | return feasible && pQueue.propagate(); 61 | } 62 | 63 | /** 64 | * Starts the search 65 | * 66 | * @param heuristic the search heursitic used to build the search tree. 67 | * @param stopCondition a predicate to stop the search. 68 | * @return A {@code SearchStats} object that contains some metrics related 69 | * to this tree search. 70 | */ 71 | public SearchStats search(Heuristic heuristic, Predicate stopCondition) { 72 | SearchStats stats = new SearchStats(); 73 | 74 | stats.startTime = System.currentTimeMillis(); 75 | 76 | // Return if the root node is unfeasible. 77 | if (!propagate()) { 78 | stats.completed = true; 79 | return stats; 80 | } 81 | 82 | // Return if the root node is already a solution. 83 | if (heuristic.pushNextDecisions(decisions)) { 84 | foundSolution(stats); 85 | stats.completed = true; 86 | return stats; 87 | } 88 | 89 | // Save the root state. 90 | trail.newLevel(); 91 | 92 | // Start the search. The search terminates if the stack of decisions 93 | // is empty (meaning that the search tree has been entirely explored) or 94 | // if the stop condition is met. 95 | while (!decisions.isEmpty() && !stopCondition.test(stats)) { 96 | stats.nNodes++; 97 | 98 | // Apply the next decision and propagate. This can result in a failed 99 | // node in which case we restore the previous state. 100 | if (!decisions.pop().apply() || !propagate()) { 101 | stats.nFails++; 102 | trail.undoLevel(); 103 | continue; 104 | } 105 | 106 | // At this point we know that the new node is not failed and we check 107 | // that it is a solution or not. 108 | if (heuristic.pushNextDecisions(decisions)) { 109 | foundSolution(stats); 110 | trail.undoLevel(); 111 | continue; 112 | } 113 | 114 | // The node is neither a failed node or a solution so we continue to 115 | // explore the branch. 116 | trail.newLevel(); 117 | } 118 | 119 | // The search is complete if there's no remaining decisions to be applied. 120 | stats.completed = decisions.isEmpty(); 121 | 122 | // Clear the remaining decisions (if the search is incomplete) and restore 123 | // the state of the root node. 124 | trail.undoAll(); 125 | decisions.clear(); 126 | 127 | return stats; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/kiwi/search/Decision.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.search; 17 | 18 | /** 19 | * Superclass to be instantiated by any search decision. 20 | * 21 | *

22 | * A {@code Decision} is taken by the search heuristic to drive the tree search 23 | * on specific directions. A {@code Decision} typically impacts directly the 24 | * domain of an {@code IntVar} by removing values from its domain. 25 | *

26 | */ 27 | public interface Decision { 28 | /** 29 | * Applies the decision. 30 | * 31 | * @return {@code true} if and only if the decision did not failed directly. 32 | */ 33 | public boolean apply(); 34 | } -------------------------------------------------------------------------------- /src/main/java/kiwi/search/Heuristic.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.search; 17 | 18 | import kiwi.util.Stack; 19 | 20 | /** 21 | * Superclass to be instantiated by any search heuristic. 22 | */ 23 | public interface Heuristic { 24 | 25 | /** 26 | * Pushes the next decisions to be taken on top of the decisions stack. 27 | * First check that the current state of the problem is a leaf. If not, it 28 | * adds the next decisions to applyon top of decisions stack. The last pushed 29 | * decision will be the first to be applied. 30 | * 31 | * Note that the behavior of the solver is not determined if the content of 32 | * the decisions stack is changed. 33 | * 34 | * @param decisions 35 | * The stack of decisions to be taken. The new decisions must be 36 | * pushed on top of the stack. The method should not remove any 37 | * decision contained in the stack. 38 | * 39 | * @return true if the decision stack is unchanged; false otherwise. 40 | */ 41 | public boolean pushNextDecisions(Stack decisions); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/kiwi/search/Objective.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.search; 17 | 18 | import kiwi.variable.IntVar; 19 | 20 | public class Objective { 21 | 22 | private final IntVar objVar; 23 | private final boolean minimize; 24 | private int bestValue; 25 | 26 | public Objective(IntVar objVar, boolean minimize) { 27 | this.objVar = objVar; 28 | this.minimize = minimize; 29 | this.bestValue = minimize ? IntVar.MAX_VALUE : IntVar.MIN_VALUE; 30 | } 31 | 32 | public void tighten() { 33 | bestValue = minimize ? objVar.max() - 1 : objVar.min() + 1; 34 | } 35 | 36 | public boolean propagate() { 37 | return minimize ? objVar.updateMax(bestValue) : objVar.updateMin(bestValue); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/kiwi/search/SearchStats.java: -------------------------------------------------------------------------------- 1 | package kiwi.search; 2 | 3 | public class SearchStats { 4 | public long startTime; 5 | public boolean completed; 6 | public int nNodes; 7 | public int nFails; 8 | public int nSolutions; 9 | 10 | @Override 11 | public String toString() { 12 | StringBuffer bf = new StringBuffer(); 13 | bf.append(completed ? "Complete search\n" : "Incomplete search\n"); 14 | bf.append("#solutions : " + nSolutions + "\n"); 15 | bf.append("#nodes : " + nNodes + "\n"); 16 | bf.append("#fails : " + nFails + "\n"); 17 | return bf.toString(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/kiwi/trail/Change.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.trail; 17 | 18 | /** 19 | * Superclass to be instantiated by any trailed change. 20 | * 21 | *

22 | * A {@code Change} represents any kind of undoable operation that affects the 23 | * state of the solver, its variables, or its propagators. A {@code Change} is 24 | * typically trailed and undone when a backtrack occurs. 25 | *

26 | */ 27 | public interface Change { 28 | /** Undoes the change */ 29 | public void undo(); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/kiwi/trail/README.md: -------------------------------------------------------------------------------- 1 | # Trail and state restoration 2 | 3 | This package contains the system responsible for storing and restoring the state of the solver. 4 | 5 | Trailing is the prominent approach used to implement such restoration mechanism. It basically consists to maintain the sequence of changes -- the trail -- that occurred on each object. The previous state of an object can thus be restored by undoing the changes in anti-chronological order. 6 | 7 | ## Changes 8 | 9 | Each time a state changing operation is performed, the necessary information to undo this operation is stored on the trail. 10 | In Kiwi, we chose to directly store the functions responsible of undoing state changes as first class objects, i.e., as closures. 11 | Each stateful object thus relies on its own restoration mechanism which is handled by the closure itself. 12 | 13 | ## The Trail 14 | 15 | Our trailing system is implemented with two stacks: 16 | 17 | - The first is a stack of Change objects that represents the trail. It is sorted chronologically such that the most recent change is on top of the stack (the root node being the empty stack). 18 | - The second stack maps each node of the current branch to its corresponding prefix in the trail. Thanks to the incrementality of the trail, only the position of the last change that lead to a node needs to be stored to characterize the whole sequence of changes that lead to this node. 19 | -------------------------------------------------------------------------------- /src/main/java/kiwi/trail/Trail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.trail; 17 | 18 | import kiwi.util.Stack; 19 | import kiwi.util.StackInt; 20 | 21 | /** 22 | * {@code Trail} contains the chronological sequences of changes to undo. 23 | */ 24 | public class Trail { 25 | 26 | private long timestamp = 0L; 27 | 28 | private final Stack changes = new Stack(); 29 | private final StackInt levels = new StackInt(); 30 | 31 | public long getTimestamp() { 32 | return timestamp; 33 | } 34 | 35 | public void store(Change change) { 36 | changes.push(change); 37 | } 38 | 39 | public void newLevel() { 40 | levels.push(changes.getSize()); 41 | timestamp++; 42 | } 43 | 44 | public void undoLevel() { 45 | if (levels.getSize() > 0) 46 | undoUntil(levels.pop()); 47 | timestamp++; 48 | } 49 | 50 | public void undoAll() { 51 | while (levels.getSize() > 0) { 52 | undoUntil(levels.pop()); 53 | } 54 | timestamp++; 55 | } 56 | 57 | private void undoUntil(int size) { 58 | while (changes.getSize() > size) { 59 | changes.pop().undo(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/kiwi/trail/TrailedBoolean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.trail; 17 | 18 | 19 | public class TrailedBoolean implements Change { 20 | 21 | private final Trail trail; 22 | 23 | private boolean currentValue; 24 | 25 | public TrailedBoolean(Trail trail, boolean initValue) { 26 | this.trail = trail; 27 | currentValue = initValue; 28 | } 29 | 30 | public void undo() { 31 | currentValue = !currentValue; 32 | } 33 | 34 | public boolean getValue() { 35 | return currentValue; 36 | } 37 | 38 | public void setValue(boolean value) { 39 | if (currentValue != value) { 40 | currentValue = value; 41 | trail.store(this); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/kiwi/trail/TrailedInt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.trail; 17 | 18 | import kiwi.util.StackInt; 19 | 20 | public class TrailedInt implements Change { 21 | 22 | private final Trail trail; 23 | 24 | private final StackInt oldValues = new StackInt(); 25 | 26 | private int currentValue; 27 | 28 | private long timestamp = -1L; 29 | 30 | public TrailedInt(Trail trail, int initValue) { 31 | this.trail = trail; 32 | currentValue = initValue; 33 | } 34 | 35 | public void undo() { 36 | currentValue = oldValues.pop(); 37 | } 38 | 39 | public int getValue() { 40 | return currentValue; 41 | } 42 | 43 | public void setValue(int value) { 44 | if (timestamp != trail.getTimestamp()) { 45 | timestamp = trail.getTimestamp(); 46 | oldValues.push(currentValue); 47 | trail.store(this); 48 | } 49 | currentValue = value; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/kiwi/util/Action.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.util; 17 | 18 | /** 19 | * An interface for lamda functions that require no argument and produce no 20 | * result. 21 | */ 22 | public interface Action { 23 | public void execute(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/kiwi/util/Array.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.util; 17 | 18 | import java.util.function.IntUnaryOperator; 19 | 20 | public class Array { 21 | public static int[] makeInt(int n, IntUnaryOperator f) { 22 | int[] array = new int[n]; 23 | for (int i = 0; i < n; i++) { 24 | array[i] = f.applyAsInt(i); 25 | } 26 | return array; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/kiwi/util/Stack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.util; 17 | 18 | import java.util.function.Consumer; 19 | 20 | public class Stack { 21 | 22 | private Object[] array = new Object[16]; 23 | 24 | private int index = 0; 25 | 26 | public int getSize() { 27 | return index; 28 | } 29 | 30 | public boolean isEmpty() { 31 | return index == 0; 32 | } 33 | 34 | @SuppressWarnings("unchecked") 35 | public T top() { 36 | return (T) array[index - 1]; 37 | } 38 | 39 | public void push(T elem) { 40 | if (index == array.length) { 41 | growStack(); 42 | } 43 | array[index] = (Object) elem; 44 | index++; 45 | } 46 | 47 | public void clear() { 48 | while (index > 0) { 49 | index--; 50 | array[index] = null; 51 | } 52 | } 53 | 54 | @SuppressWarnings("unchecked") 55 | public T pop() { 56 | return (T) array[--index]; 57 | } 58 | 59 | @SuppressWarnings("unchecked") 60 | public void forEach(Consumer c) { 61 | for (int i = 0; i < index; i++) { 62 | c.accept((T) array[i]); 63 | } 64 | } 65 | 66 | private void growStack() { 67 | Object[] newArray = new Object[index * 2]; 68 | System.arraycopy(array, 0, newArray, 0, index); 69 | array = newArray; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/kiwi/util/StackInt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.util; 17 | 18 | public class StackInt { 19 | 20 | private int[] array = new int[16]; 21 | 22 | private int index = 0; 23 | 24 | public int getSize() { 25 | return index; 26 | } 27 | 28 | public boolean isEmpty() { 29 | return index == 0; 30 | } 31 | 32 | public int top() { 33 | return array[index - 1]; 34 | } 35 | 36 | public void push(int elem) { 37 | if (index == array.length) 38 | growStack(); 39 | array[index] = elem; 40 | index++; 41 | } 42 | 43 | public int pop() { 44 | return array[--index]; 45 | } 46 | 47 | private void growStack() { 48 | int[] newArray = new int[index * 2]; 49 | System.arraycopy(array, 0, newArray, 0, index); 50 | array = newArray; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/kiwi/util/Tuple.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.util; 17 | 18 | public class Tuple { 19 | public final X x; 20 | public final Y y; 21 | public Tuple(X x, Y y) { 22 | this.x = x; 23 | this.y = y; 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/kiwi/variable/IntVar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import java.util.Arrays; 19 | 20 | import kiwi.propagation.PropagationQueue; 21 | import kiwi.propagation.Propagator; 22 | import kiwi.trail.Trail; 23 | 24 | /** 25 | * Superclass to be instantiated by any integer variable. 26 | * 27 | *

28 | * An {@code IntVar} basically represents the set of integer values that can be 29 | * assigned to the variable. A variable is assigned when its domain becomes a 30 | * singleton, i.e, it contains a single value. The variable cannot be empty. 31 | * Operation that directly impact the domain of the variable must return false 32 | * if the operation would have lead to an empty domain. 33 | *

34 | * 35 | *

36 | * An {@code IntVar} must be able to restore its state when a backtrack occurs. 37 | *

38 | */ 39 | public abstract class IntVar { 40 | 41 | /** 42 | * A constant holding the minimum value an {@code IntVar} can be assigned to. 43 | */ 44 | public static final int MIN_VALUE = -1000000000; 45 | 46 | /** 47 | * A constant holding the maximum value an {@code IntVar} can be assigned to. 48 | */ 49 | public static final int MAX_VALUE = 1000000000; 50 | 51 | /** 52 | * Returns the {@code PropagationQueue} associated to this variable. 53 | * 54 | * @return the {@code PropagationQueue} of this {@code IntVar}. 55 | */ 56 | public abstract PropagationQueue propagQueue(); 57 | 58 | /** 59 | * Returns the {@code Trail} associated to this {@code IntVar}. 60 | * 61 | * @return the {@code Trail} of this {@code IntVar}. 62 | */ 63 | public abstract Trail trail(); 64 | 65 | /** 66 | * Returns the minimum value contained in the domain of this variable. 67 | * 68 | * @return the minimum {@code int} contained in this {@code IntVar}. 69 | */ 70 | public abstract int min(); 71 | 72 | /** 73 | * Returns the maximum value contained in the domain of this variable. 74 | * 75 | * @return the maximum {@code int} contained in this {@code IntVar}. 76 | */ 77 | public abstract int max(); 78 | 79 | /** 80 | * Returns the number of values contained in the domain of this variable. 81 | * 82 | * @return the number of values contained in this {@code IntVar}. 83 | */ 84 | public abstract int size(); 85 | 86 | /** 87 | * Returns {@code true} if this {@code IntVar} is assigned. An {@code IntVar} 88 | * is assigned if its domain contains a single value. 89 | * 90 | * @return {@code true} if the variable is assigned; {@code false} otherwise. 91 | */ 92 | public abstract boolean isAssigned(); 93 | 94 | /** 95 | * Returns {@code true} if and only if this {@code IntVar} contains the 96 | * specified value. 97 | * 98 | * @param value the value to be check for containment in this 99 | * {@code IntVar}. 100 | * @return {@code true} if the variable contains the value; {@code false} 101 | * otherwise. 102 | */ 103 | public abstract boolean contains(int value); 104 | 105 | /** 106 | * Tries to assign this variable to the specified value. 107 | * 108 | *

109 | * If this {@code IntVar} is not assigned and contains {@code value} then all 110 | * the propagators registered on this {@code IntVar} are scheduled for 111 | * propagation as specified in {@link #watchAssign watchAssign}, 112 | * {@link #watchBounds watchBounds}, and {@link #watchChange watchChange}. 113 | * This method does nothing if this {@code IntVar} is already assigned to 114 | * {@code value}. 115 | *

116 | * 117 | * @param value the value to which this {@code IntVar} must be assigned 118 | * @return {@code true} if and only if the {@code value} is contained 119 | * in the domain of this {@code IntVar} 120 | * @see PropagationQueue#enqueue enqueue 121 | * @see #watchAssign watchAssign 122 | * @see #watchBounds watchBounds 123 | * @see #watchChange watchChange 124 | */ 125 | public abstract boolean assign(int value); 126 | 127 | /** 128 | * Tries to remove the specified value from the domain of this variable. 129 | * 130 | *

131 | * If this {@code IntVar} is not assigned and contains {@code value} then all 132 | * the propagators registered on this {@code IntVar} are scheduled for 133 | * propagation as specified in {@link #watchAssign watchAssign}, 134 | * {@link #watchBounds watchBounds}, and {@link #watchChange watchChange}. 135 | * This method does nothing if {@code value} is not contained in the domain 136 | * of this {@code IntVar}. 137 | *

138 | * 139 | * @param value the value to remove from the domain of this {@code IntVar} 140 | * @return {@code true} if and only if this {@code IntVar} is not 141 | * assigned to {@code value} 142 | * @see PropagationQueue#enqueue enqueue 143 | * @see #watchAssign watchAssign 144 | * @see #watchBounds watchBounds 145 | * @see #watchChange watchChange 146 | */ 147 | public abstract boolean remove(int value); 148 | 149 | /** 150 | * Tries to remove all the values that are smaller than the specified value. 151 | * 152 | *

153 | * If {@code value} is greater than {@link #min} and smaller or equal to 154 | * {@link #max} then all the propagators registered on this {@code IntVar} 155 | * are scheduled for propagation as specified in 156 | * {@link #watchAssign watchAssign}, {@link #watchBounds watchBounds}, and 157 | * {@link #watchChange watchChange}. This method does nothing if 158 | * {@code value} is smaller or equal to {@link #min}. 159 | *

160 | * 161 | * @param value the value such that all the smaller values contained in the 162 | * domain of this {@code IntVar} must be removed 163 | * @return {@code true} if and only if {@code value} is lower or equal 164 | * to {@link #max} 165 | * @see PropagationQueue#enqueue enqueue 166 | * @see #watchAssign watchAssign 167 | * @see #watchBounds watchBounds 168 | * @see #watchChange watchChange 169 | */ 170 | public abstract boolean updateMin(int value); 171 | 172 | /** 173 | * Tries to remove all the values that are greater than the specified value. 174 | * 175 | *

176 | * If {@code value} is lower than {@link #max} and greater or equal to 177 | * {@link #min} then all the propagators registered on this {@code IntVar} 178 | * are scheduled for propagation as specified in 179 | * {@link #watchAssign watchAssign}, {@link #watchBounds watchBounds}, and 180 | * {@link #watchChange watchChange}. This method does nothing if 181 | * {@code value} is greater or equal to {@link #max}. 182 | *

183 | * 184 | * @param value the value such that all the greater values contained in the 185 | * domain of this {@code IntVar} must be removed 186 | * @return {@code true} if and only if {@code value} is greater or 187 | * equal to {@link #min} 188 | * @see PropagationQueue#enqueue enqueue 189 | * @see #watchAssign watchAssign 190 | * @see #watchBounds watchBounds 191 | * @see #watchChange watchChange 192 | */ 193 | public abstract boolean updateMax(int value); 194 | 195 | /** 196 | * Registers the propagator on domain assignations. 197 | * 198 | *

199 | * The propagator will be scheduled for propagation using the 200 | * {@link PropagationQueue#enqueue enqueue} method each time this 201 | * {@code IntVar} is assigned to a value. 202 | *

203 | * 204 | * @param propagator propagator to be registered on this {@code IntVar} 205 | * @see #assign assign 206 | * @see #remove remove 207 | * @see #updateMin updateMin 208 | * @see #updateMax updateMax 209 | */ 210 | public abstract void watchAssign(Propagator propagator); 211 | 212 | /** 213 | * Registers the propagator on bound changes. 214 | * 215 | *

216 | * The propagator will be scheduled for propagation using the 217 | * {@link PropagationQueue#enqueue enqueue} method each time the value of 218 | * {@link #min} or {@link #max} is removed from the domain of this 219 | * {@code IntVar}. 220 | *

221 | * 222 | * @param propagator propagator to be registered on this {@code IntVar} 223 | * @see #assign assign 224 | * @see #remove remove 225 | * @see #updateMin updateMin 226 | * @see #updateMax updateMax 227 | */ 228 | public abstract void watchBounds(Propagator propagator); 229 | 230 | /** 231 | * Registers the propagator on the domain changes. 232 | * 233 | *

234 | * The propagator will be scheduled for propagation using the 235 | * {@link PropagationQueue#enqueue enqueue} method each time a value is 236 | * removed from the domain of this {@code IntVar}. 237 | *

238 | * 239 | * @param propagator propagator to be registered on this {@code IntVar} 240 | * @see #assign assign 241 | * @see #remove remove 242 | * @see #updateMin updateMin 243 | * @see #updateMax updateMax 244 | */ 245 | public abstract void watchChange(Propagator propagator); 246 | 247 | 248 | /** 249 | * Copies the values contained in the domain of this {@code IntVar} in the 250 | * specified array. 251 | * 252 | *

253 | * This function gives no guarantee on the order of the copied values. 254 | *

255 | * 256 | * @return the size of the domain 257 | */ 258 | public abstract int copyDomain(int[] array); 259 | 260 | @Override 261 | public String toString() { 262 | int[] domain = new int[size()]; 263 | copyDomain(domain); 264 | Arrays.sort(domain); 265 | StringBuffer bf = new StringBuffer("{"); 266 | for (int i = 0; i < domain.length - 1; i++) { 267 | bf.append(domain[i] + ", "); 268 | } 269 | bf.append(domain[domain.length - 1] + "}"); 270 | return bf.toString(); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/main/java/kiwi/variable/IntVarImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import kiwi.propagation.PropagationQueue; 19 | import kiwi.propagation.Propagator; 20 | import kiwi.trail.Trail; 21 | import kiwi.trail.TrailedInt; 22 | import kiwi.util.Array; 23 | import kiwi.util.Stack; 24 | 25 | /** 26 | * A sparse set based implementation of IntVar 27 | * 28 | *

29 | * This class implements {@code IntVar} with a sparse set representation. 30 | * 31 | * Reference: 32 | * - 33 | * - 34 | *

35 | * 36 | * 37 | * 38 | */ 39 | public class IntVarImpl extends IntVar { 40 | 41 | private final PropagationQueue pQueue; 42 | private final Trail trail; 43 | 44 | private final int initMin; 45 | private final int initMax; 46 | 47 | private final TrailedInt minT; 48 | private final TrailedInt maxT; 49 | private final TrailedInt sizeT; 50 | 51 | private final int[] values; 52 | private final int[] positions; 53 | 54 | private final Stack changeWatchers = new Stack(); 55 | private final Stack assignWatchers = new Stack(); 56 | private final Stack boundsWatchers = new Stack(); 57 | 58 | public IntVarImpl(PropagationQueue pQueue, Trail trail, int initMin, int initMax) { 59 | this.pQueue = pQueue; 60 | this.trail = trail; 61 | this.initMin = initMin; 62 | this.initMax = initMax; 63 | this.minT = new TrailedInt(trail, initMin); 64 | this.maxT = new TrailedInt(trail, initMax); 65 | final int size = initMax - initMin + 1; 66 | this.sizeT = new TrailedInt(trail, size); 67 | this.values = Array.makeInt(size, i -> i + initMin); 68 | this.positions = Array.makeInt(size, i -> i); 69 | } 70 | 71 | public IntVarImpl(PropagationQueue pQueue, Trail trail, int[] values) { 72 | this.pQueue = pQueue; 73 | this.trail = trail; 74 | this.values = values.clone(); 75 | this.sizeT = new TrailedInt(trail, values.length); 76 | 77 | // Compute the minimum and maximum values in the domain. 78 | int min = IntVar.MAX_VALUE; 79 | int max = IntVar.MIN_VALUE; 80 | for (int i = 0; i < values.length; i++) { 81 | min = Math.min(min, values[i]); 82 | max = Math.max(max, values[i]); 83 | } 84 | this.initMin = min; 85 | this.initMax = max; 86 | this.minT = new TrailedInt(trail, initMin); 87 | this.maxT = new TrailedInt(trail, initMax); 88 | 89 | // Build the domain representation. 90 | final int range = max - min + 1; 91 | this.positions = Array.makeInt(range, i -> range); 92 | for (int i = 0; i < values.length; i++) { 93 | this.positions[values[i] - initMin] = i; 94 | } 95 | } 96 | 97 | @Override 98 | public PropagationQueue propagQueue() { 99 | return pQueue; 100 | }; 101 | 102 | @Override 103 | public Trail trail() { 104 | return trail; 105 | }; 106 | 107 | @Override 108 | public int min() { 109 | return minT.getValue(); 110 | } 111 | 112 | @Override 113 | public int max() { 114 | return maxT.getValue(); 115 | } 116 | 117 | @Override 118 | public int size() { 119 | return sizeT.getValue(); 120 | } 121 | 122 | @Override 123 | public boolean isAssigned() { 124 | return sizeT.getValue() == 1; 125 | } 126 | 127 | @Override 128 | public boolean contains(int value) { 129 | if (value < initMin || value > initMax) { 130 | return false; 131 | } 132 | return positions[value - initMin] < sizeT.getValue(); 133 | } 134 | 135 | private void swap(int pos1, int pos2) { 136 | int v1 = values[pos1]; 137 | int v2 = values[pos2]; 138 | int id1 = v1 - initMin; 139 | int id2 = v2 - initMin; 140 | values[pos1] = v2; 141 | values[pos2] = v1; 142 | positions[id1] = pos2; 143 | positions[id2] = pos1; 144 | } 145 | 146 | @Override 147 | public boolean assign(int value) { 148 | if (value < minT.getValue() || value > maxT.getValue()) { 149 | return false; 150 | } 151 | int size = sizeT.getValue(); 152 | 153 | // We know that the variable is already assigned to value. 154 | if (size == 1) { 155 | return true; 156 | } 157 | 158 | // The value is not in the domain. 159 | int position = positions[value - initMin]; 160 | if (position >= size) { 161 | return false; 162 | } 163 | 164 | // Remove the value and update the domain. 165 | swap(position, 0); 166 | minT.setValue(value); 167 | maxT.setValue(value); 168 | sizeT.setValue(1); 169 | awakeAssign(); 170 | awakeBounds(); 171 | awakeChange(); 172 | return true; 173 | } 174 | 175 | @Override 176 | public boolean remove(int value) { 177 | // The value is already removed if it is not contained 178 | // in the range [min, max]. 179 | int min = minT.getValue(); 180 | int max = maxT.getValue(); 181 | if (value < min || value > max) { 182 | return true; 183 | } 184 | 185 | // We cannot remove the value if the variable is 186 | // already assigned to it. 187 | int size = sizeT.getValue(); 188 | if (size == 1) { 189 | return false; 190 | } 191 | 192 | // Check that the value is not already removed. 193 | int position = positions[value - initMin]; 194 | if (position >= size) { 195 | return true; 196 | } 197 | 198 | // At this point, we know that the value is in the domain and that 199 | // the domain is not assigned. We thus remove the value. 200 | size--; 201 | swap(position, size); 202 | sizeT.setValue(size); 203 | 204 | // We now have to notify the propagators and to update one of the 205 | // bound if necessary. 206 | if (size == 1) { 207 | // Removing the value assigned the variable. 208 | if (value == min) { 209 | minT.setValue(max); 210 | } else { 211 | maxT.setValue(min); 212 | } 213 | awakeAssign(); 214 | awakeBounds(); 215 | } else if (min == value) { 216 | // We removed the minimum value and thus have to find the new one. 217 | int i = min - initMin + 1; 218 | while (positions[i] >= size) { 219 | i++; 220 | } 221 | minT.setValue(i + initMin); 222 | awakeBounds(); 223 | } else if (max == value) { 224 | // We removed the maximum value and thus have to find the new one. 225 | int i = max - initMin - 1; 226 | while (positions[i] >= size) { 227 | i--; 228 | } 229 | maxT.setValue(i + initMin); 230 | awakeBounds(); 231 | } 232 | awakeChange(); 233 | return true; 234 | } 235 | 236 | @Override 237 | public boolean updateMin(int value) { 238 | int max = maxT.getValue(); 239 | if (value == max) { 240 | return assign(value); 241 | } 242 | if (max < value) { 243 | return false; 244 | } 245 | int min = minT.getValue(); 246 | if (value <= min) { 247 | return true; 248 | } 249 | // Remove values. 250 | int i = min - initMin; 251 | int size = sizeT.getValue(); 252 | while (i < value - initMin) { 253 | int position = positions[i]; 254 | if (position < size) { 255 | swap(position, --size); 256 | } 257 | i++; 258 | } 259 | // Search new min. 260 | while (size <= positions[i]) { 261 | i++; 262 | } 263 | 264 | // Update the domain. 265 | minT.setValue(i + initMin); 266 | sizeT.setValue(size); 267 | 268 | // Awake propagators. 269 | if (size == 1) { 270 | awakeAssign(); 271 | } 272 | awakeBounds(); 273 | awakeChange(); 274 | return true; 275 | } 276 | 277 | @Override 278 | public boolean updateMax(int value) { 279 | int min = minT.getValue(); 280 | if (value == min) { 281 | return assign(value); 282 | } 283 | if (min > value) { 284 | return false; 285 | } 286 | int max = maxT.getValue(); 287 | if (value >= max) { 288 | return true; 289 | } 290 | // Remove values. 291 | int i = max - initMin; 292 | int size = sizeT.getValue(); 293 | while (i > value - initMin) { 294 | int position = positions[i]; 295 | if (position < size) { 296 | swap(position, --size); 297 | } 298 | i--; 299 | } 300 | // Search new max. 301 | while (size <= positions[i]) { 302 | i--; 303 | } 304 | 305 | // Update the domain. 306 | maxT.setValue(i + initMin); 307 | sizeT.setValue(size); 308 | 309 | // Awake propagators. 310 | if (size == 1) { 311 | awakeAssign(); 312 | } 313 | awakeBounds(); 314 | awakeChange(); 315 | return true; 316 | } 317 | 318 | @Override 319 | public int copyDomain(int[] array) { 320 | int size = sizeT.getValue(); 321 | System.arraycopy(values, 0, array, 0, size); 322 | return size; 323 | } 324 | 325 | @Override 326 | public void watchChange(Propagator propagator) { 327 | changeWatchers.push(propagator); 328 | } 329 | 330 | @Override 331 | public void watchAssign(Propagator propagator) { 332 | boundsWatchers.push(propagator); 333 | } 334 | 335 | @Override 336 | public void watchBounds(Propagator propagator) { 337 | changeWatchers.push(propagator); 338 | } 339 | 340 | private void awakeAssign() { 341 | assignWatchers.forEach(p -> pQueue.enqueue(p)); 342 | } 343 | 344 | private void awakeBounds() { 345 | boundsWatchers.forEach(p -> pQueue.enqueue(p)); 346 | } 347 | 348 | private void awakeChange() { 349 | changeWatchers.forEach(p -> pQueue.enqueue(p)); 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/main/java/kiwi/variable/IntVarOffset.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import kiwi.propagation.PropagationQueue; 19 | import kiwi.propagation.Propagator; 20 | import kiwi.trail.Trail; 21 | 22 | /** */ 23 | public class IntVarOffset extends IntVar { 24 | 25 | private final IntVar variable; 26 | private final int offset; 27 | 28 | public IntVarOffset(IntVar variable, int offset) { 29 | this.variable = variable; 30 | this.offset = offset; 31 | } 32 | 33 | @Override public PropagationQueue propagQueue() { 34 | return variable.propagQueue(); 35 | } 36 | 37 | @Override public Trail trail() { 38 | return variable.trail(); 39 | } 40 | 41 | @Override public int min() { 42 | return variable.min() + offset; 43 | } 44 | 45 | @Override public int max() { 46 | return variable.max() + offset; 47 | } 48 | 49 | @Override public int size() { 50 | return variable.size(); 51 | } 52 | 53 | @Override public boolean isAssigned() { 54 | return variable.isAssigned(); 55 | } 56 | 57 | @Override public boolean contains(int value) { 58 | return variable.contains(value - offset); 59 | } 60 | 61 | @Override public boolean assign(int value) { 62 | return variable.assign(value - offset); 63 | } 64 | 65 | @Override public boolean remove(int value) { 66 | return variable.remove(value - offset); 67 | } 68 | 69 | @Override public boolean updateMin(int value) { 70 | return variable.updateMin(value - offset); 71 | } 72 | 73 | @Override public boolean updateMax(int value) { 74 | return variable.updateMax(value - offset); 75 | } 76 | 77 | @Override public int copyDomain(int[] array) { 78 | int size = variable.copyDomain(array); 79 | for (int i = 0; i < size; i++) 80 | array[i] += offset; 81 | return size; 82 | } 83 | 84 | @Override public void watchChange(Propagator propagator) { 85 | variable.watchChange(propagator); 86 | } 87 | 88 | @Override public void watchAssign(Propagator propagator) { 89 | variable.watchAssign(propagator); 90 | } 91 | 92 | @Override public void watchBounds(Propagator propagator) { 93 | variable.watchBounds(propagator); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/kiwi/variable/IntVarOpposite.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import kiwi.propagation.PropagationQueue; 19 | import kiwi.propagation.Propagator; 20 | import kiwi.trail.Trail; 21 | 22 | /** */ 23 | public class IntVarOpposite extends IntVar { 24 | 25 | private final IntVar variable; 26 | 27 | public IntVarOpposite(IntVar variable) { 28 | this.variable = variable; 29 | } 30 | 31 | @Override public PropagationQueue propagQueue() { 32 | return variable.propagQueue(); 33 | } 34 | 35 | @Override public Trail trail() { 36 | return variable.trail(); 37 | } 38 | 39 | @Override public int min() { 40 | return -variable.max(); 41 | } 42 | 43 | @Override public int max() { 44 | return -variable.min(); 45 | } 46 | 47 | @Override public int size() { 48 | return variable.size(); 49 | } 50 | 51 | @Override public boolean isAssigned() { 52 | return variable.isAssigned(); 53 | } 54 | 55 | @Override public boolean contains(int value) { 56 | return variable.contains(-value); 57 | } 58 | 59 | @Override public boolean assign(int value) { 60 | return variable.assign(-value); 61 | } 62 | 63 | @Override public boolean remove(int value) { 64 | return variable.remove(-value); 65 | } 66 | 67 | @Override public boolean updateMin(int value) { 68 | return variable.updateMax(-value); 69 | } 70 | 71 | @Override public boolean updateMax(int value) { 72 | return variable.updateMin(-value); 73 | } 74 | 75 | @Override public int copyDomain(int[] array) { 76 | int size = variable.copyDomain(array); 77 | for (int i = 0; i < size; i++) { 78 | array[i] = -array[i]; 79 | } 80 | return size; 81 | } 82 | 83 | @Override public void watchChange(Propagator propagator) { 84 | variable.watchChange(propagator); 85 | } 86 | 87 | @Override public void watchAssign(Propagator propagator) { 88 | variable.watchAssign(propagator); 89 | } 90 | 91 | @Override public void watchBounds(Propagator propagator) { 92 | variable.watchBounds(propagator); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/kiwi/variable/IntVarSingleton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import kiwi.propagation.PropagationQueue; 19 | import kiwi.propagation.Propagator; 20 | import kiwi.trail.Trail; 21 | 22 | public class IntVarSingleton extends IntVar { 23 | 24 | private final PropagationQueue pQueue; 25 | private final Trail trail; 26 | private final int value; 27 | 28 | public IntVarSingleton(PropagationQueue pQueue, Trail trail, int value) { 29 | this.pQueue = pQueue; 30 | this.trail = trail; 31 | this.value = value; 32 | } 33 | 34 | @Override 35 | public PropagationQueue propagQueue() { 36 | return pQueue; 37 | } 38 | 39 | @Override 40 | public Trail trail() { 41 | return trail; 42 | } 43 | 44 | @Override 45 | public int min() { 46 | return value; 47 | } 48 | 49 | @Override 50 | public int max() { 51 | return value; 52 | } 53 | 54 | @Override 55 | public int size() { 56 | return 1; 57 | } 58 | 59 | @Override 60 | public boolean isAssigned() { 61 | return true; 62 | } 63 | 64 | @Override 65 | public boolean contains(int value) { 66 | return this.value == value; 67 | } 68 | 69 | @Override 70 | public boolean assign(int value) { 71 | return this.value == value; 72 | } 73 | 74 | @Override 75 | public boolean remove(int value) { 76 | return this.value != value; 77 | } 78 | 79 | @Override 80 | public boolean updateMin(int value) { 81 | return this.value >= value; 82 | } 83 | 84 | @Override 85 | public boolean updateMax(int value) { 86 | return this.value <= value; 87 | } 88 | 89 | @Override 90 | public int copyDomain(int[] array) { 91 | array[0] = value; 92 | return 1; 93 | } 94 | 95 | @Override 96 | public void watchChange(Propagator propagator) {} 97 | 98 | @Override 99 | public void watchAssign(Propagator propagator) {} 100 | 101 | @Override 102 | public void watchBounds(Propagator propagator) {} 103 | } -------------------------------------------------------------------------------- /src/test/java/kiwi/example/NQueensTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.example; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import kiwi.Solver; 20 | import kiwi.modeling.Constraints; 21 | import kiwi.modeling.Heuristics; 22 | import kiwi.modeling.Views; 23 | import kiwi.variable.IntVar; 24 | 25 | import org.junit.Test; 26 | 27 | public class NQueensTest { 28 | 29 | @Test 30 | public void test3Queens() { 31 | assertEquals(0, solveNQueens(3)); 32 | } 33 | 34 | @Test 35 | public void test4Queens() { 36 | assertEquals(2, solveNQueens(4) ); 37 | } 38 | 39 | @Test 40 | public void test8Queens() { 41 | assertEquals(92, solveNQueens(8)); 42 | } 43 | 44 | @Test 45 | public void test10Queens() { 46 | assertEquals(724, solveNQueens(10)); 47 | } 48 | 49 | private int solveNQueens(int n) { 50 | Solver solver = new Solver(); 51 | IntVar[] queens = new IntVar[n]; 52 | IntVar[] queensUp = new IntVar[n]; 53 | IntVar[] queensDown = new IntVar[n]; 54 | for (int i = 0; i < n; i++) { 55 | queens[i] = solver.intVar(0, n - 1); 56 | queensUp[i] = Views.offset(queens[i], i); 57 | queensDown[i] = Views.offset(queens[i], -i); 58 | } 59 | solver.add(Constraints.allDifferent(queens)); 60 | solver.add(Constraints.allDifferent(queensUp)); 61 | solver.add(Constraints.allDifferent(queensDown)); 62 | return solver.solve(Heuristics.binaryFirstFail(queens)).nSolutions; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/kiwi/variable/IntVarImplTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import kiwi.Solver; 19 | import kiwi.variable.IntVar; 20 | 21 | public class IntVarImplTest extends IntVarTest { 22 | 23 | @Override 24 | public IntVar intVar(Solver solver, int min, int max) { 25 | return solver.intVar(min, max); 26 | } 27 | 28 | @Override 29 | public IntVar intVar(Solver solver, int[] values) { 30 | return solver.intVar(values); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/kiwi/variable/IntVarOffsetTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import kiwi.Solver; 19 | import kiwi.variable.IntVar; 20 | import kiwi.variable.IntVarOffset; 21 | 22 | public class IntVarOffsetTest extends IntVarTest { 23 | 24 | @Override 25 | public IntVar intVar(Solver solver, int min, int max) { 26 | IntVar x = solver.intVar(min, max); 27 | return new IntVarOffset(new IntVarOffset(new IntVarOffset(x, -10), 12), -2); 28 | } 29 | 30 | @Override 31 | public IntVar intVar(Solver solver, int[] values) { 32 | IntVar x = solver.intVar(values); 33 | return new IntVarOffset(new IntVarOffset(new IntVarOffset(x, -10), 12), -2); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/kiwi/variable/IntVarOppositeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import kiwi.Solver; 19 | import kiwi.variable.IntVar; 20 | import kiwi.variable.IntVarOpposite; 21 | 22 | public class IntVarOppositeTest extends IntVarTest { 23 | 24 | @Override 25 | public IntVar intVar(Solver solver, int min, int max) { 26 | IntVar x = solver.intVar(min, max); 27 | return new IntVarOpposite(new IntVarOpposite(x)); 28 | } 29 | 30 | @Override 31 | public IntVar intVar(Solver solver, int[] values) { 32 | IntVar x = solver.intVar(values); 33 | return new IntVarOpposite(new IntVarOpposite(x)); 34 | } 35 | } -------------------------------------------------------------------------------- /src/test/java/kiwi/variable/IntVarSingletonTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import kiwi.Solver; 23 | import kiwi.variable.IntVar; 24 | 25 | import org.junit.Test; 26 | 27 | public class IntVarSingletonTest { 28 | 29 | // All values should be contained in the initial domain 30 | @Test 31 | public void test1() { 32 | Solver solver = new Solver(); 33 | IntVar x = solver.intVar(5); 34 | assertEquals(x.size(), 1); 35 | assertTrue(x.isAssigned()); 36 | assertTrue(x.contains(5)); 37 | } 38 | 39 | // Contains should return false if value is not in the domain 40 | @Test 41 | public void test3() { 42 | Solver solver = new Solver(); 43 | IntVar x = solver.intVar(5); 44 | assertFalse(x.contains(-5)); 45 | assertFalse(x.contains(0)); 46 | assertFalse(x.contains(4)); 47 | assertFalse(x.contains(6)); 48 | } 49 | 50 | // UpdateMin with a lesser or equal value than min should not impact the 51 | // domain 52 | @Test 53 | public void test5() { 54 | Solver solver = new Solver(); 55 | IntVar x = solver.intVar(5); 56 | assertTrue(x.updateMin(4)); 57 | assertEquals(x.size(), 1); 58 | assertEquals(x.min(), 5); 59 | assertEquals(x.max(), 5); 60 | assertTrue(x.updateMin(5)); 61 | assertEquals(x.size(), 1); 62 | assertEquals(x.min(), 5); 63 | assertEquals(x.max(), 5); 64 | } 65 | 66 | // UpdateMin greater than max should fail 67 | @Test 68 | public void test7() { 69 | Solver solver = new Solver(); 70 | IntVar x = solver.intVar(5); 71 | assertFalse(x.updateMin(6)); 72 | } 73 | 74 | // UpdateMax with a greater or equal value than max should not impact the 75 | // domain 76 | @Test 77 | public void test9() { 78 | Solver solver = new Solver(); 79 | IntVar x = solver.intVar(5); 80 | assertTrue(x.updateMax(6)); 81 | assertEquals(x.size(), 1); 82 | assertEquals(x.min(), 5); 83 | assertEquals(x.max(), 5); 84 | assertTrue(x.updateMax(5)); 85 | assertEquals(x.size(), 1); 86 | assertEquals(x.min(), 5); 87 | assertEquals(x.max(), 5); 88 | } 89 | 90 | // UpdateMax lesser than min should fail 91 | @Test 92 | public void test11() { 93 | Solver solver = new Solver(); 94 | IntVar x = solver.intVar(5); 95 | assertFalse(x.updateMax(0)); 96 | } 97 | 98 | // Assign the assigned value should do nothing. 99 | @Test 100 | public void test14() { 101 | Solver solver = new Solver(); 102 | IntVar x = solver.intVar(5); 103 | assertTrue(x.assign(5)); 104 | assertTrue(x.contains(5)); 105 | assertTrue(x.isAssigned()); 106 | assertEquals(x.min(), 5); 107 | assertEquals(x.max(), 5); 108 | } 109 | 110 | // Assign an out of bounds value should fail 111 | @Test 112 | public void test16() { 113 | Solver solver = new Solver(); 114 | IntVar x = solver.intVar(5); 115 | assertFalse(x.assign(20)); 116 | } 117 | 118 | // Remove a removed value should not impact the domain 119 | @Test 120 | public void test19() { 121 | Solver solver = new Solver(); 122 | IntVar x = solver.intVar(5); 123 | assertTrue(x.remove(4)); 124 | assertTrue(x.isAssigned()); 125 | assertTrue(x.remove(6)); 126 | assertTrue(x.isAssigned()); 127 | } 128 | 129 | // Remove the assigned value should fail 130 | @Test 131 | public void test23() { 132 | Solver solver = new Solver(); 133 | IntVar x = solver.intVar(5); 134 | assertFalse(x.remove(5)); 135 | } 136 | 137 | // Copy domain and to Array 138 | @Test 139 | public void test28() { 140 | Solver solver = new Solver(); 141 | int[] domain = new int[1]; 142 | IntVar x = solver.intVar(5); 143 | assertEquals(x.size(), x.copyDomain(domain)); 144 | assertEquals(domain[0], 5); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/test/java/kiwi/variable/IntVarTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016, Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package kiwi.variable; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.Arrays; 23 | 24 | import kiwi.Solver; 25 | import kiwi.trail.Trail; 26 | import kiwi.variable.IntVar; 27 | 28 | import org.junit.Test; 29 | 30 | public abstract class IntVarTest { 31 | 32 | public abstract IntVar intVar(Solver solver, int min, int max); 33 | 34 | public abstract IntVar intVar(Solver solver, int[] values); 35 | 36 | // Returns true if the variable contains all the values. 37 | private boolean containsAll(IntVar x, int... values) { 38 | for (int i = 0; i < values.length; i++) { 39 | if (!x.contains(values[i])) { 40 | return false; 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | // All values should be contained in the initial domain 47 | @Test 48 | public void test1() { 49 | Solver solver = new Solver(); 50 | IntVar x = intVar(solver, 5, 10); 51 | assertEquals(x.size(), 6); 52 | assertTrue(containsAll(x, 5, 6, 7, 8, 9, 10)); 53 | } 54 | 55 | // All values should be contained in the initial domain (sparse) 56 | @Test 57 | public void test1a() { 58 | Solver solver = new Solver(); 59 | int[] values = new int[]{-10, 0, 10, 100}; 60 | IntVar x = intVar(solver, values); 61 | assertEquals(x.size(), 4); 62 | assertTrue(containsAll(x, values)); 63 | } 64 | 65 | // Contains should return true if value is in the domain 66 | @Test 67 | public void test2() { 68 | Solver solver = new Solver(); 69 | IntVar x = intVar(solver, 5, 15); 70 | assertTrue(x.contains(5)); 71 | assertTrue(x.contains(10)); 72 | assertTrue(x.contains(15)); 73 | } 74 | 75 | // Contains should return false if value is not in the domain 76 | @Test 77 | public void test3() { 78 | Solver solver = new Solver(); 79 | IntVar x = intVar(solver, 5, 15); 80 | assertFalse(x.contains(-1000)); 81 | assertFalse(x.contains(-100)); 82 | assertFalse(x.contains(4)); 83 | assertFalse(x.contains(16)); 84 | assertFalse(x.contains(100)); 85 | assertFalse(x.contains(1000)); 86 | } 87 | 88 | // UpdateMin should remove all values lesser than min 89 | @Test 90 | public void test4() { 91 | Solver solver = new Solver(); 92 | IntVar x = intVar(solver, 5, 15); 93 | assertEquals(x.size(), 11); 94 | assertTrue(x.updateMin(10)); 95 | assertEquals(x.size(), 6); 96 | assertEquals(x.min(), 10); 97 | assertTrue(containsAll(x, 10, 11, 12, 13, 14, 15)); 98 | } 99 | 100 | // UpdateMin with a lesser or equal value than min should not impact the 101 | // domain 102 | @Test 103 | public void test5() { 104 | Solver solver = new Solver(); 105 | IntVar x = intVar(solver, 5, 15); 106 | assertTrue(x.updateMin(4)); 107 | assertEquals(x.size(), 11); 108 | assertEquals(x.min(), 5); 109 | assertEquals(x.max(), 15); 110 | assertTrue(x.updateMin(5)); 111 | assertEquals(x.size(), 11); 112 | assertEquals(x.min(), 5); 113 | assertEquals(x.max(), 15); 114 | } 115 | 116 | // UpdateMin to max should assign max 117 | @Test 118 | public void test6() { 119 | Solver solver = new Solver(); 120 | IntVar x = intVar(solver, 5, 15); 121 | assertTrue(x.updateMin(15)); 122 | assertEquals(x.size(), 1); 123 | assertTrue(x.isAssigned()); 124 | assertTrue(x.contains(15)); 125 | } 126 | 127 | // UpdateMin greater than max should fail 128 | @Test 129 | public void test7() { 130 | Solver solver = new Solver(); 131 | IntVar x = intVar(solver, 5, 15); 132 | assertFalse(x.updateMin(20)); 133 | } 134 | 135 | // UpdateMax should adjust the maximum value and the size 136 | @Test 137 | public void test8() { 138 | Solver solver = new Solver(); 139 | IntVar x = intVar(solver, 5, 15); 140 | assertEquals(x.size(), 11); 141 | assertTrue(x.updateMax(10)); 142 | assertEquals(x.size(), 6); 143 | assertEquals(x.max(), 10); 144 | assertTrue(containsAll(x, 5, 6, 7, 8, 9, 10)); 145 | } 146 | 147 | // UpdateMax with a greater or equal value than max should not impact the 148 | // domain 149 | @Test 150 | public void test9() { 151 | Solver solver = new Solver(); 152 | IntVar x = intVar(solver, 5, 15); 153 | assertTrue(x.updateMax(20)); 154 | assertEquals(x.size(), 11); 155 | assertEquals(x.min(), 5); 156 | assertEquals(x.max(), 15); 157 | assertTrue(x.updateMax(15)); 158 | assertEquals(x.size(), 11); 159 | assertEquals(x.min(), 5); 160 | assertEquals(x.max(), 15); 161 | } 162 | 163 | // UpdateMax to min should assign min 164 | @Test 165 | public void test10() { 166 | Solver solver = new Solver(); 167 | IntVar x = intVar(solver, 5, 15); 168 | assertTrue(x.updateMax(5)); 169 | assertEquals(x.size(), 1); 170 | assertTrue(x.isAssigned()); 171 | assertTrue(x.contains(5)); 172 | } 173 | 174 | // UpdateMax lesser than min should fail 175 | @Test 176 | public void test11() { 177 | Solver solver = new Solver(); 178 | IntVar x = intVar(solver, 5, 15); 179 | assertFalse(x.updateMax(0)); 180 | } 181 | 182 | // Bounds should be restored when a backtrack occurs 183 | @Test 184 | public void test13() { 185 | Solver solver = new Solver(); 186 | Trail trail = solver.trail(); 187 | IntVar x = intVar(solver, 5, 15); 188 | trail.newLevel(); 189 | assertTrue(x.updateMax(10)); 190 | assertEquals(x.min(), 5); 191 | assertEquals(x.max(), 10); 192 | assertEquals(x.size(), 6); 193 | assertTrue(containsAll(x, 5, 6, 7, 8, 9, 10)); 194 | trail.newLevel(); 195 | assertTrue(x.updateMax(9)); 196 | assertTrue(x.updateMin(6)); 197 | assertEquals(x.min(), 6); 198 | assertEquals(x.max(), 9); 199 | assertEquals(x.size(), 4); 200 | assertTrue(containsAll(x, 6, 7, 8, 9)); 201 | trail.newLevel(); 202 | trail.undoLevel(); 203 | assertEquals(x.min(), 6); 204 | assertEquals(x.max(), 9); 205 | assertEquals(x.size(), 4); 206 | assertTrue(containsAll(x, 6, 7, 8, 9)); 207 | trail.undoLevel(); 208 | assertEquals(x.min(), 5); 209 | assertEquals(x.max(), 10); 210 | assertEquals(x.size(), 6); 211 | assertTrue(containsAll(x, 5, 6, 7, 8, 9, 10)); 212 | trail.undoLevel(); 213 | assertEquals(x.min(), 5); 214 | assertEquals(x.max(), 15); 215 | assertEquals(x.size(), 11); 216 | assertTrue(containsAll(x, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)); 217 | } 218 | 219 | // Assign should make min equal to max 220 | @Test 221 | public void test14() { 222 | Solver solver = new Solver(); 223 | IntVar x = intVar(solver, 5, 15); 224 | assertTrue(x.assign(10)); 225 | assertTrue(x.contains(10)); 226 | assertEquals(x.min(), 10); 227 | assertEquals(x.max(), 10); 228 | } 229 | 230 | // Assign should reduce the size to 1 231 | @Test 232 | public void test15() { 233 | Solver solver = new Solver(); 234 | IntVar x = intVar(solver, 5, 15); 235 | assertTrue(x.assign(10)); 236 | assertEquals(x.size(), 1); 237 | } 238 | 239 | // Assign an out of bounds value should fail 240 | @Test 241 | public void test16() { 242 | Solver solver = new Solver(); 243 | IntVar x = intVar(solver, 5, 15); 244 | assertFalse(x.assign(20)); 245 | } 246 | 247 | // Removed values should not be contained in the domain anymore 248 | @Test 249 | public void test17() { 250 | Solver solver = new Solver(); 251 | IntVar x = intVar(solver, 5, 10); 252 | assertTrue(x.remove(5)); 253 | assertFalse(x.contains(5)); 254 | assertTrue(x.remove(7)); 255 | assertFalse(x.contains(7)); 256 | assertTrue(x.remove(8)); 257 | assertFalse(x.contains(8)); 258 | } 259 | 260 | // Remove a value should reduce the size 261 | @Test 262 | public void test18() { 263 | Solver solver = new Solver(); 264 | IntVar x = intVar(solver, 5, 10); 265 | int size = x.size(); 266 | assertTrue(x.remove(5)); 267 | assertEquals(x.size(), size - 1); 268 | assertTrue(x.remove(5)); 269 | assertEquals(x.size(), size - 1); 270 | assertTrue(x.remove(6)); 271 | assertEquals(x.size(), size - 2); 272 | } 273 | 274 | // Remove a removed value should not impact the domain 275 | @Test 276 | public void test19() { 277 | Solver solver = new Solver(); 278 | IntVar x = intVar(solver, 5, 10); 279 | int size = x.size(); 280 | assertTrue(x.remove(4)); 281 | assertEquals(x.size(), size); 282 | assertTrue(x.remove(11)); 283 | assertEquals(x.size(), size); 284 | } 285 | 286 | // Remove the minimal value should change the minimum value 287 | @Test 288 | public void test20() { 289 | Solver solver = new Solver(); 290 | IntVar x = intVar(solver, 5, 10); 291 | assertTrue(x.remove(5)); 292 | assertEquals(x.min(), 6); 293 | assertTrue(x.remove(6)); 294 | assertTrue(x.remove(7)); 295 | assertEquals(x.min(), 8); 296 | assertTrue(x.remove(10)); 297 | assertEquals(x.min(), 8); 298 | } 299 | 300 | // Remove all but one value should assign that value 301 | @Test 302 | public void test21() { 303 | Solver solver = new Solver(); 304 | IntVar x = intVar(solver, 5, 10); 305 | assertTrue(x.remove(5)); 306 | assertTrue(x.contains(7)); 307 | assertTrue(x.remove(6)); 308 | assertTrue(x.contains(7)); 309 | assertTrue(x.remove(9)); 310 | assertTrue(x.contains(7)); 311 | assertTrue(x.remove(10)); 312 | assertTrue(x.contains(7)); 313 | assertTrue(x.remove(8)); 314 | assertTrue(x.contains(7)); 315 | assertTrue(x.isAssigned()); 316 | } 317 | 318 | // Removed values should be restored when a backtrack occurs 319 | @Test 320 | public void test22() { 321 | Solver solver = new Solver(); 322 | Trail trail = solver.trail(); 323 | IntVar x = intVar(solver, 5, 10); 324 | trail.newLevel(); 325 | assertTrue(x.remove(5)); 326 | assertTrue(x.remove(6)); 327 | trail.newLevel(); 328 | assertTrue(x.remove(9)); 329 | trail.newLevel(); 330 | assertTrue(x.remove(8)); 331 | assertFalse(x.contains(5)); 332 | assertFalse(x.contains(6)); 333 | assertTrue(x.contains(7)); 334 | assertFalse(x.contains(8)); 335 | assertFalse(x.contains(9)); 336 | assertTrue(x.contains(10)); 337 | trail.undoLevel(); 338 | assertFalse(x.contains(5)); 339 | assertFalse(x.contains(6)); 340 | assertTrue(x.contains(7)); 341 | assertTrue(x.contains(8)); 342 | assertFalse(x.contains(9)); 343 | assertTrue(x.contains(10)); 344 | trail.undoLevel(); 345 | assertFalse(x.contains(5)); 346 | assertFalse(x.contains(6)); 347 | assertTrue(x.contains(7)); 348 | assertTrue(x.contains(8)); 349 | assertTrue(x.contains(9)); 350 | assertTrue(x.contains(10)); 351 | trail.undoLevel(); 352 | assertTrue(x.contains(5)); 353 | assertTrue(x.contains(6)); 354 | assertTrue(x.contains(7)); 355 | assertTrue(x.contains(8)); 356 | assertTrue(x.contains(9)); 357 | assertTrue(x.contains(10)); 358 | } 359 | 360 | // Remove the assigned value should fail 361 | @Test 362 | public void test23() { 363 | Solver solver = new Solver(); 364 | IntVar x = intVar(solver, 10, 10); 365 | assertFalse(x.remove(10)); 366 | } 367 | 368 | // UpdateMin should adjust the minimum value and the size (sparse) 369 | @Test 370 | public void test24() { 371 | Solver solver = new Solver(); 372 | int[] values = new int[]{10, 11, 15, 16, 17, 20, 21, 25}; 373 | IntVar x = intVar(solver, values); 374 | assertTrue(x.updateMin(12)); 375 | assertEquals(x.size(), 6); 376 | assertEquals(x.min(), 15); 377 | } 378 | 379 | // UpdateMin should remove all values lesser than min (sparse) 380 | @Test 381 | public void test25() { 382 | Solver solver = new Solver(); 383 | int[] values = new int[]{10, 11, 15, 16, 17, 20, 21, 25}; 384 | IntVar x = intVar(solver, values); 385 | assertTrue(x.updateMin(16)); 386 | assertFalse(x.contains(10)); 387 | assertFalse(x.contains(11)); 388 | assertFalse(x.contains(15)); 389 | } 390 | 391 | // UpdateMax should adjust the maximum value and the size (sparse) 392 | @Test 393 | public void test26() { 394 | Solver solver = new Solver(); 395 | int[] values = new int[]{10, 11, 15, 16, 17, 20, 21, 25}; 396 | IntVar x = intVar(solver, values); 397 | assertTrue(x.updateMax(19)); 398 | assertEquals(x.size(), 5); 399 | assertEquals(x.max(), 17); 400 | } 401 | 402 | // UpdateMax should remove all values greater than max (sparse) 403 | @Test 404 | public void test27() { 405 | Solver solver = new Solver(); 406 | int[] values = new int[]{10, 11, 15, 16, 17, 20, 21, 25}; 407 | IntVar x = intVar(solver, values); 408 | assertTrue(x.updateMax(17)); 409 | assertFalse(x.contains(20)); 410 | assertFalse(x.contains(21)); 411 | assertFalse(x.contains(25)); 412 | } 413 | 414 | // Copy domain and to Array 415 | @Test 416 | public void test28() { 417 | Solver solver = new Solver(); 418 | int[] values1 = new int[]{10, 11, 15, 16, 17, 20, 21, 25}; 419 | int[] domain1 = new int[values1.length]; 420 | IntVar x = intVar(solver, values1); 421 | assertTrue(containsAll(x, values1)); 422 | assertEquals(x.size(), values1.length); 423 | assertEquals(x.size(), x.copyDomain(domain1)); 424 | assertTrue(containsAll(x, domain1)); 425 | assertTrue(x.remove(11)); 426 | assertTrue(x.remove(17)); 427 | assertTrue(x.remove(25)); 428 | assertEquals(x.size(), 5); 429 | int[] values2 = new int[]{10, 15, 16, 20, 21}; 430 | int[] domain2 = new int[values2.length]; 431 | assertTrue(containsAll(x, values2)); 432 | assertEquals(x.size(), values2.length); 433 | assertEquals(x.size(), x.copyDomain(domain2)); 434 | Arrays.sort(domain2); 435 | for (int i = 0; i < x.size(); i++) { 436 | assertEquals(values2[i], domain2[i]); 437 | } 438 | } 439 | } 440 | --------------------------------------------------------------------------------