View Javadoc

1   /*******************************************************************************
2    * SAT4J: a SATisfiability library for Java Copyright (C) 2004-2008 Daniel Le Berre
3    *
4    * All rights reserved. This program and the accompanying materials
5    * are made available under the terms of the Eclipse Public License v1.0
6    * which accompanies this distribution, and is available at
7    * http://www.eclipse.org/legal/epl-v10.html
8    *
9    * Alternatively, the contents of this file may be used under the terms of
10   * either the GNU Lesser General Public License Version 2.1 or later (the
11   * "LGPL"), in which case the provisions of the LGPL are applicable instead
12   * of those above. If you wish to allow use of your version of this file only
13   * under the terms of the LGPL, and not to allow others to use your version of
14   * this file under the terms of the EPL, indicate your decision by deleting
15   * the provisions above and replace them with the notice and other provisions
16   * required by the LGPL. If you do not delete the provisions above, a recipient
17   * may use your version of this file under the terms of the EPL or the LGPL.
18   * 
19   * Based on the original MiniSat specification from:
20   * 
21   * An extensible SAT solver. Niklas Een and Niklas Sorensson. Proceedings of the
22   * Sixth International Conference on Theory and Applications of Satisfiability
23   * Testing, LNCS 2919, pp 502-518, 2003.
24   *
25   * See www.minisat.se for the original solver in C++.
26   * 
27   *******************************************************************************/
28  package org.sat4j.tools;
29  
30  import java.math.BigInteger;
31  
32  import org.sat4j.core.Vec;
33  import org.sat4j.core.VecInt;
34  import org.sat4j.specs.ContradictionException;
35  import org.sat4j.specs.IConstr;
36  import org.sat4j.specs.ISolver;
37  import org.sat4j.specs.IVec;
38  import org.sat4j.specs.IVecInt;
39  
40  /**
41   * Utility class to easily feed a SAT solver using logical gates.
42   * 
43   * @author leberre
44   * 
45   */
46  public class GateTranslator extends SolverDecorator<ISolver> {
47  
48  	/**
49       * 
50       */
51  	private static final long serialVersionUID = 1L;
52  
53  	public GateTranslator(ISolver solver) {
54  		super(solver);
55  	}
56  
57  	/**
58  	 * translate y <=> FALSE into a clause.
59  	 * 
60  	 * @param y
61  	 *            a variable to falsify
62  	 * @throws ContradictionException
63  	 *             iff a trivial inconsistency is found.
64  	 * @since 2.1
65  	 */
66  	public IConstr gateFalse(int y) throws ContradictionException {
67  		IVecInt clause = new VecInt(2);
68  		clause.push(-y);
69  		return processClause(clause);
70  	}
71  
72  	/**
73  	 * translate y <=> TRUE into a clause.
74  	 * 
75  	 * @param y
76  	 *            a variable to verify
77  	 * @throws ContradictionException
78  	 * @since 2.1
79  	 */
80  	public IConstr gateTrue(int y) throws ContradictionException {
81  		IVecInt clause = new VecInt(2);
82  		clause.push(y);
83  		return processClause(clause);
84  	}
85  
86  	/**
87  	 * translate y <=> if x1 then x2 else x3 into clauses.
88  	 * 
89  	 * @param y
90  	 * @param x1
91  	 *            the selector variable
92  	 * @param x2
93  	 * @param x3
94  	 * @throws ContradictionException
95  	 * @since 2.1
96  	 */
97  	public IConstr[] ite(int y, int x1, int x2, int x3)
98  			throws ContradictionException {
99  		IConstr[] constrs = new IConstr[6];
100 		IVecInt clause = new VecInt(5);
101 		// y <=> (x1 -> x2) and (not x1 -> x3)
102 		// y -> (x1 -> x2) and (not x1 -> x3)
103 		clause.push(-y).push(-x1).push(x2);
104 		constrs[0] = processClause(clause);
105 		clause.clear();
106 		clause.push(-y).push(x1).push(x3);
107 		constrs[1] = processClause(clause);
108 		// y <- (x1 -> x2) and (not x1 -> x3)
109 		// not(x1 -> x2) or not(not x1 -> x3) or y
110 		// x1 and not x2 or not x1 and not x3 or y
111 		// (x1 and not x2) or ((not x1 or y) and (not x3 or y))
112 		// (x1 or not x1 or y) and (not x2 or not x1 or y) and (x1 or not x3 or
113 		// y) and (not x2 or not x3 or y)
114 		// not x1 or not x2 or y and x1 or not x3 or y and not x2 or not x3 or y
115 		clause.clear();
116 		clause.push(-x1).push(-x2).push(y);
117 		constrs[2] = processClause(clause);
118 		clause.clear();
119 		clause.push(x1).push(-x3).push(y);
120 		constrs[3] = processClause(clause);
121 		clause.clear();
122 		clause.push(-x2).push(-x3).push(y);
123 		constrs[4] = processClause(clause);
124 		// taken from Niklas Een et al SAT 2007 paper
125 		// Adding the following redundant clause will improve unit propagation
126 		// y -> x2 or x3
127 		clause.clear();
128 		clause.push(-y).push(x2).push(x3);
129 		constrs[5] = processClause(clause);
130 		return constrs;
131 	}
132 
133 	/**
134 	 * Translate y <=> x1 /\ x2 /\ ... /\ xn into clauses.
135 	 * 
136 	 * @param y
137 	 * @param literals
138 	 *            the x1 ... xn literals.
139 	 * @throws ContradictionException
140 	 * @since 2.1
141 	 */
142 	public IConstr[] and(int y, IVecInt literals) throws ContradictionException {
143 		// y <=> AND x1 ... xn
144 		IConstr[] constrs = new IConstr[literals.size() + 1];
145 		// y <= x1 .. xn
146 		IVecInt clause = new VecInt(literals.size() + 2);
147 		clause.push(y);
148 		for (int i = 0; i < literals.size(); i++) {
149 			clause.push(-literals.get(i));
150 		}
151 		constrs[0] = processClause(clause);
152 		clause.clear();
153 		for (int i = 0; i < literals.size(); i++) {
154 			// y => xi
155 			clause.clear();
156 			clause.push(-y);
157 			clause.push(literals.get(i));
158 			constrs[i + 1] = processClause(clause);
159 		}
160 		return constrs;
161 	}
162 
163 	/**
164 	 * Translate y <=> x1 /\ x2
165 	 * 
166 	 * @param y
167 	 * @param x1
168 	 * @param x2
169 	 * @throws ContradictionException
170 	 * @since 2.1
171 	 */
172 	public IConstr[] and(int y, int x1, int x2) throws ContradictionException {
173 		IVecInt clause = new VecInt(4);
174 		IConstr[] constrs = new IConstr[3];
175 		clause.push(-y);
176 		clause.push(x1);
177 		constrs[0] = addClause(clause);
178 		clause.clear();
179 		clause.push(-y);
180 		clause.push(x2);
181 		constrs[1] = addClause(clause);
182 		clause.clear();
183 		clause.push(y);
184 		clause.push(-x1);
185 		clause.push(-x2);
186 		constrs[2] = addClause(clause);
187 		return constrs;
188 	}
189 
190 	/**
191 	 * translate y <=> x1 \/ x2 \/ ... \/ xn into clauses.
192 	 * 
193 	 * @param y
194 	 * @param literals
195 	 * @throws ContradictionException
196 	 * @since 2.1
197 	 */
198 	public IConstr[] or(int y, IVecInt literals) throws ContradictionException {
199 		// y <=> OR x1 x2 ...xn
200 		// y => x1 x2 ... xn
201 		IConstr[] constrs = new IConstr[literals.size() + 1];
202 		IVecInt clause = new VecInt(literals.size() + 2);
203 		literals.copyTo(clause);
204 		clause.push(-y);
205 		constrs[0] = processClause(clause);
206 		clause.clear();
207 		for (int i = 0; i < literals.size(); i++) {
208 			// xi => y
209 			clause.clear();
210 			clause.push(y);
211 			clause.push(-literals.get(i));
212 			constrs[i + 1] = processClause(clause);
213 		}
214 		return constrs;
215 	}
216 
217 	private IConstr processClause(IVecInt clause) throws ContradictionException {
218 		return addClause(clause);
219 	}
220 
221 	/**
222 	 * Translate y <=> not x into clauses.
223 	 * 
224 	 * @param y
225 	 * @param x
226 	 * @throws ContradictionException
227 	 * @since 2.1
228 	 */
229 	public IConstr[] not(int y, int x) throws ContradictionException {
230 		IConstr[] constrs = new IConstr[2];
231 		IVecInt clause = new VecInt(3);
232 		// y <=> not x
233 		// y => not x = not y or not x
234 		clause.push(-y).push(-x);
235 		constrs[0] = processClause(clause);
236 		// y <= not x = y or x
237 		clause.clear();
238 		clause.push(y).push(x);
239 		constrs[1] = processClause(clause);
240 		return constrs;
241 	}
242 
243 	/**
244 	 * translate y <=> x1 xor x2 xor ... xor xn into clauses.
245 	 * 
246 	 * @param y
247 	 * @param literals
248 	 * @throws ContradictionException
249 	 * @since 2.1
250 	 */
251 	public IConstr[] xor(int y, IVecInt literals) throws ContradictionException {
252 		literals.push(-y);
253 		int[] f = new int[literals.size()];
254 		literals.copyTo(f);
255 		IVec<IConstr> vconstrs = new Vec<IConstr>();
256 		xor2Clause(f, 0, false, vconstrs);
257 		IConstr[] constrs = new IConstr[vconstrs.size()];
258 		vconstrs.copyTo(constrs);
259 		return constrs;
260 	}
261 
262 	/**
263 	 * translate y <=> (x1 <=> x2 <=> ... <=> xn) into clauses.
264 	 * 
265 	 * @param y
266 	 * @param literals
267 	 * @throws ContradictionException
268 	 * @since 2.1
269 	 */
270 	public IConstr[] iff(int y, IVecInt literals) throws ContradictionException {
271 		literals.push(y);
272 		int[] f = new int[literals.size()];
273 		literals.copyTo(f);
274 		IVec<IConstr> vconstrs = new Vec<IConstr>();
275 		iff2Clause(f, 0, false, vconstrs);
276 		IConstr[] constrs = new IConstr[vconstrs.size()];
277 		vconstrs.copyTo(constrs);
278 		return constrs;
279 	}
280 
281 	/**
282 	 * @since 2.2
283 	 */
284 	public void xor(int x, int a, int b) throws ContradictionException {
285 		IVecInt clause = new VecInt(3);
286 		clause.push(-a).push(b).push(x);
287 		processClause(clause);
288 		clause.clear();
289 		clause.push(a).push(-b).push(x);
290 		processClause(clause);
291 		clause.clear();
292 		clause.push(-a).push(-b).push(-x);
293 		processClause(clause);
294 		clause.clear();
295 		clause.push(a).push(b).push(-x);
296 		processClause(clause);
297 		clause.clear();
298 	}
299 
300 	private void xor2Clause(int[] f, int prefix, boolean negation,
301 			IVec<IConstr> constrs) throws ContradictionException {
302 		if (prefix == f.length - 1) {
303 			IVecInt clause = new VecInt(f.length + 1);
304 			for (int i = 0; i < f.length - 1; ++i) {
305 				clause.push(f[i]);
306 			}
307 			clause.push(f[f.length - 1] * (negation ? -1 : 1));
308 			constrs.push(processClause(clause));
309 			return;
310 		}
311 
312 		if (negation) {
313 			f[prefix] = -f[prefix];
314 			xor2Clause(f, prefix + 1, false, constrs);
315 			f[prefix] = -f[prefix];
316 
317 			xor2Clause(f, prefix + 1, true, constrs);
318 		} else {
319 			xor2Clause(f, prefix + 1, false, constrs);
320 
321 			f[prefix] = -f[prefix];
322 			xor2Clause(f, prefix + 1, true, constrs);
323 			f[prefix] = -f[prefix];
324 		}
325 	}
326 
327 	private void iff2Clause(int[] f, int prefix, boolean negation,
328 			IVec<IConstr> constrs) throws ContradictionException {
329 		if (prefix == f.length - 1) {
330 			IVecInt clause = new VecInt(f.length + 1);
331 			for (int i = 0; i < f.length - 1; ++i) {
332 				clause.push(f[i]);
333 			}
334 			clause.push(f[f.length - 1] * (negation ? -1 : 1));
335 			processClause(clause);
336 			return;
337 		}
338 
339 		if (negation) {
340 			iff2Clause(f, prefix + 1, false, constrs);
341 			f[prefix] = -f[prefix];
342 			iff2Clause(f, prefix + 1, true, constrs);
343 			f[prefix] = -f[prefix];
344 		} else {
345 			f[prefix] = -f[prefix];
346 			iff2Clause(f, prefix + 1, false, constrs);
347 			f[prefix] = -f[prefix];
348 			iff2Clause(f, prefix + 1, true, constrs);
349 		}
350 	}
351 
352 	// From Een and Sorensson JSAT 2006 paper
353 
354 	/**
355 	 * @since 2.2
356 	 */
357 	public void fullAdderSum(int x, int a, int b, int c)
358 			throws ContradictionException {
359 		IVecInt clause = new VecInt(4);
360 		// -a /\ -b /\ -c -> -x
361 		clause.push(a).push(b).push(c).push(-x);
362 		processClause(clause);
363 		clause.clear();
364 		// -a /\ b /\ c -> -x
365 		clause.push(a).push(-b).push(-c).push(-x);
366 		processClause(clause);
367 		clause.clear();
368 		clause.push(-a).push(b).push(-c).push(-x);
369 		processClause(clause);
370 		clause.clear();
371 		clause.push(-a).push(-b).push(c).push(-x);
372 		processClause(clause);
373 		clause.clear();
374 		clause.push(-a).push(-b).push(-c).push(x);
375 		processClause(clause);
376 		clause.clear();
377 		clause.push(-a).push(b).push(c).push(x);
378 		processClause(clause);
379 		clause.clear();
380 		clause.push(a).push(-b).push(c).push(x);
381 		processClause(clause);
382 		clause.clear();
383 		clause.push(a).push(b).push(-c).push(x);
384 		processClause(clause);
385 		clause.clear();
386 	}
387 
388 	/**
389 	 * @since 2.2
390 	 */
391 	public void fullAdderCarry(int x, int a, int b, int c)
392 			throws ContradictionException {
393 		IVecInt clause = new VecInt(3);
394 		clause.push(-b).push(-c).push(x);
395 		processClause(clause);
396 		clause.clear();
397 		clause.push(-a).push(-c).push(x);
398 		processClause(clause);
399 		clause.clear();
400 		clause.push(-a).push(-b).push(x);
401 		processClause(clause);
402 		clause.clear();
403 		clause.push(b).push(c).push(-x);
404 		processClause(clause);
405 		clause.clear();
406 		clause.push(a).push(c).push(-x);
407 		processClause(clause);
408 		clause.clear();
409 		clause.push(a).push(b).push(-x);
410 		processClause(clause);
411 		clause.clear();
412 	}
413 
414 	/**
415 	 * @since 2.2
416 	 */
417 	public void additionalFullAdderConstraints(int xcarry, int xsum, int a,
418 			int b, int c) throws ContradictionException {
419 		IVecInt clause = new VecInt(3);
420 		clause.push(-xcarry).push(-xsum).push(a);
421 		processClause(clause);
422 		clause.push(-xcarry).push(-xsum).push(b);
423 		processClause(clause);
424 		clause.push(-xcarry).push(-xsum).push(c);
425 		processClause(clause);
426 		clause.push(xcarry).push(xsum).push(-a);
427 		processClause(clause);
428 		clause.push(xcarry).push(xsum).push(-b);
429 		processClause(clause);
430 		clause.push(xcarry).push(xsum).push(-c);
431 		processClause(clause);
432 	}
433 
434 	/**
435 	 * @since 2.2
436 	 */
437 	public void halfAdderSum(int x, int a, int b) throws ContradictionException {
438 		xor(x, a, b);
439 	}
440 
441 	/**
442 	 * @since 2.2
443 	 */
444 	public void halfAdderCarry(int x, int a, int b)
445 			throws ContradictionException {
446 		and(x, a, b);
447 	}
448 
449 	/**
450 	 * Translate an optimization function into constraints and provides the
451 	 * binary literals in results. Works only when the value of the objective
452 	 * function is positive.
453 	 * 
454 	 * @since 2.2
455 	 */
456 	public void optimisationFunction(IVecInt literals, IVec<BigInteger> coefs,
457 			IVecInt result) throws ContradictionException {
458 		IVec<IVecInt> buckets = new Vec<IVecInt>();
459 		IVecInt bucket;
460 		// filling the buckets
461 		for (int i = 0; i < literals.size(); i++) {
462 			int p = literals.get(i);
463 			BigInteger a = coefs.get(i);
464 			for (int j = 0; j < a.bitLength(); j++) {
465 				bucket = createIfNull(buckets, j);
466 				if (a.testBit(j)) {
467 					bucket.push(p);
468 				}
469 			}
470 		}
471 		// creating the adder
472 		int x, y, z;
473 		int sum, carry;
474 		for (int i = 0; i < buckets.size(); i++) {
475 			bucket = buckets.get(i);
476 			while (bucket.size() >= 3) {
477 				x = bucket.get(0);
478 				y = bucket.get(1);
479 				z = bucket.get(2);
480 				bucket.remove(x);
481 				bucket.remove(y);
482 				bucket.remove(z);
483 				sum = nextFreeVarId(true);
484 				carry = nextFreeVarId(true);
485 				fullAdderSum(sum, x, y, z);
486 				fullAdderCarry(carry, x, y, z);
487 				additionalFullAdderConstraints(carry, sum, x, y, z);
488 				bucket.push(sum);
489 				createIfNull(buckets, i + 1).push(carry);
490 			}
491 			while (bucket.size() == 2) {
492 				x = bucket.get(0);
493 				y = bucket.get(1);
494 				bucket.remove(x);
495 				bucket.remove(y);
496 				sum = nextFreeVarId(true);
497 				carry = nextFreeVarId(true);
498 				halfAdderSum(sum, x, y);
499 				halfAdderCarry(carry, x, y);
500 				bucket.push(sum);
501 				createIfNull(buckets, i + 1).push(carry);
502 			}
503 			assert bucket.size() == 1;
504 			result.push(bucket.last());
505 			bucket.pop();
506 			assert bucket.isEmpty();
507 		}
508 	}
509 
510 	/**
511 	 * Create a new bucket if it does not exists. Works only because the buckets
512 	 * are explored in increasing order.
513 	 * 
514 	 * @param buckets
515 	 * @param i
516 	 * @return
517 	 */
518 	private IVecInt createIfNull(IVec<IVecInt> buckets, int i) {
519 		IVecInt bucket = buckets.get(i);
520 		if (bucket == null) {
521 			bucket = new VecInt();
522 			buckets.push(bucket);
523 			assert buckets.get(i) == bucket;
524 		}
525 		return bucket;
526 
527 	}
528 }