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