1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.sat4j.pb;
31
32 import static org.junit.Assert.assertEquals;
33 import static org.junit.Assert.assertFalse;
34 import static org.junit.Assert.assertTrue;
35 import static org.sat4j.pb.tools.WeightedObject.newWO;
36
37 import java.util.List;
38 import java.util.Set;
39
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.sat4j.pb.tools.DependencyHelper;
43 import org.sat4j.specs.ContradictionException;
44 import org.sat4j.specs.TimeoutException;
45
46 public class XplainTests {
47 private static final int EXTRA_IMPLICATIONS_PER_LEVEL = 10;
48 private DependencyHelper<String, String> helper;
49
50 @Before
51 public void setUp() {
52
53
54
55 this.helper = new DependencyHelper<String, String>(
56 SolverFactory.newEclipseP2());
57 }
58
59 @Test(timeout = 10000)
60 public void testRequiredSoftwareDependsOnOlderVersion()
61 throws ContradictionException, TimeoutException {
62 this.helper.setTrue("profile", "profile must exist");
63 this.helper.implication("profile").implies("a_1").named("profile->a_1");
64 addExtraImplications("profile");
65 this.helper.implication("a_1").implies("b_1").named("a_1->b_1");
66 addExtraImplications("a_1");
67 this.helper.implication("b_1").implies("c_[2,3)").named("b_1->c_[2,3)");
68 addExtraImplications("b_1");
69 this.helper.implication("c_[2,3)").implies("p(c_[2,3))")
70 .named("c_[2,3) does not exist");
71 this.helper.setFalse("p(c_[2,3))", "placeholder(c_[2,3))");
72
73 Explanation<String> explanation = new Explanation<String>();
74 explanation.newFalseRoot("profile must exist").newChild("profile->a_1")
75 .newChild("a_1->b_1").newChild("b_1->c_[2,3)")
76 .newChild("c_[2,3) does not exist")
77 .newChild("placeholder(c_[2,3))");
78 checkExplanationForMissingRequirement(explanation);
79 }
80
81 @Test(timeout = 10000)
82 public void testRequiredSoftwareDependsOnOlderVersionDeepTree()
83 throws ContradictionException, TimeoutException {
84 this.helper.setTrue("profile", "profile must exist");
85 this.helper.implication("profile").implies("a_1").named("profile->a_1");
86 addExtraImplications("profile");
87
88 Explanation<String> explanation = new Explanation<String>();
89 DepdendenyNode<String> node = explanation.newFalseRoot(
90 "profile must exist").newChild("profile->a_1");
91
92 String lastThing = "a_1";
93 for (int i = 0; i < 10; i++) {
94 String newThing = "newThing" + i;
95 String name = lastThing + "->" + newThing;
96 this.helper.implication(lastThing).implies(newThing).named(name);
97 node = node.newChild(name);
98 addExtraImplications(lastThing);
99 lastThing = newThing;
100 }
101 this.helper.implication(lastThing).implies("c_[2,3)")
102 .named("b_1->c_[2,3)");
103 node = node.newChild("b_1->c_[2,3)");
104 addExtraImplications(lastThing);
105 this.helper.implication("c_[2,3)").implies("p(c_[2,3))")
106 .named("c_[2,3) does not exist");
107 node = node.newChild("c_[2,3) does not exist");
108 this.helper.setFalse("p(c_[2,3))", "placeholder(c_[2,3))");
109 node = node.newChild("placeholder(c_[2,3))");
110
111 checkExplanationForMissingRequirement(explanation);
112 }
113
114
115
116 @Test(timeout = 10000)
117 public void testUseWeightToOrderSolutions() throws ContradictionException,
118 TimeoutException {
119 this.helper.setTrue("profile", "profile must exist");
120 this.helper.implication("profile").implies("a_1").named("profile->a_1");
121 this.helper.implication("profile").implies("a_2").named("profile->a_2");
122 addExtraImplications("profile");
123 this.helper.implication("a_1").implies("b_1").named("a_1->b_1");
124 this.helper.implication("a_1").implies("b_2").named("a_1->b_2");
125 addExtraImplications("a_1");
126 this.helper.implication("a_2").implies("b_1").named("a_2->b_1");
127 this.helper.implication("a_2").implies("b_2").named("a_2->b_2");
128 addExtraImplications("a_2");
129 this.helper.implication("b_1").implies("c_[2,3)").named("b_1->c_[2,3)");
130 addExtraImplications("b_1");
131 this.helper.implication("b_2").implies("c_[2,3)").named("b_2->c_[2,3)");
132 addExtraImplications("b_2");
133
134
135 this.helper.setObjectiveFunction(newWO("a_1", 2), newWO("a_2", 1),
136 newWO("b_1", 8), newWO("b_2", 4));
137 this.helper.implication("c_[2,3)").implies("p(c_[2,3))")
138 .named("c_[2,3) does not exist");
139 this.helper.setFalse("p(c_[2,3))", "placeholder(c_[2,3))");
140
141 Explanation<String> explanation = new Explanation<String>();
142 explanation.newFalseRoot("profile must exist").newChild("profile->a_2")
143 .newChild("a_2->b_2").newChild("b_2->c_[2,3)")
144 .newChild("c_[2,3) does not exist")
145 .newChild("placeholder(c_[2,3))");
146 checkExplanationForMissingRequirement(explanation);
147 }
148
149 @Test(timeout = 10000)
150 public void testUseNumberOfMissingVariablesToOrderExplanations()
151 throws ContradictionException, TimeoutException {
152 this.helper.setTrue("profile", "profile must exist");
153 this.helper.implication("profile").implies("a_1").named("profile->a_1");
154 this.helper.implication("profile").implies("a_2").named("profile->a_2");
155 addExtraImplications("profile");
156 this.helper.implication("a_1").implies("b_1").named("a_1->b_1");
157 addExtraImplications("a_1");
158 this.helper.implication("a_2").implies("b_1").named("a_2->b_1");
159 this.helper.implication("a_2").implies("d").named("a_2->d");
160 addExtraImplications("a_2");
161 this.helper.implication("b_1").implies("c_[2,3)").named("b_1->c_[2,3)");
162 addExtraImplications("b_1");
163
164
165 this.helper.setObjectiveFunction(newWO("a_1", 2), newWO("a_2", 1),
166 newWO("b_1", 8), newWO("b_2", 4));
167 this.helper.implication("c_[2,3)").implies("p(c_[2,3))")
168 .named("c_[2,3) does not exist");
169 this.helper.setFalse("p(c_[2,3))", "placeholder(c_[2,3))");
170 this.helper.implication("d").implies("p(d)").named("d does not exist");
171 this.helper.setFalse("p(d)", "placeholder(d)");
172
173 Explanation<String> explanation = new Explanation<String>();
174 explanation.newFalseRoot("profile must exist").newChild("profile->a_1")
175 .newChild("a_1->b_1").newChild("b_1->c_[2,3)")
176 .newChild("c_[2,3) does not exist")
177 .newChild("placeholder(c_[2,3))");
178 checkExplanationForMissingRequirement(explanation);
179 }
180
181
182 @Test(timeout = 10000)
183 public void testUseNumberOfMissingVariablesAndWeightToOrderExplanations()
184 throws ContradictionException, TimeoutException {
185 this.helper.setTrue("profile", "profile must exist");
186 this.helper.implication("profile").implies("a_1").named("profile->a_1");
187 this.helper.implication("profile").implies("a_2").named("profile->a_2");
188 addExtraImplications("profile");
189 this.helper.implication("a_1").implies("b_1").named("a_1->b_1");
190 this.helper.implication("a_1").implies("b_2").named("a_1->b_2");
191 addExtraImplications("a_1");
192 this.helper.implication("a_2").implies("b_1").named("a_2->b_1");
193 this.helper.implication("a_2").implies("b_2").named("a_2->b_2");
194 this.helper.implication("a_2").implies("d").named("a_2->d");
195 addExtraImplications("a_2");
196 this.helper.implication("b_1").implies("c_[2,3)").named("b_1->c_[2,3)");
197 addExtraImplications("b_1");
198 this.helper.implication("b_2").implies("c_[2,3)").named("b_2->c_[2,3)");
199 addExtraImplications("b_2");
200
201
202 this.helper.setObjectiveFunction(newWO("a_1", 2), newWO("a_2", 1),
203 newWO("b_1", 8), newWO("b_2", 4));
204 this.helper.implication("c_[2,3)").implies("p(c_[2,3))")
205 .named("c_[2,3) does not exist");
206 this.helper.setFalse("p(c_[2,3))", "placeholder(c_[2,3))");
207 this.helper.implication("d").implies("p(d)").named("d does not exist");
208 this.helper.setFalse("p(d)", "placeholder(d)");
209
210 Explanation<String> explanation = new Explanation<String>();
211 explanation.newFalseRoot("profile must exist").newChild("profile->a_1")
212 .newChild("a_1->b_2").newChild("b_2->c_[2,3)")
213 .newChild("c_[2,3) does not exist")
214 .newChild("placeholder(c_[2,3))");
215 checkExplanationForMissingRequirement(explanation);
216 }
217
218 @Test
219 public void testConflictingRequirements() throws ContradictionException,
220 TimeoutException {
221 this.helper.setTrue("profile", "profile must exist");
222 this.helper.implication("profile").implies("a_1").named("profile->a_1");
223 this.helper.implication("profile").implies("b_1").named("profile->b_1");
224 addExtraImplications("profile");
225 this.helper.implication("a_1").implies("c_1").named("a_1->c_1");
226 addExtraImplications("a_1");
227 this.helper.implication("b_1").implies("d_1").named("b_1->d_1");
228 addExtraImplications("b_1");
229 this.helper.implication("c_1").implies("d_2").named("c_1->d_2");
230 addExtraImplications("c_1");
231 this.helper.setTrue("d_1", "d_1 exists");
232 this.helper.setTrue("d_2", "d_2 exists");
233 this.helper.atMost(1, "d_1", "d_2").named("singleton on d");
234
235 Explanation<String> explanation = new Explanation<String>();
236 Conflict<String> conflict = explanation.newConflict();
237 conflict.newRoot("profile must exist").newChild("profile->a_1")
238 .newChild("a_1->c_1").newChild("c_1->d_2");
239 conflict.newRoot("profile must exist").newChild("profile->b_1")
240 .newChild("b_1->d_1");
241 checkExplanationForConflict(explanation);
242 }
243
244 private void checkExplanationForConflict(Explanation<String> explanation)
245 throws TimeoutException {
246 assertFalse(this.helper.hasASolution());
247
248
249
250
251
252
253 List<Conflict<String>> conflicts = explanation.getConflicts();
254
255
256
257 Set<String> cause = this.helper.why();
258
259
260 for (Conflict<String> conflict : conflicts) {
261 List<DepdendenyNode<String>> roots = conflict.getRoots();
262 for (DepdendenyNode<String> node : roots) {
263 while (node != null) {
264 assertTrue("Could not find " + node.getName(),
265 cause.remove(node.getName()));
266 node = node.getOnlyChild();
267 }
268 }
269 }
270 assertTrue(cause.isEmpty());
271 }
272
273 private void checkExplanationForMissingRequirement(
274 Explanation<String> explanation) throws TimeoutException {
275 assertFalse(this.helper.hasASolution());
276
277 Set<String> cause = this.helper.why();
278 List<DepdendenyNode<String>> roots = explanation.getRoots();
279 assertEquals(1, roots.size());
280
281
282 DepdendenyNode<String> root = roots.get(0);
283 assertFalse(root.hasBranches());
284 assertEquals(root.getMaxDepth(), cause.size());
285
286 DepdendenyNode<String> node = root;
287 while (node != null) {
288 assertTrue("Could not find " + node.getName() + " in " + cause,
289 cause.contains(node.getName()));
290 node = node.getOnlyChild();
291 }
292 }
293
294
295
296
297
298
299
300
301
302 private void addExtraImplications(String variable)
303 throws ContradictionException {
304 for (int i = 0; i < EXTRA_IMPLICATIONS_PER_LEVEL; i++) {
305 String newVariable = variable + "Extra" + i;
306 this.helper.setTrue(newVariable, newVariable + " exists");
307 this.helper.implication(variable).implies(newVariable)
308 .named(variable + "->" + newVariable);
309 }
310 }
311 }