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 pseudo boolean algorithms described in:
20   * A fast pseudo-Boolean constraint solver Chai, D.; Kuehlmann, A.
21   * Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on
22   * Volume 24, Issue 3, March 2005 Page(s): 305 - 317
23   * 
24   * and 
25   * Heidi E. Dixon, 2004. Automating Pseudo-Boolean Inference within a DPLL 
26   * Framework. Ph.D. Dissertation, University of Oregon.
27   *******************************************************************************/
28  package org.sat4j.pb.constraints.pb;
29  
30  import java.math.BigInteger;
31  
32  import org.sat4j.minisat.core.ILits;
33  import org.sat4j.minisat.core.UnitPropagationListener;
34  import org.sat4j.specs.ContradictionException;
35  
36  /**
37   * Data structure for pseudo-boolean constraint with watched literals.
38   * 
39   * All literals are watched. The sum of the literals satisfied or unvalued is
40   * always memorized, to detect conflict.
41   * 
42   * @author anne
43   * 
44   */
45  public class MinWatchPb extends WatchPb {
46  
47  	private static final long serialVersionUID = 1L;
48  
49  	/**
50  	 * sum of the coefficients of the literals satisfied or unvalued
51  	 */
52  	protected BigInteger watchCumul = BigInteger.ZERO;
53  
54  	/**
55  	 * is the literal of index i watching the constraint ?
56  	 */
57  	protected boolean[] watched;
58  
59  	/**
60  	 * indexes of literals watching the constraint
61  	 */
62  	protected int[] watching;
63  
64  	/**
65  	 * number of literals watching the constraint.
66  	 * 
67  	 * This is the real size of the array watching
68  	 */
69  	protected int watchingCount = 0;
70  
71  	/**
72  	 * Basic constructor for pb constraint a0.x0 + a1.x1 + ... + an.xn >= k
73  	 * 
74  	 * This constructor is called for learnt pseudo boolean constraints.
75  	 * 
76  	 * @param voc
77  	 *            all the possible variables (vocabulary)
78  	 * @param mpb
79  	 *            a mutable PB constraint
80  	 */
81  	protected MinWatchPb(ILits voc, IDataStructurePB mpb) {
82  
83  		super(mpb);
84  		this.voc = voc;
85  
86  		watching = new int[this.coefs.length];
87  		watched = new boolean[this.coefs.length];
88  		activity = 0;
89  		watchCumul = BigInteger.ZERO;
90  		watchingCount = 0;
91  
92  	}
93  
94  	/**
95  	 * Basic constructor for PB constraint a0.x0 + a1.x1 + ... + an.xn >= k
96  	 * 
97  	 * @param voc
98  	 *            all the possible variables (vocabulary)
99  	 * @param lits
100 	 *            literals of the constraint (x0,x1, ... xn)
101 	 * @param coefs
102 	 *            coefficients of the left side of the constraint (a0, a1, ...
103 	 *            an)
104 	 * @param degree
105 	 *            degree of the constraint (k)
106 	 */
107 	protected MinWatchPb(ILits voc, int[] lits, BigInteger[] coefs, // NOPMD
108 			BigInteger degree) {
109 
110 		super(lits, coefs, degree);
111 		this.voc = voc;
112 
113 		watching = new int[this.coefs.length];
114 		watched = new boolean[this.coefs.length];
115 		activity = 0;
116 		watchCumul = BigInteger.ZERO;
117 		watchingCount = 0;
118 
119 	}
120 
121 	/*
122 	 * This method initialize the watched literals.
123 	 * 
124 	 * This method is only called in the factory methods.
125 	 * 
126 	 * @see org.sat4j.minisat.constraints.WatchPb#computeWatches()
127 	 */
128 	@Override
129 	protected void computeWatches() throws ContradictionException {
130 		assert watchCumul.signum() == 0;
131 		assert watchingCount == 0;
132 		for (int i = 0; i < lits.length
133 				&& watchCumul.subtract(coefs[0]).compareTo(degree) < 0; i++) {
134 			if (!voc.isFalsified(lits[i])) {
135 				voc.watch(lits[i] ^ 1, this);
136 				watching[watchingCount++] = i;
137 				watched[i] = true;
138 				// update the initial value for watchCumul (poss)
139 				watchCumul = watchCumul.add(coefs[i]);
140 			}
141 		}
142 
143 		if (learnt)
144 			watchMoreForLearntConstraint();
145 
146 		if (watchCumul.compareTo(degree) < 0) {
147 			throw new ContradictionException("non satisfiable constraint");
148 		}
149 		assert nbOfWatched() == watchingCount;
150 	}
151 
152 	private void watchMoreForLearntConstraint() {
153 		// looking for literals to be watched,
154 		// ordered by decreasing level
155 		int free = 1;
156 		int maxlevel, maxi, level;
157 
158 		while ((watchCumul.subtract(coefs[0]).compareTo(degree) < 0)
159 				&& (free > 0)) {
160 			free = 0;
161 			// looking for the literal falsified
162 			// at the least (lowest ?) level
163 			maxlevel = -1;
164 			maxi = -1;
165 			for (int i = 0; i < lits.length; i++) {
166 				if (voc.isFalsified(lits[i]) && !watched[i]) {
167 					free++;
168 					level = voc.getLevel(lits[i]);
169 					if (level > maxlevel) {
170 						maxi = i;
171 						maxlevel = level;
172 					}
173 				}
174 			}
175 
176 			if (free > 0) {
177 				assert maxi >= 0;
178 				voc.watch(lits[maxi] ^ 1, this);
179 				watching[watchingCount++] = maxi;
180 				watched[maxi] = true;
181 				// update of the watchCumul value
182 				watchCumul = watchCumul.add(coefs[maxi]);
183 				free--;
184 				assert free >= 0;
185 			}
186 		}
187 		assert lits.length == 1 || watchingCount > 1;
188 	}
189 
190 	/*
191 	 * This method propagates any possible value.
192 	 * 
193 	 * This method is only called in the factory methods.
194 	 * 
195 	 * @see
196 	 * org.sat4j.minisat.constraints.WatchPb#computePropagation(org.sat4j.minisat
197 	 * .UnitPropagationListener)
198 	 */
199 	@Override
200 	protected void computePropagation(UnitPropagationListener s)
201 			throws ContradictionException {
202 		// propagate any possible value
203 		int ind = 0;
204 		while (ind < lits.length
205 				&& watchCumul.subtract(coefs[watching[ind]]).compareTo(degree) < 0) {
206 			if (voc.isUnassigned(lits[ind]) && !s.enqueue(lits[ind], this)) {
207 				throw new ContradictionException("non satisfiable constraint");
208 			}
209 			ind++;
210 		}
211 	}
212 
213 	/**
214 	 * build a pseudo boolean constraint. Coefficients are positive integers
215 	 * less than or equal to the degree (this is called a normalized
216 	 * constraint).
217 	 * 
218 	 * @param s
219 	 *            a unit propagation listener
220 	 * @param voc
221 	 *            the vocabulary
222 	 * @param lits
223 	 *            the literals
224 	 * @param coefs
225 	 *            the coefficients
226 	 * @param degree
227 	 *            the degree of the constraint to normalize.
228 	 * @return a new PB constraint or null if a trivial inconsistency is
229 	 *         detected.
230 	 */
231 	public static MinWatchPb normalizedMinWatchPbNew(UnitPropagationListener s,
232 			ILits voc, int[] lits, BigInteger[] coefs, BigInteger degree)
233 			throws ContradictionException {
234 		// Parameters must not be modified
235 		MinWatchPb outclause = new MinWatchPb(voc, lits, coefs, degree);
236 
237 		if (outclause.degree.signum() <= 0) {
238 			return null;
239 		}
240 
241 		outclause.computeWatches();
242 
243 		outclause.computePropagation(s);
244 
245 		return outclause;
246 
247 	}
248 
249 	/**
250 	 * Number of really watched literals. It should return the same value as
251 	 * watchingCount.
252 	 * 
253 	 * This method must only be called for assertions.
254 	 * 
255 	 * @return number of watched literals.
256 	 */
257 	protected int nbOfWatched() {
258 		int retour = 0;
259 		for (int ind = 0; ind < this.watched.length; ind++) {
260 			for (int i = 0; i < watchingCount; i++)
261 				if (watching[i] == ind)
262 					assert watched[ind];
263 			retour += (this.watched[ind]) ? 1 : 0;
264 		}
265 		return retour;
266 	}
267 
268 	/**
269 	 * Propagation of a falsified literal
270 	 * 
271 	 * @param s
272 	 *            the solver
273 	 * @param p
274 	 *            the propagated literal (it must be falsified)
275 	 * @return false iff there is a conflict
276 	 */
277 	public boolean propagate(UnitPropagationListener s, int p) {
278 		assert nbOfWatched() == watchingCount;
279 		assert watchingCount > 1;
280 
281 		// finding the index for p in the array of literals (pIndice)
282 		// and in the array of watching (pIndiceWatching)
283 		int pIndiceWatching = 0;
284 		while (pIndiceWatching < watchingCount
285 				&& (lits[watching[pIndiceWatching]] ^ 1) != p)
286 			pIndiceWatching++;
287 		int pIndice = watching[pIndiceWatching];
288 
289 		assert p == (lits[pIndice] ^ 1);
290 		assert watched[pIndice];
291 
292 		// the greatest coefficient of the watched literals is necessary
293 		// (pIndice excluded)
294 		BigInteger maxCoef = maximalCoefficient(pIndice);
295 
296 		// update watching and watched w.r.t. to the propogation of p
297 		// new literals will be watched, maxCoef could be changed
298 		maxCoef = updateWatched(maxCoef, pIndice);
299 
300 		BigInteger upWatchCumul = watchCumul.subtract(coefs[pIndice]);
301 		assert nbOfWatched() == watchingCount;
302 
303 		// if a conflict has been detected, return false
304 		if (upWatchCumul.compareTo(degree) < 0) {
305 			// conflit
306 			voc.watch(p, this);
307 			assert watched[pIndice];
308 			assert !isSatisfiable();
309 			return false;
310 		} else if (upWatchCumul.compareTo(degree.add(maxCoef)) < 0) {
311 			// some literals must be assigned to true and then propagated
312 			assert watchingCount != 0;
313 			BigInteger limit = upWatchCumul.subtract(degree);
314 			for (int i = 0; i < watchingCount; i++) {
315 				if (limit.compareTo(coefs[watching[i]]) < 0
316 						&& i != pIndiceWatching
317 						&& !voc.isSatisfied(lits[watching[i]])
318 						&& !s.enqueue(lits[watching[i]], this)) {
319 					voc.watch(p, this);
320 					assert !isSatisfiable();
321 					return false;
322 				}
323 			}
324 			// if the constraint is added to the undos of p (by propagation),
325 			// then p should be preserved.
326 			voc.undos(p).push(this);
327 		}
328 
329 		// else p is no more watched
330 		watched[pIndice] = false;
331 		watchCumul = upWatchCumul;
332 		watching[pIndiceWatching] = watching[--watchingCount];
333 
334 		assert watchingCount != 0;
335 		assert nbOfWatched() == watchingCount;
336 
337 		return true;
338 	}
339 
340 	/**
341 	 * Remove the constraint from the solver
342 	 */
343 	public void remove(UnitPropagationListener upl) {
344 		for (int i = 0; i < watchingCount; i++) {
345 			voc.watches(lits[watching[i]] ^ 1).remove(this);
346 			this.watched[this.watching[i]] = false;
347 		}
348 		watchingCount = 0;
349 		assert nbOfWatched() == watchingCount;
350 	}
351 
352 	/**
353 	 * this method is called during backtrack
354 	 * 
355 	 * @param p
356 	 *            un unassigned literal
357 	 */
358 	public void undo(int p) {
359 		voc.watch(p, this);
360 		int pIndice = 0;
361 		while ((lits[pIndice] ^ 1) != p)
362 			pIndice++;
363 
364 		assert pIndice < lits.length;
365 
366 		watchCumul = watchCumul.add(coefs[pIndice]);
367 
368 		assert watchingCount == nbOfWatched();
369 
370 		watched[pIndice] = true;
371 		watching[watchingCount++] = pIndice;
372 
373 		assert watchingCount == nbOfWatched();
374 	}
375 
376 	/**
377 	 * build a pseudo boolean constraint from a specific data structure. For
378 	 * learnt constraints.
379 	 * 
380 	 * @param s
381 	 *            a unit propagation listener (usually the solver)
382 	 * @param mpb
383 	 *            data structure which contains literals of the constraint,
384 	 *            coefficients (a0, a1, ... an), and the degree of the
385 	 *            constraint (k). The constraint is a "more than" constraint.
386 	 * @return a new PB constraint or null if a trivial inconsistency is
387 	 *         detected.
388 	 */
389 	public static WatchPb normalizedWatchPbNew(ILits voc, IDataStructurePB mpb) {
390 		return new MinWatchPb(voc, mpb);
391 	}
392 
393 	/**
394 	 * the maximal coefficient for the watched literals
395 	 * 
396 	 * @param pIndice
397 	 *            propagated literal : its coefficient is excluded from the
398 	 *            search of the maximal coefficient
399 	 * @return the maximal coefficient for the watched literals
400 	 */
401 	protected BigInteger maximalCoefficient(int pIndice) {
402 		BigInteger maxCoef = BigInteger.ZERO;
403 		for (int i = 0; i < watchingCount; i++)
404 			if (coefs[watching[i]].compareTo(maxCoef) > 0
405 					&& watching[i] != pIndice) {
406 				maxCoef = coefs[watching[i]];
407 			}
408 
409 		assert learnt || maxCoef.signum() != 0;
410 		// DLB assert maxCoef!=0;
411 		return maxCoef;
412 	}
413 
414 	/**
415 	 * update arrays watched and watching w.r.t. the propagation of a literal.
416 	 * 
417 	 * return the maximal coefficient of the watched literals (could have been
418 	 * changed).
419 	 * 
420 	 * @param mc
421 	 *            the current maximal coefficient of the watched literals
422 	 * @param pIndice
423 	 *            the literal propagated (falsified)
424 	 * @return the new maximal coefficient of the watched literals
425 	 */
426 	protected BigInteger updateWatched(BigInteger mc, int pIndice) {
427 		BigInteger maxCoef = mc;
428 		// if not all the literals are watched
429 		if (watchingCount < size()) {
430 			// the watchCumul sum will have to be updated
431 			BigInteger upWatchCumul = watchCumul.subtract(coefs[pIndice]);
432 
433 			// we must obtain upWatchCumul such that
434 			// upWatchCumul = degree + maxCoef
435 			BigInteger degreePlusMaxCoef = degree.add(maxCoef); // dvh
436 			for (int ind = 0; ind < lits.length; ind++) {
437 				if (upWatchCumul.compareTo(degreePlusMaxCoef) >= 0) {
438 					// nothing more to watch
439 					// note: logic negated to old version // dvh
440 					break;
441 				}
442 				// while upWatchCumul does not contain enough
443 				if (!voc.isFalsified(lits[ind]) && !watched[ind]) {
444 					// watch one more
445 					upWatchCumul = upWatchCumul.add(coefs[ind]);
446 					// update arrays watched and watching
447 					watched[ind] = true;
448 					assert watchingCount < size();
449 					watching[watchingCount++] = ind;
450 					voc.watch(lits[ind] ^ 1, this);
451 					// this new watched literal could change the maximal
452 					// coefficient
453 					if (coefs[ind].compareTo(maxCoef) > 0) {
454 						maxCoef = coefs[ind];
455 						degreePlusMaxCoef = degree.add(maxCoef); // update
456 						// that one
457 						// too
458 					}
459 				}
460 			}
461 			// update watchCumul
462 			watchCumul = upWatchCumul.add(coefs[pIndice]);
463 		}
464 		return maxCoef;
465 	}
466 
467 }