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