View Javadoc

1   /*******************************************************************************
2    * SAT4J: a SATisfiability library for Java Copyright (C) 2004, 2012 Artois University and CNRS
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   * Contributors:
28   *   CRIL - initial API and implementation
29   *******************************************************************************/
30  
31  package org.sat4j.pb.tools;
32  
33  import java.math.BigInteger;
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.Map;
39  import java.util.Set;
40  import java.util.TreeSet;
41  
42  import org.sat4j.core.Vec;
43  import org.sat4j.core.VecInt;
44  import org.sat4j.pb.IPBSolver;
45  import org.sat4j.pb.ObjectiveFunction;
46  import org.sat4j.specs.ContradictionException;
47  import org.sat4j.specs.IConstr;
48  import org.sat4j.specs.IVec;
49  import org.sat4j.specs.IVecInt;
50  import org.sat4j.specs.IteratorInt;
51  import org.sat4j.specs.TimeoutException;
52  import org.sat4j.tools.Backbone;
53  import org.sat4j.tools.GateTranslator;
54  
55  /**
56   * Helper class intended to make life easier to people to feed a sat solver
57   * programmatically.
58   * 
59   * @author daniel
60   * 
61   * @param <T>
62   *            The class of the objects to map into boolean variables.
63   * @param <C>
64   *            The class of the object to map to each constraint.
65   */
66  public class DependencyHelper<T, C> {
67  
68  	public static final INegator NO_NEGATION = new INegator() {
69  
70  		public boolean isNegated(Object thing) {
71  			return false;
72  		}
73  
74  		public Object unNegate(Object thing) {
75  			return thing;
76  		}
77  	};
78  
79  	public static final INegator BASIC_NEGATION = new INegator() {
80  
81  		public boolean isNegated(Object thing) {
82  			return thing instanceof Negation;
83  		}
84  
85  		public Object unNegate(Object thing) {
86  			return ((Negation) thing).getThing();
87  		}
88  	};
89  
90  	private static final class Negation {
91  		private final Object thing;
92  
93  		Negation(Object thing) {
94  			this.thing = thing;
95  		}
96  
97  		Object getThing() {
98  			return this.thing;
99  		}
100 
101 		@Override
102 		public String toString() {
103 			return "-" + thing;
104 		}
105 	}
106 
107 	/**
108 	 * 
109 	 */
110 	private static final long serialVersionUID = 1L;
111 
112 	private final Map<T, Integer> mapToDimacs = new HashMap<T, Integer>();
113 	private final Map<Integer, T> mapToDomain = new HashMap<Integer, T>();
114 	final Map<IConstr, C> descs = new HashMap<IConstr, C>();
115 
116 	private final XplainPB xplain;
117 	private final GateTranslator gator;
118 	final IPBSolver solver;
119 	private INegator negator = BASIC_NEGATION;
120 
121 	private ObjectiveFunction objFunction;
122 	private IVecInt objLiterals;
123 	private IVec<BigInteger> objCoefs;
124 
125 	private final boolean explanationEnabled;
126 	private final boolean canonicalOptFunction;
127 
128 	/**
129 	 * 
130 	 * @param solver
131 	 *            the solver to be used to solve the problem.
132 	 */
133 	public DependencyHelper(IPBSolver solver) {
134 		this(solver, true);
135 	}
136 
137 	/**
138 	 * 
139 	 * @param solver
140 	 *            the solver to be used to solve the problem.
141 	 * @param explanationEnabled
142 	 *            if true, will add one new variable per constraint to allow the
143 	 *            solver to compute an explanation in case of failure. Default
144 	 *            is true. Usually, this is set to false when one wants to check
145 	 *            the encoding produced by the helper.
146 	 */
147 	public DependencyHelper(IPBSolver solver, boolean explanationEnabled) {
148 		this(solver, explanationEnabled, true);
149 	}
150 
151 	/**
152 	 * 
153 	 * @param solver
154 	 *            the solver to be used to solve the problem.
155 	 * @param explanationEnabled
156 	 *            if true, will add one new variable per constraint to allow the
157 	 *            solver to compute an explanation in case of failure. Default
158 	 *            is true. Usually, this is set to false when one wants to check
159 	 *            the encoding produced by the helper.
160 	 * @param canonicalOptFunctionEnabled
161 	 *            when set to true, the objective function sum up all the
162 	 *            coefficients for a given literal. The default is true. It is
163 	 *            useful to set it to false when checking the encoding produced
164 	 *            by the helper.
165 	 * @since 2.2
166 	 */
167 	public DependencyHelper(IPBSolver solver, boolean explanationEnabled,
168 			boolean canonicalOptFunctionEnabled) {
169 		if (explanationEnabled) {
170 			this.xplain = new XplainPB(solver);
171 			this.solver = this.xplain;
172 		} else {
173 			this.xplain = null;
174 			this.solver = solver;
175 		}
176 		this.gator = new GateTranslator(this.solver);
177 		this.explanationEnabled = explanationEnabled;
178 		this.canonicalOptFunction = canonicalOptFunctionEnabled;
179 	}
180 
181 	public void setNegator(INegator negator) {
182 		this.negator = negator;
183 	}
184 
185 	/**
186 	 * Translate a domain object into a dimacs variable.
187 	 * 
188 	 * @param thing
189 	 *            a domain object
190 	 * @return the dimacs variable (an integer) representing that domain object.
191 	 */
192 	protected int getIntValue(T thing) {
193 		return getIntValue(thing, true);
194 	}
195 
196 	/**
197 	 * Translate a domain object into a dimacs variable.
198 	 * 
199 	 * @param thing
200 	 *            a domain object
201 	 * @param create
202 	 *            to allow or not the solver to create a new id if the object in
203 	 *            unknown. If set to false, the method will throw an
204 	 *            IllegalArgumentException if the object is unknown.
205 	 * @return the dimacs variable (an integer) representing that domain object.
206 	 */
207 	protected int getIntValue(T thing, boolean create) {
208 		T myThing;
209 		boolean negated = this.negator.isNegated(thing);
210 		if (negated) {
211 			myThing = (T) this.negator.unNegate(thing);
212 		} else {
213 			myThing = thing;
214 		}
215 		Integer intValue = this.mapToDimacs.get(myThing);
216 		if (intValue == null) {
217 			if (create) {
218 				intValue = this.solver.nextFreeVarId(true);
219 				this.mapToDomain.put(intValue, myThing);
220 				this.mapToDimacs.put(myThing, intValue);
221 			} else {
222 				throw new IllegalArgumentException("" + myThing
223 						+ " is unknown in the solver!");
224 			}
225 		}
226 		if (negated) {
227 			return -intValue;
228 		}
229 		return intValue;
230 	}
231 
232 	/**
233 	 * Retrieve the solution found.
234 	 * 
235 	 * Note that this method returns a Sat4j specific data structure (IVec).
236 	 * People willing to retrieve a more common data structure should use
237 	 * {@link #getASolution()} instead.
238 	 * 
239 	 * THAT METHOD IS EXPECTED TO BE CALLED IF hasASolution() RETURNS TRUE.
240 	 * 
241 	 * @return the domain object that must be satisfied to satisfy the
242 	 *         constraints entered in the solver.
243 	 * @see {@link #hasASolution()}
244 	 * @see {@link #getASolution()}
245 	 */
246 	public IVec<T> getSolution() {
247 		int[] model = this.solver.model();
248 		IVec<T> toInstall = new Vec<T>();
249 		if (model != null) {
250 			for (int i : model) {
251 				if (i > 0) {
252 					toInstall.push(this.mapToDomain.get(i));
253 				}
254 			}
255 		}
256 		return toInstall;
257 	}
258 
259 	/**
260 	 * Retrieve a collection of objects satisfying the constraints.
261 	 * 
262 	 * It behaves basically the same as {@link #getSolution()} but returns a
263 	 * common Collection instead of a Sat4j specific IVec.
264 	 * 
265 	 * THAT METHOD IS EXPECTED TO BE CALLED IF hasASolution() RETURNS TRUE.
266 	 * 
267 	 * @return the domain object that must be satisfied to satisfy the
268 	 *         constraints entered in the solver.
269 	 * @see {@link #hasASolution()}
270 	 * @see {@link #getSolution()}
271 	 * @since 2.3.1
272 	 */
273 	public Collection<T> getASolution() {
274 		int[] model = this.solver.model();
275 		Collection<T> toInstall = new ArrayList<T>();
276 		if (model != null) {
277 			for (int i : model) {
278 				if (i > 0) {
279 					toInstall.add(this.mapToDomain.get(i));
280 				}
281 			}
282 		}
283 		return toInstall;
284 	}
285 
286 	public BigInteger getSolutionCost() {
287 		return this.objFunction.calculateDegree(this.solver);
288 	}
289 
290 	/**
291 	 * Retrieve the boolean value associated with a domain object in the
292 	 * solution found by the solver. THAT METHOD IS EXPECTED TO BE CALLED IF
293 	 * hasASolution() RETURNS TRUE.
294 	 * 
295 	 * @param t
296 	 *            a domain object
297 	 * @return true iff the domain object has been set to true in the current
298 	 *         solution.
299 	 * @throws IllegalArgumentException
300 	 *             If the argument of the method is unknown to the solver.
301 	 */
302 	public boolean getBooleanValueFor(T t) {
303 		int dimacsValue = getIntValue(t, false);
304 		if (dimacsValue > 0) {
305 			return this.solver.model(dimacsValue);
306 		} else {
307 			return !this.solver.model(-dimacsValue);
308 		}
309 	}
310 
311 	/**
312 	 * 
313 	 * @return true if the set of constraints entered inside the solver can be
314 	 *         satisfied.
315 	 * @throws TimeoutException
316 	 */
317 	public boolean hasASolution() throws TimeoutException {
318 		return this.solver.isSatisfiable();
319 	}
320 
321 	/**
322 	 * 
323 	 * @return true if the set of constraints entered inside the solver can be
324 	 *         satisfied.
325 	 * @throws TimeoutException
326 	 */
327 	public boolean hasASolution(IVec<T> assumps) throws TimeoutException {
328 		IVecInt assumptions = new VecInt();
329 		for (Iterator<T> it = assumps.iterator(); it.hasNext();) {
330 			assumptions.push(getIntValue(it.next()));
331 		}
332 		return this.solver.isSatisfiable(assumptions);
333 	}
334 
335 	/**
336 	 * 
337 	 * @return true if the set of constraints entered inside the solver can be
338 	 *         satisfied.
339 	 * @throws TimeoutException
340 	 */
341 	public boolean hasASolution(Collection<T> assumps) throws TimeoutException {
342 		IVecInt assumptions = new VecInt();
343 		for (T t : assumps) {
344 			assumptions.push(getIntValue(t));
345 		}
346 		return this.solver.isSatisfiable(assumptions);
347 	}
348 
349 	/**
350 	 * Explain the reason of the inconsistency of the set of constraints.
351 	 * 
352 	 * THAT METHOD IS EXPECTED TO BE CALLED IF hasASolution() RETURNS FALSE.
353 	 * 
354 	 * @return a set of objects used to "name" each constraint entered in the
355 	 *         solver.
356 	 * @throws TimeoutException
357 	 * @see {@link #hasASolution()}
358 	 */
359 	public Set<C> why() throws TimeoutException {
360 		if (!this.explanationEnabled) {
361 			throw new UnsupportedOperationException("Explanation not enabled!");
362 		}
363 		Collection<IConstr> explanation = this.xplain.explain();
364 		Set<C> ezexplain = new TreeSet<C>();
365 		for (IConstr constr : explanation) {
366 			C desc = this.descs.get(constr);
367 			if (desc != null) {
368 				ezexplain.add(desc);
369 			}
370 		}
371 		return ezexplain;
372 	}
373 
374 	/**
375 	 * Explain a domain object has been set to true in a solution.
376 	 * 
377 	 * @return a set of objects used to "name" each constraint entered in the
378 	 *         solver.
379 	 * @throws TimeoutException
380 	 * @see {@link #hasASolution()}
381 	 */
382 	public Set<C> why(T thing) throws TimeoutException {
383 		IVecInt assumps = new VecInt();
384 		assumps.push(-getIntValue(thing));
385 		return why(assumps);
386 	}
387 
388 	/**
389 	 * Explain a domain object has been set to false in a solution.
390 	 * 
391 	 * @return a set of objects used to "name" each constraint entered in the
392 	 *         solver.
393 	 * @throws TimeoutException
394 	 * @see {@link #hasASolution()}
395 	 */
396 	public Set<C> whyNot(T thing) throws TimeoutException {
397 		IVecInt assumps = new VecInt();
398 		assumps.push(getIntValue(thing));
399 		return why(assumps);
400 	}
401 
402 	private Set<C> why(IVecInt assumps) throws TimeoutException {
403 		if (this.xplain.isSatisfiable(assumps)) {
404 			return new TreeSet<C>();
405 		}
406 		return why();
407 	}
408 
409 	/**
410 	 * Add a constraint to set the value of a domain object to true.
411 	 * 
412 	 * @param thing
413 	 *            the domain object
414 	 * @param name
415 	 *            the name of the constraint, to be used in an explanation if
416 	 *            needed.
417 	 * @throws ContradictionException
418 	 *             if the set of constraints appears to be trivially
419 	 *             inconsistent.
420 	 */
421 	public void setTrue(T thing, C name) throws ContradictionException {
422 		IConstr constr = this.gator.gateTrue(getIntValue(thing));
423 		if (constr != null) {
424 			this.descs.put(constr, name);
425 		}
426 	}
427 
428 	/**
429 	 * Add a constraint to set the value of a domain object to false.
430 	 * 
431 	 * @param thing
432 	 *            the domain object
433 	 * @param name
434 	 *            the name of the constraint, to be used in an explanation if
435 	 *            needed.
436 	 * @throws ContradictionException
437 	 *             if the set of constraints appears to be trivially
438 	 *             inconsistent.
439 	 */
440 	public void setFalse(T thing, C name) throws ContradictionException {
441 		IConstr constr = this.gator.gateFalse(getIntValue(thing));
442 		// constraints duplication detection may end up with null constraint
443 		if (constr != null) {
444 			this.descs.put(constr, name);
445 		}
446 
447 	}
448 
449 	/**
450 	 * Create a logical implication of the form lhs -> rhs
451 	 * 
452 	 * @param lhs
453 	 *            some domain objects. They form a conjunction in the left hand
454 	 *            side of the implication.
455 	 * @return the right hand side of the implication.
456 	 */
457 	public ImplicationRHS<T, C> implication(T... lhs) {
458 		IVecInt clause = new VecInt();
459 		for (T t : lhs) {
460 			clause.push(-getIntValue(t));
461 		}
462 		return new ImplicationRHS<T, C>(this, clause);
463 	}
464 
465 	public DisjunctionRHS<T, C> disjunction(T... lhs) {
466 		IVecInt literals = new VecInt();
467 		for (T t : lhs) {
468 			literals.push(-getIntValue(t));
469 		}
470 		return new DisjunctionRHS<T, C>(this, literals);
471 	}
472 
473 	/**
474 	 * Create a constraint stating that at most i domain object should be set to
475 	 * true.
476 	 * 
477 	 * @param i
478 	 *            the maximum number of domain object to set to true.
479 	 * @param things
480 	 *            the domain objects.
481 	 * @return an object used to name the constraint. The constraint MUST BE
482 	 *         NAMED.
483 	 * @throws ContradictionException
484 	 */
485 	public void atLeast(C name, int i, T... things)
486 			throws ContradictionException {
487 		IVecInt literals = new VecInt();
488 		for (T t : things) {
489 			literals.push(getIntValue(t));
490 		}
491 		this.descs.put(this.solver.addAtLeast(literals, i), name);
492 	}
493 
494 	/**
495 	 * Create a constraint stating that at most i domain object should be set to
496 	 * true.
497 	 * 
498 	 * @param i
499 	 *            the maximum number of domain object to set to true.
500 	 * @param things
501 	 *            the domain objects.
502 	 * @return an object used to name the constraint. The constraint MUST BE
503 	 *         NAMED.
504 	 * @throws ContradictionException
505 	 */
506 	public ImplicationNamer<T, C> atMost(int i, T... things)
507 			throws ContradictionException {
508 		IVec<IConstr> toName = new Vec<IConstr>();
509 		IVecInt literals = new VecInt();
510 		for (T t : things) {
511 			literals.push(getIntValue(t));
512 		}
513 		toName.push(this.solver.addAtMost(literals, i));
514 		return new ImplicationNamer<T, C>(this, toName);
515 	}
516 
517 	/**
518 	 * Create a constraint stating that at most i domain object should be set to
519 	 * true.
520 	 * 
521 	 * @param i
522 	 *            the maximum number of domain object to set to true.
523 	 * @param things
524 	 *            the domain objects.
525 	 * @return an object used to name the constraint. The constraint MUST BE
526 	 *         NAMED.
527 	 * @throws ContradictionException
528 	 */
529 	public void atMost(C name, int i, T... things)
530 			throws ContradictionException {
531 		IVecInt literals = new VecInt();
532 		for (T t : things) {
533 			literals.push(getIntValue(t));
534 		}
535 		this.descs.put(this.solver.addAtMost(literals, i), name);
536 	}
537 
538 	/**
539 	 * Create a clause (thing1 or thing 2 ... or thingn)
540 	 * 
541 	 * @param name
542 	 * @param things
543 	 * 
544 	 * @throws ContradictionException
545 	 */
546 	public void clause(C name, T... things) throws ContradictionException {
547 		IVecInt literals = new VecInt(things.length);
548 		for (T t : things) {
549 			literals.push(getIntValue(t));
550 		}
551 		IConstr constr = this.gator.addClause(literals);
552 		// constr can be null if duplicated clauses are detected.
553 		if (constr != null) {
554 			this.descs.put(constr, name);
555 		}
556 
557 	}
558 
559 	/**
560 	 * Create a constraint using equivalency chains thing <=> (thing1 <=> thing2
561 	 * <=> ... <=> thingn)
562 	 * 
563 	 * @param thing
564 	 *            a domain object
565 	 * @param things
566 	 *            other domain objects.
567 	 * @throws ContradictionException
568 	 */
569 	public void iff(C name, T thing, T... otherThings)
570 			throws ContradictionException {
571 		IVecInt literals = new VecInt(otherThings.length);
572 		for (T t : otherThings) {
573 			literals.push(getIntValue(t));
574 		}
575 		IConstr[] constrs = this.gator.iff(getIntValue(thing), literals);
576 		for (IConstr constr : constrs) {
577 			if (constr != null) {
578 				this.descs.put(constr, name);
579 			}
580 		}
581 	}
582 
583 	/**
584 	 * Create a constraint of the form thing <=> (thing1 and thing 2 ... and
585 	 * thingn)
586 	 * 
587 	 * @param name
588 	 * @param thing
589 	 * @param otherThings
590 	 * @throws ContradictionException
591 	 */
592 	public void and(C name, T thing, T... otherThings)
593 			throws ContradictionException {
594 		IVecInt literals = new VecInt(otherThings.length);
595 		for (T t : otherThings) {
596 			literals.push(getIntValue(t));
597 		}
598 		IConstr[] constrs = this.gator.and(getIntValue(thing), literals);
599 		for (IConstr constr : constrs) {
600 			if (constr != null) {
601 				this.descs.put(constr, name);
602 			}
603 		}
604 	}
605 
606 	/**
607 	 * Create a constraint of the form thing <=> (thing1 or thing 2 ... or
608 	 * thingn)
609 	 * 
610 	 * @param name
611 	 * @param thing
612 	 * @param otherThings
613 	 * @throws ContradictionException
614 	 */
615 	public void or(C name, T thing, T... otherThings)
616 			throws ContradictionException {
617 		IVecInt literals = new VecInt(otherThings.length);
618 		for (T t : otherThings) {
619 			literals.push(getIntValue(t));
620 		}
621 		IConstr[] constrs = this.gator.or(getIntValue(thing), literals);
622 		for (IConstr constr : constrs) {
623 			if (constr != null) {
624 				this.descs.put(constr, name);
625 			}
626 		}
627 	}
628 
629 	/**
630 	 * Create a constraint of the form thing <= (thing1 or thing 2 ... or
631 	 * thingn)
632 	 * 
633 	 * @param name
634 	 * @param thing
635 	 * @param otherThings
636 	 * @throws ContradictionException
637 	 */
638 	public void halfOr(C name, T thing, T... otherThings)
639 			throws ContradictionException {
640 		IVecInt literals = new VecInt(otherThings.length);
641 		for (T t : otherThings) {
642 			literals.push(getIntValue(t));
643 		}
644 		IConstr[] constrs = this.gator.halfOr(getIntValue(thing), literals);
645 		for (IConstr constr : constrs) {
646 			if (constr != null) {
647 				this.descs.put(constr, name);
648 			}
649 		}
650 	}
651 
652 	/**
653 	 * Create a constraint of the form thing <=> (if conditionThing then
654 	 * thenThing else elseThing)
655 	 * 
656 	 * @param name
657 	 * @param thing
658 	 * @param otherThings
659 	 * @throws ContradictionException
660 	 */
661 	public void ifThenElse(C name, T thing, T conditionThing, T thenThing,
662 			T elseThing) throws ContradictionException {
663 		IConstr[] constrs = this.gator.ite(getIntValue(thing),
664 				getIntValue(conditionThing), getIntValue(thenThing),
665 				getIntValue(elseThing));
666 		for (IConstr constr : constrs) {
667 			if (constr != null) {
668 				this.descs.put(constr, name);
669 			}
670 		}
671 	}
672 
673 	/**
674 	 * Add an objective function to ask for a solution that minimize the
675 	 * objective function.
676 	 * 
677 	 * @param wobj
678 	 *            a set of weighted objects (pairs of domain object and
679 	 *            BigInteger).
680 	 */
681 	public void setObjectiveFunction(WeightedObject<T>... wobj) {
682 		createObjectivetiveFunctionIfNeeded(wobj.length);
683 		for (WeightedObject<T> wo : wobj) {
684 			addProperly(wo.thing, wo.getWeight());
685 		}
686 
687 	}
688 
689 	private void addProperly(T thing, BigInteger weight) {
690 		int lit = getIntValue(thing);
691 		int index;
692 		if (this.canonicalOptFunction
693 				&& (index = this.objLiterals.indexOf(lit)) != -1) {
694 			this.objCoefs.set(index, this.objCoefs.get(index).add(weight));
695 			if (this.objCoefs.get(index).equals(BigInteger.ZERO)) {
696 				this.objLiterals.delete(index);
697 				this.objCoefs.delete(index);
698 			}
699 		} else {
700 			this.objLiterals.push(lit);
701 			this.objCoefs.push(weight);
702 		}
703 	}
704 
705 	private void createObjectivetiveFunctionIfNeeded(int n) {
706 		if (this.objFunction == null) {
707 			this.objLiterals = new VecInt(n);
708 			this.objCoefs = new Vec<BigInteger>(n);
709 			this.objFunction = new ObjectiveFunction(this.objLiterals,
710 					this.objCoefs);
711 			this.solver.setObjectiveFunction(this.objFunction);
712 		}
713 	}
714 
715 	/**
716 	 * Add a weighted literal to the objective function.
717 	 * 
718 	 * @param thing
719 	 * @param weight
720 	 */
721 	public void addToObjectiveFunction(T thing, int weight) {
722 		addToObjectiveFunction(thing, BigInteger.valueOf(weight));
723 	}
724 
725 	/**
726 	 * Add a weighted literal to the objective function.
727 	 * 
728 	 * @param thing
729 	 * @param weight
730 	 */
731 	public void addToObjectiveFunction(T thing, BigInteger weight) {
732 		createObjectivetiveFunctionIfNeeded(20);
733 		addProperly(thing, weight);
734 	}
735 
736 	/**
737 	 * Create a PB constraint of the form <code>
738 	 * w1.l1 + w2.l2 + ... wn.ln >= degree
739 	 * </code> where wi are position integers and li are domain objects.
740 	 * 
741 	 * @param degree
742 	 * @param wobj
743 	 * @throws ContradictionException
744 	 */
745 	public void atLeast(C name, BigInteger degree, WeightedObject<T>... wobj)
746 			throws ContradictionException {
747 		IVecInt literals = new VecInt(wobj.length);
748 		IVec<BigInteger> coeffs = new Vec<BigInteger>(wobj.length);
749 		for (WeightedObject<T> wo : wobj) {
750 			literals.push(getIntValue(wo.thing));
751 			coeffs.push(wo.getWeight());
752 		}
753 		this.descs.put(
754 				this.solver.addPseudoBoolean(literals, coeffs, true, degree),
755 				name);
756 	}
757 
758 	/**
759 	 * Create a PB constraint of the form <code>
760 	 * w1.l1 + w2.l2 + ... wn.ln <= degree
761 	 * </code> where wi are position integers and li are domain objects.
762 	 * 
763 	 * @param degree
764 	 * @param wobj
765 	 * @throws ContradictionException
766 	 */
767 	public void atMost(C name, BigInteger degree, WeightedObject<T>... wobj)
768 			throws ContradictionException {
769 		IVecInt literals = new VecInt(wobj.length);
770 		IVec<BigInteger> coeffs = new Vec<BigInteger>(wobj.length);
771 		for (WeightedObject<T> wo : wobj) {
772 			literals.push(getIntValue(wo.thing));
773 			coeffs.push(wo.getWeight());
774 		}
775 		this.descs.put(
776 				this.solver.addPseudoBoolean(literals, coeffs, false, degree),
777 				name);
778 	}
779 
780 	public void atMost(C name, int degree, WeightedObject<T>... wobj)
781 			throws ContradictionException {
782 		atMost(name, BigInteger.valueOf(degree), wobj);
783 	}
784 
785 	/**
786 	 * Stop the SAT solver that is looking for a solution. The solver will throw
787 	 * a TimeoutException.
788 	 */
789 	public void stopSolver() {
790 		this.solver.expireTimeout();
791 	}
792 
793 	/**
794 	 * Stop the explanation computation. A TimeoutException will be thrown by
795 	 * the explanation algorithm.
796 	 */
797 	public void stopExplanation() {
798 		if (!this.explanationEnabled) {
799 			throw new UnsupportedOperationException("Explanation not enabled!");
800 		}
801 		this.xplain.cancelExplanation();
802 	}
803 
804 	public void discard(IVec<T> things) throws ContradictionException {
805 		IVecInt literals = new VecInt(things.size());
806 		for (Iterator<T> it = things.iterator(); it.hasNext();) {
807 			literals.push(-getIntValue(it.next()));
808 		}
809 		this.solver.addBlockingClause(literals);
810 	}
811 
812 	public void discardSolutionsWithObjectiveValueGreaterThan(long value)
813 			throws ContradictionException {
814 		ObjectiveFunction obj = this.solver.getObjectiveFunction();
815 		IVecInt literals = new VecInt(obj.getVars().size());
816 		obj.getVars().copyTo(literals);
817 		IVec<BigInteger> coeffs = new Vec<BigInteger>(obj.getCoeffs().size());
818 		obj.getCoeffs().copyTo(coeffs);
819 		this.solver.addPseudoBoolean(literals, coeffs, false,
820 				BigInteger.valueOf(value));
821 	}
822 
823 	public String getObjectiveFunction() {
824 		ObjectiveFunction obj = this.solver.getObjectiveFunction();
825 		StringBuffer stb = new StringBuffer();
826 		for (int i = 0; i < obj.getVars().size(); i++) {
827 			stb.append(obj.getCoeffs().get(i)
828 					+ (obj.getVars().get(i) > 0 ? " " : "~")
829 					+ this.mapToDomain.get(Math.abs(obj.getVars().get(i)))
830 					+ " ");
831 		}
832 		return stb.toString();
833 	}
834 
835 	public int getNumberOfVariables() {
836 		return this.mapToDimacs.size();
837 	}
838 
839 	public int getNumberOfConstraints() {
840 		return this.descs.size();
841 	}
842 
843 	public Map<Integer, T> getMappingToDomain() {
844 		return this.mapToDomain;
845 	}
846 
847 	public Object not(T thing) {
848 		return new Negation(thing);
849 	}
850 
851 	/**
852 	 * @since 2.2
853 	 * @return the IPBSolver enclosed in the helper.
854 	 */
855 	public IPBSolver getSolver() {
856 		if (this.explanationEnabled) {
857 			return this.xplain.decorated();
858 		}
859 		return this.solver;
860 	}
861 
862 	/**
863 	 * Reset the state of the helper (mapping, objective function, etc).
864 	 * 
865 	 * @since 2.3.1
866 	 */
867 	public void reset() {
868 		this.mapToDimacs.clear();
869 		this.mapToDomain.clear();
870 		this.descs.clear();
871 		this.solver.reset();
872 		if (this.objLiterals != null) {
873 			this.objLiterals.clear();
874 			this.objCoefs.clear();
875 		}
876 	}
877 
878 	/**
879 	 * Compute the objects implied by the assumptions.
880 	 * 
881 	 * @param assumptions
882 	 *            already satisfied objects (e.g. user choices)
883 	 * @param satisfied
884 	 *            the objects that are always satisfied under assumptions
885 	 *            (includes the objects from assumptions)
886 	 * @param falsified
887 	 *            the objects that are always falsified under assumptions.
888 	 * @return the set of objects fixed
889 	 * @throws TimeoutException
890 	 * @since 2.3.3
891 	 */
892 	public void impliedBy(Collection<T> assumptions, Collection<T> satisfied,
893 			Collection<T> falsified) throws TimeoutException {
894 		IVecInt assump = new VecInt(assumptions.size());
895 		for (T thing : assumptions) {
896 			assump.push(getIntValue(thing));
897 		}
898 		IVecInt implied = Backbone.compute(solver, assump);
899 		int p;
900 		for (IteratorInt it = implied.iterator(); it.hasNext();) {
901 			p = it.next();
902 			if (p > 0) {
903 				satisfied.add(mapToDomain.get(p));
904 			} else {
905 				falsified.add(mapToDomain.get(-p));
906 			}
907 		}
908 	}
909 }