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.minisat.constraints.card;
29  
30  import java.io.Serializable;
31  
32  import org.sat4j.minisat.constraints.cnf.Lits;
33  import org.sat4j.minisat.constraints.cnf.UnitClauses;
34  import org.sat4j.minisat.core.Constr;
35  import org.sat4j.minisat.core.ILits;
36  import org.sat4j.minisat.core.Undoable;
37  import org.sat4j.minisat.core.UnitPropagationListener;
38  import org.sat4j.specs.ContradictionException;
39  import org.sat4j.specs.IVecInt;
40  
41  public class MinWatchCard implements Constr, Undoable, Serializable {
42  
43  	private static final long serialVersionUID = 1L;
44  
45  	public static final boolean ATLEAST = true;
46  
47  	public static final boolean ATMOST = false;
48  
49  	/**
50  	 * degree of the cardinality constraint
51  	 */
52  	protected int degree;
53  
54  	/**
55  	 * literals involved in the constraint
56  	 */
57  	private final int[] lits;
58  
59  	/**
60  	 * contains the sign of the constraint : ATLEAT or ATMOST
61  	 */
62  	private boolean moreThan;
63  
64  	/**
65  	 * contains the sum of the coefficients of the watched literals
66  	 */
67  	protected int watchCumul;
68  
69  	/**
70  	 * Vocabulary of the constraint
71  	 */
72  	private final ILits voc;
73  
74  	/**
75  	 * Constructs and normalizes a cardinality constraint. used by
76  	 * minWatchCardNew in the non-normalized case.
77  	 * 
78  	 * @param voc
79  	 *            vocabulary used by the constraint
80  	 * @param ps
81  	 *            literals involved in the constraint
82  	 * @param moreThan
83  	 *            should be ATLEAST or ATMOST;
84  	 * @param degree
85  	 *            degree of the constraint
86  	 */
87  	public MinWatchCard(ILits voc, IVecInt ps, boolean moreThan, int degree) {
88  		// On met en place les valeurs
89  		this.voc = voc;
90  		this.degree = degree;
91  		this.moreThan = moreThan;
92  
93  		// On simplifie ps
94  		int[] index = new int[voc.nVars() * 2 + 2];
95  		// Fresh array should have all elements set to 0
96  
97  		// On repertorie les litt?raux utiles
98  		for (int i = 0; i < ps.size(); i++) {
99  			int p = ps.get(i);
100 			if (index[p ^ 1] == 0) {
101 				index[p]++;
102 			} else {
103 				index[p ^ 1]--;
104 			}
105 		}
106 		// On supprime les litt?raux inutiles
107 		int ind = 0;
108 		while (ind < ps.size()) {
109 			if (index[ps.get(ind)] > 0) {
110 				index[ps.get(ind)]--;
111 				ind++;
112 			} else {
113 				// ??
114 				if ((ps.get(ind) & 1) != 0)
115 					this.degree--;
116 				ps.delete(ind);
117 			}
118 		}
119 
120 		// On copie les litt?raux de la contrainte
121 		lits = new int[ps.size()];
122 		ps.moveTo(lits);
123 
124 		// On normalise la contrainte au sens de Barth
125 		normalize();
126 
127 	}
128 
129 	/**
130 	 * Constructs and normalizes a cardinality constraint. used by
131 	 * MinWatchCardPB.normalizedMinWatchCardNew() in the normalized case. <br />
132 	 * <strong>Should not be used if parameters are not already
133 	 * normalized</strong><br />
134 	 * This constraint is always an ATLEAST constraint.
135 	 * 
136 	 * @param voc
137 	 *            vocabulary used by the constraint
138 	 * @param ps
139 	 *            literals involved in the constraint
140 	 * @param degree
141 	 *            degree of the constraint
142 	 */
143 	protected MinWatchCard(ILits voc, IVecInt ps, int degree) {
144 		// On met en place les valeurs
145 		this.voc = voc;
146 		this.degree = degree;
147 		this.moreThan = ATLEAST;
148 
149 		// On copie les litt?raux de la contrainte
150 		lits = new int[ps.size()];
151 		ps.moveTo(lits);
152 
153 	}
154 
155 	/**
156 	 * computes the reason for a literal
157 	 * 
158 	 * @param p
159 	 *            falsified literal (or Lit.UNDEFINED)
160 	 * @param outReason
161 	 *            the reason to be computed. Vector of literals.
162 	 * @see Constr#calcReason(int p, IVecInt outReason)
163 	 */
164 	public void calcReason(int p, IVecInt outReason) {
165 		for (int i = 0; i < lits.length; i++) {
166 			if (voc.isFalsified(lits[i])) {
167 				outReason.push(lits[i] ^ 1);
168 			}
169 		}
170 	}
171 
172 	/**
173 	 * Returns the activity of the constraint
174 	 * 
175 	 * @return activity value of the constraint
176 	 * @see Constr#getActivity()
177 	 */
178 	public double getActivity() {
179 		// TODO getActivity
180 		return 0;
181 	}
182 
183 	/**
184 	 * Increments activity of the constraint
185 	 * 
186 	 * @param claInc
187 	 *            value to be added to the activity of the constraint
188 	 * @see Constr#incActivity(double claInc)
189 	 */
190 	public void incActivity(double claInc) {
191 		// TODO incActivity
192 	}
193 
194 	/**
195 	 * Returns wether the constraint is learnt or not.
196 	 * 
197 	 * @return false : a MinWatchCard cannot be learnt.
198 	 * @see Constr#learnt()
199 	 */
200 	public boolean learnt() {
201 		return false;
202 	}
203 
204 	/**
205 	 * Simplifies the constraint w.r.t. the assignments of the literals
206 	 * 
207 	 * @param voc
208 	 *            vocabulary used
209 	 * @param ps
210 	 *            literals involved
211 	 * @return value to be added to the degree. This value is less than or equal
212 	 *         to 0.
213 	 */
214 	protected static int linearisation(ILits voc, IVecInt ps) {
215 		// Stockage de l'influence des modifications
216 		int modif = 0;
217 
218 		for (int i = 0; i < ps.size();) {
219 			// on verifie si le litteral est affecte
220 			if (voc.isUnassigned(ps.get(i))) {
221 				i++;
222 			} else {
223 				// Si le litteral est satisfait,
224 				// ?a revient ? baisser le degr?
225 				if (voc.isSatisfied(ps.get(i))) {
226 					modif--;
227 				}
228 				// dans tous les cas, s'il est assign?,
229 				// on enleve le ieme litteral
230 				ps.set(i, ps.last());
231 				ps.pop();
232 			}
233 		}
234 
235 		// DLB: inutile?
236 		// ps.shrinkTo(nbElement);
237 		assert modif <= 0;
238 
239 		return modif;
240 	}
241 
242 	/**
243 	 * Returns if the constraint is the reason for a unit propagation.
244 	 * 
245 	 * @return true
246 	 * @see Constr#locked()
247 	 */
248 	public boolean locked() {
249 		// TODO locked
250 		return true;
251 	}
252 
253 	/**
254 	 * Constructs a cardinality constraint with a minimal set of watched
255 	 * literals Permet la cr?ation de contrainte de cardinalit? ? observation
256 	 * minimale
257 	 * 
258 	 * @param s
259 	 *            tool for propagation
260 	 * @param voc
261 	 *            vocalulary used by the constraint
262 	 * @param ps
263 	 *            literals involved in the constraint
264 	 * @param moreThan
265 	 *            sign of the constraint. Should be ATLEAST or ATMOST.
266 	 * @param degree
267 	 *            degree of the constraint
268 	 * @return a new cardinality constraint, null if it is a tautology
269 	 * @throws ContradictionException
270 	 */
271 	public static Constr minWatchCardNew(UnitPropagationListener s, ILits voc,
272 			IVecInt ps, boolean moreThan, int degree)
273 			throws ContradictionException {
274 
275 		int mydegree = degree + linearisation(voc, ps);
276 
277 		if (ps.size() < mydegree) {
278 			throw new ContradictionException();
279 		} else if (ps.size() == mydegree) {
280 			for (int i = 0; i < ps.size(); i++)
281 				if (!s.enqueue(ps.get(i))) {
282 					throw new ContradictionException();
283 				}
284 			return new UnitClauses(ps);
285 		}
286 
287 		// La contrainte est maintenant cr??e
288 		MinWatchCard retour = new MinWatchCard(voc, ps, moreThan, mydegree);
289 
290 		if (retour.degree <= 0)
291 			return null;
292 
293 		retour.computeWatches();
294 
295 		retour.computePropagation(s);
296 
297 		return retour;
298 	}
299 
300 	/**
301 	 * normalize the constraint (cf. P.Barth normalization)
302 	 */
303 	public final void normalize() {
304 		// Gestion du signe
305 		if (!moreThan) {
306 			// On multiplie le degr? par -1
307 			this.degree = 0 - this.degree;
308 			// On r?vise chaque litt?ral
309 			for (int indLit = 0; indLit < lits.length; indLit++) {
310 				lits[indLit] = lits[indLit] ^ 1;
311 				this.degree++;
312 			}
313 			this.moreThan = true;
314 		}
315 	}
316 
317 	/**
318 	 * propagates the value of a falsified literal
319 	 * 
320 	 * @param s
321 	 *            tool for literal propagation
322 	 * @param p
323 	 *            falsified literal
324 	 * @return false if an inconistency is detected, else true
325 	 */
326 	public boolean propagate(UnitPropagationListener s, int p) {
327 
328 		// Si la contrainte est responsable de propagation unitaire
329 		if (watchCumul == degree) {
330 			voc.watch(p, this);
331 			return false;
332 		}
333 
334 		// Recherche du litt?ral falsifi?
335 		int indFalsified = 0;
336 		while ((lits[indFalsified] ^ 1) != p)
337 			indFalsified++;
338 		assert watchCumul > degree;
339 
340 		// Recherche du litt?ral swap
341 		int indSwap = degree + 1;
342 		while (indSwap < lits.length && voc.isFalsified(lits[indSwap]))
343 			indSwap++;
344 
345 		// Mise ? jour de la contrainte
346 		if (indSwap == lits.length) {
347 			// Si aucun litt?ral n'a ?t? trouv?
348 			voc.watch(p, this);
349 			// La limite est atteinte
350 			watchCumul--;
351 			assert watchCumul == degree;
352 			voc.undos(p).push(this);
353 
354 			// On met en queue les litt?raux impliqu?s
355 			for (int i = 0; i <= degree; i++)
356 				if ((p != (lits[i] ^ 1)) && !s.enqueue(lits[i], this))
357 					return false;
358 
359 			return true;
360 		}
361 		// Si un litt?ral a ?t? trouv? on les ?change
362 		int tmpInt = lits[indSwap];
363 		lits[indSwap] = lits[indFalsified];
364 		lits[indFalsified] = tmpInt;
365 
366 		// On observe le nouveau litt?ral
367 		voc.watch(tmpInt ^ 1, this);
368 
369 		return true;
370 	}
371 
372 	/**
373 	 * Removes a constraint from the solver
374 	 * 
375 	 * @since 2.1
376 	 */
377 	public void remove(UnitPropagationListener upl) {
378 		for (int i = 0; i < Math.min(degree + 1, lits.length); i++) {
379 			voc.watches(lits[i] ^ 1).remove(this);
380 		}
381 	}
382 
383 	/**
384 	 * Rescales the activity value of the constraint
385 	 * 
386 	 * @param d
387 	 *            rescale factor
388 	 */
389 	public void rescaleBy(double d) {
390 		// TODO rescaleBy
391 	}
392 
393 	/**
394 	 * simplifies the constraint
395 	 * 
396 	 * @return true if the constraint is satisfied, else false
397 	 */
398 	public boolean simplify() {
399 		// Calcul de la valeur actuelle
400 		for (int i = 0, count = 0; i < lits.length; i++)
401 			if (voc.isSatisfied(lits[i]) && (++count == degree))
402 				return true;
403 
404 		return false;
405 	}
406 
407 	/**
408 	 * Returns a string representation of the constraint.
409 	 * 
410 	 * @return representation of the constraint.
411 	 */
412 	@Override
413 	public String toString() {
414 		StringBuffer stb = new StringBuffer();
415 		stb.append("Card (" + lits.length + ") : ");
416 		if (lits.length > 0) {
417 			// if (voc.isUnassigned(lits[0])) {
418 			stb.append(Lits.toString(this.lits[0]));
419 			stb.append("[");
420 			stb.append(voc.valueToString(lits[0]));
421 			stb.append("@");
422 			stb.append(voc.getLevel(lits[0]));
423 			stb.append("]");
424 			stb.append(" "); //$NON-NLS-1$
425 			// }
426 			for (int i = 1; i < lits.length; i++) {
427 				// if (voc.isUnassigned(lits[i])) {
428 				stb.append(" + "); //$NON-NLS-1$
429 				stb.append(Lits.toString(this.lits[i]));
430 				stb.append("[");
431 				stb.append(voc.valueToString(lits[i]));
432 				stb.append("@");
433 				stb.append(voc.getLevel(lits[i]));
434 				stb.append("]");
435 				stb.append(" "); //$NON-NLS-1$
436 				// }
437 			}
438 			stb.append(">= "); //$NON-NLS-1$
439 			stb.append(this.degree);
440 		}
441 		return stb.toString();
442 	}
443 
444 	/**
445 	 * Updates information on the constraint in case of a backtrack
446 	 * 
447 	 * @param p
448 	 *            unassigned literal
449 	 */
450 	public void undo(int p) {
451 		// Le litt?ral observ? et falsifi? devient non assign?
452 		watchCumul++;
453 	}
454 
455 	public void setLearnt() {
456 		throw new UnsupportedOperationException();
457 	}
458 
459 	public void register() {
460 		throw new UnsupportedOperationException();
461 	}
462 
463 	public int size() {
464 		return lits.length;
465 	}
466 
467 	public int get(int i) {
468 		return lits[i];
469 	}
470 
471 	public void assertConstraint(UnitPropagationListener s) {
472 		throw new UnsupportedOperationException();
473 	}
474 
475 	protected void computeWatches() {
476 		int indSwap = lits.length;
477 		int tmpInt;
478 		for (int i = 0; i <= degree && i < indSwap; i++) {
479 			while (voc.isFalsified(lits[i]) && --indSwap > i) {
480 				tmpInt = lits[i];
481 				lits[i] = lits[indSwap];
482 				lits[indSwap] = tmpInt;
483 			}
484 
485 			// Si le litteral est observable
486 			if (!voc.isFalsified(lits[i])) {
487 				watchCumul++;
488 				voc.watch(lits[i] ^ 1, this);
489 			}
490 		}
491 		if (learnt()) {
492 			// chercher tous les litteraux a regarder
493 			// par ordre de niveau decroissant
494 			int free = 1;
495 			while ((watchCumul <= degree) && (free > 0)) {
496 				free = 0;
497 				// regarder le litteral falsifie au plus bas niveau
498 				int maxlevel = -1, maxi = -1;
499 				for (int i = watchCumul; i < lits.length; i++) {
500 					if (voc.isFalsified(lits[i])) {
501 						free++;
502 						int level = voc.getLevel(lits[i]);
503 						if (level > maxlevel) {
504 							maxi = i;
505 							maxlevel = level;
506 						}
507 					}
508 				}
509 				if (free > 0) {
510 					assert maxi >= 0;
511 					voc.watch(lits[maxi] ^ 1, this);
512 					tmpInt = lits[maxi];
513 					lits[maxi] = lits[watchCumul];
514 					lits[watchCumul] = tmpInt;
515 					watchCumul++;
516 					free--;
517 					assert free >= 0;
518 				}
519 			}
520 			assert lits.length == 1 || watchCumul > 1;
521 		}
522 
523 	}
524 
525 	protected MinWatchCard computePropagation(UnitPropagationListener s)
526 			throws ContradictionException {
527 
528 		// Si on a des litteraux impliques
529 		if (watchCumul == degree) {
530 			for (int i = 0; i < lits.length; i++)
531 				if (!s.enqueue(lits[i])) {
532 					throw new ContradictionException();
533 				}
534 			return null;
535 		}
536 
537 		// Si on n'observe pas suffisamment
538 		if (watchCumul < degree) {
539 			throw new ContradictionException();
540 		}
541 		return this;
542 	}
543 
544 	public int[] getLits() {
545 		int[] tmp = new int[size()];
546 		System.arraycopy(lits, 0, tmp, 0, size());
547 		return tmp;
548 	}
549 
550 	public ILits getVocabulary() {
551 		return voc;
552 	}
553 
554 	@Override
555 	public boolean equals(Object card) {
556 		if (card == null) {
557 			return false;
558 		}
559 		try {
560 			MinWatchCard mcard = (MinWatchCard) card;
561 			if (mcard.degree != degree)
562 				return false;
563 			if (lits.length != mcard.lits.length)
564 				return false;
565 			boolean ok;
566 			for (int lit : lits) {
567 				ok = false;
568 				for (int lit2 : mcard.lits)
569 					if (lit == lit2) {
570 						ok = true;
571 						break;
572 					}
573 				if (!ok)
574 					return false;
575 			}
576 			return true;
577 		} catch (ClassCastException e) {
578 			return false;
579 		}
580 	}
581 
582 	@Override
583 	public int hashCode() {
584 		long sum = 0;
585 		for (int p : lits) {
586 			sum += p;
587 		}
588 		sum += degree;
589 		return (int) sum / (lits.length + 1);
590 	}
591 
592 	/**
593 	 * @since 2.1
594 	 */
595 	public void forwardActivity(double claInc) {
596 		// do nothing
597 	}
598 
599 	public boolean canBePropagatedMultipleTimes() {
600 		return true;
601 	}
602 }