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