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  package org.sat4j.maxsat;
31  
32  import java.math.BigInteger;
33  import java.util.HashSet;
34  import java.util.Set;
35  
36  import org.sat4j.core.ConstrGroup;
37  import org.sat4j.core.Vec;
38  import org.sat4j.core.VecInt;
39  import org.sat4j.pb.IPBSolver;
40  import org.sat4j.pb.ObjectiveFunction;
41  import org.sat4j.pb.PBSolverDecorator;
42  import org.sat4j.specs.ContradictionException;
43  import org.sat4j.specs.IConstr;
44  import org.sat4j.specs.IVec;
45  import org.sat4j.specs.IVecInt;
46  import org.sat4j.specs.IteratorInt;
47  
48  /**
49   * A decorator for solving weighted MAX SAT problems.
50   * 
51   * The first value of the list of literals in the addClause() method contains
52   * the weight of the clause.
53   * 
54   * @author daniel
55   * 
56   */
57  public class WeightedMaxSatDecorator extends PBSolverDecorator {
58  
59      public static final BigInteger SAT4J_MAX_BIG_INTEGER = new BigInteger(
60              "100000000000000000000000000000000000000000");
61  
62      protected int nbnewvar;
63  
64      private static final long serialVersionUID = 1L;
65  
66      private BigInteger falsifiedWeight = BigInteger.ZERO;
67  
68      private boolean maxVarIdFixed = false;
69      private final boolean equivalence;
70  
71      private final IVecInt lits = new VecInt();
72  
73      private final IVec<BigInteger> coefs = new Vec<BigInteger>();
74  
75      private final ObjectiveFunction obj = new ObjectiveFunction(this.lits,
76              this.coefs);
77  
78      private final Set<Integer> unitClauses = new HashSet<Integer>();
79  
80      private boolean noNewVarForUnitSoftClauses = true;
81  
82      public WeightedMaxSatDecorator(IPBSolver solver) {
83          this(solver, false);
84      }
85  
86      public WeightedMaxSatDecorator(IPBSolver solver, boolean equivalence) {
87          super(solver);
88          solver.setObjectiveFunction(this.obj);
89          this.equivalence = equivalence;
90      }
91  
92      @Override
93      public int newVar(int howmany) {
94          int res = super.newVar(howmany);
95          this.maxVarIdFixed = true;
96          return res;
97      }
98  
99      @Override
100     public void setExpectedNumberOfClauses(int nb) {
101         this.lits.ensure(nb);
102         this.falsifiedWeight = BigInteger.ZERO;
103         super.setExpectedNumberOfClauses(nb);
104     }
105 
106     protected BigInteger top = SAT4J_MAX_BIG_INTEGER;
107 
108     public void setTopWeight(BigInteger top) {
109         this.top = top;
110     }
111 
112     protected void checkMaxVarId() {
113         if (!this.maxVarIdFixed) {
114             throw new IllegalStateException(
115                     "Please call newVar(int) before adding constraints!!!");
116         }
117     }
118 
119     /**
120      * Add a soft clause to the solver.
121      * 
122      * That method allows to read a clause in a CNF and to consider it as soft,
123      * in order to solve MAXSAT problems.
124      * 
125      * Note that the behavior of that method changed in release 2.3.1. Prior to
126      * that, the method was expecting a weight as first element of the list of
127      * literals.
128      * 
129      * @param literals
130      *            a weighted clause, the weight being the first element of the
131      *            vector.
132      * @see #setTopWeight(int)
133      */
134     @Override
135     public IConstr addClause(IVecInt literals) throws ContradictionException {
136         return addSoftClause(1, literals);
137     }
138 
139     /**
140      * Add a hard clause in the solver, i.e. a clause that must be satisfied.
141      * 
142      * @param literals
143      *            the clause
144      * @return the constraint is it is not trivially satisfied.
145      * @throws ContradictionException
146      */
147     public IConstr addHardClause(IVecInt literals)
148             throws ContradictionException {
149         return super.addClause(literals);
150     }
151 
152     /**
153      * Add a soft clause in the solver, i.e. a clause with a weight of 1.
154      * 
155      * @param literals
156      *            the clause.
157      * @return the constraint is it is not trivially satisfied.
158      * @throws ContradictionException
159      */
160     public IConstr addSoftClause(IVecInt literals)
161             throws ContradictionException {
162         return addSoftClause(1, literals);
163     }
164 
165     /**
166      * Add a soft clause to the solver.
167      * 
168      * if the weight of the clause is greater of equal to the top weight, the
169      * clause will be considered as a hard clause.
170      * 
171      * @param weight
172      *            the weight of the clause
173      * @param literals
174      *            the clause
175      * @return the constraint is it is not trivially satisfied.
176      * @throws ContradictionException
177      */
178     public IConstr addSoftClause(int weight, IVecInt literals)
179             throws ContradictionException {
180         return addSoftClause(BigInteger.valueOf(weight), literals);
181     }
182 
183     public IConstr addSoftClause(BigInteger weight, IVecInt literals)
184             throws ContradictionException {
185         checkMaxVarId();
186         if (weight.compareTo(this.top) < 0) {
187 
188             if (literals.size() == 1 && noNewVarForUnitSoftClauses) {
189                 // if there is only a coefficient and a literal, no need to
190                 // create
191                 // a new variable
192                 // check first if the literal is already in the list:
193                 int lit = -literals.get(0);
194                 int index = this.lits.containsAt(lit);
195 
196                 this.unitClauses.add(-lit);
197 
198                 if (index == -1) {
199                     // check if the opposite literal is already there
200                     index = this.lits.containsAt(-lit);
201                     if (index != -1) {
202                         this.falsifiedWeight = this.falsifiedWeight.add(weight);
203                         BigInteger oldw = this.coefs.get(index);
204                         BigInteger diff = oldw.subtract(weight);
205                         if (diff.signum() > 0) {
206                             this.coefs.set(index, diff);
207                         } else if (diff.signum() < 0) {
208                             this.lits.set(index, lit);
209                             this.coefs.set(index, diff.abs());
210                             // remove from falsifiedWeight the
211                             // part of the weight that will remain
212                             // in the objective function
213                             this.falsifiedWeight = this.falsifiedWeight
214                                     .add(diff);
215                         } else {
216                             assert diff.signum() == 0;
217                             this.lits.delete(index);
218                             this.coefs.delete(index);
219                         }
220                         this.obj.setCorrection(this.falsifiedWeight);
221 
222                     } else {
223                         registerLiteral(lit);
224                         this.lits.push(lit);
225                         this.coefs.push(weight);
226                     }
227                 } else {
228                     this.coefs.set(index, this.coefs.get(index).add(weight));
229                 }
230                 return UnitWeightedClause.instance();
231             }
232             this.coefs.push(weight);
233             int newvar = nextFreeVarId(true);
234             literals.push(newvar);
235             this.lits.push(newvar);
236             if (this.equivalence) {
237                 ConstrGroup constrs = new ConstrGroup();
238                 IConstr constr = super.addClause(literals);
239                 if (constr==null&&isVerbose()) {
240                     System.out.println(getLogPrefix()+" solft constraint "+literals+"("+weight+") is ignored");
241                 }
242                 if (constr!=null) {
243                     constrs.add(constr);
244                     IVecInt clause = new VecInt(2);
245                     clause.push(-newvar);
246                     for (int i = 0; i < literals.size() - 1; i++) {
247                         clause.push(-literals.get(i));
248                         constrs.add(super.addClause(clause));
249                         clause.pop();
250                     }
251                 } 
252                 return constrs;
253             }
254         }
255         IConstr constr = super.addClause(literals);
256         if (constr==null&&isVerbose()) {
257             System.out.println(getLogPrefix()+" hard constraint "+literals+"("+weight+") is ignored");
258         }
259         return constr;
260     }
261 
262     /**
263      * Allow adding a new soft cardinality constraint in the solver.
264      * 
265      * @param literals
266      *            the literals of the cardinality constraint.
267      * @param degree
268      *            the degree of the cardinality constraint.
269      * @return a pseudo boolean constraint encoding that soft constraint.
270      * @throws ContradictionException
271      *             if a trivial contradiction is found.
272      * @since 2.3
273      */
274     public IConstr addSoftAtLeast(IVecInt literals, int degree)
275             throws ContradictionException {
276         return addSoftAtLeast(BigInteger.ONE, literals, degree);
277     }
278 
279     /**
280      * Allow adding a new soft cardinality constraint in the solver.
281      * 
282      * @param weight
283      *            the weight of the constraint.
284      * @param literals
285      *            the literals of the cardinality constraint.
286      * @param degree
287      *            the degree of the cardinality constraint.
288      * @return a pseudo boolean constraint encoding that soft constraint.
289      * @throws ContradictionException
290      *             if a trivial contradiction is found.
291      * @since 2.3
292      */
293     public IConstr addSoftAtLeast(int weight, IVecInt literals, int degree)
294             throws ContradictionException {
295         return addSoftAtLeast(BigInteger.valueOf(weight), literals, degree);
296     }
297 
298     /**
299      * Allow adding a new soft cardinality constraint in the solver.
300      * 
301      * @param weight
302      *            the weight of the constraint.
303      * @param literals
304      *            the literals of the cardinality constraint.
305      * @param degree
306      *            the degree of the cardinality constraint.
307      * @return a pseudo boolean constraint encoding that soft constraint.
308      * @throws ContradictionException
309      *             if a trivial contradiction is found.
310      * @since 2.3
311      */
312     public IConstr addSoftAtLeast(BigInteger weight, IVecInt literals,
313             int degree) throws ContradictionException {
314         checkMaxVarId();
315         if (weight.compareTo(this.top) < 0) {
316             this.coefs.push(weight);
317             int newvar = nextFreeVarId(true);
318             this.lits.push(newvar);
319             IVec<BigInteger> cardcoeffs = new Vec<BigInteger>(
320                     literals.size() + 1);
321             cardcoeffs.growTo(literals.size(), BigInteger.ONE);
322             literals.push(newvar);
323             BigInteger bigDegree = BigInteger.valueOf(degree);
324             cardcoeffs.push(bigDegree);
325             return addPseudoBoolean(literals, cardcoeffs, true, bigDegree);
326         } else {
327             return addAtLeast(literals, degree);
328         }
329     }
330 
331     /**
332      * Allow adding a new soft cardinality constraint in the solver.
333      * 
334      * @param literals
335      *            the literals of the cardinality constraint.
336      * @param degree
337      *            the degree of the cardinality constraint.
338      * @return a pseudo boolean constraint encoding that soft constraint.
339      * @throws ContradictionException
340      *             if a trivial contradiction is found.
341      * @since 2.3
342      */
343     public IConstr addSoftAtMost(IVecInt literals, int degree)
344             throws ContradictionException {
345         return addSoftAtMost(BigInteger.ONE, literals, degree);
346     }
347 
348     /**
349      * Allow adding a new soft cardinality constraint in the solver.
350      * 
351      * @param weight
352      *            the weight of the constraint.
353      * @param literals
354      *            the literals of the cardinality constraint.
355      * @param degree
356      *            the degree of the cardinality constraint.
357      * @return a pseudo boolean constraint encoding that soft constraint.
358      * @throws ContradictionException
359      *             if a trivial contradiction is found.
360      * @since 2.3
361      */
362     public IConstr addSoftAtMost(int weight, IVecInt literals, int degree)
363             throws ContradictionException {
364         return addSoftAtMost(BigInteger.valueOf(weight), literals, degree);
365     }
366 
367     /**
368      * Allow adding a new soft cardinality constraint in the solver.
369      * 
370      * @param weight
371      *            the weight of the constraint.
372      * @param literals
373      *            the literals of the cardinality constraint.
374      * @param degree
375      *            the degree of the cardinality constraint.
376      * @return a pseudo boolean constraint encoding that soft constraint.
377      * @throws ContradictionException
378      *             if a trivial contradiction is found.
379      * @since 2.3
380      */
381     public IConstr addSoftAtMost(BigInteger weight, IVecInt literals, int degree)
382             throws ContradictionException {
383         checkMaxVarId();
384         if (weight.compareTo(this.top) < 0) {
385             this.coefs.push(weight);
386             int newvar = nextFreeVarId(true);
387             this.lits.push(newvar);
388             IVec<BigInteger> cardcoeffs = new Vec<BigInteger>(
389                     literals.size() + 1);
390             cardcoeffs.growTo(literals.size(), BigInteger.ONE);
391             literals.push(newvar);
392             BigInteger bigDegree = BigInteger.valueOf(degree);
393             cardcoeffs.push(bigDegree.negate());
394             return addPseudoBoolean(literals, cardcoeffs, true, bigDegree);
395         } else {
396             return addAtMost(literals, degree);
397         }
398     }
399 
400     /**
401      * Set some literals whose sum must be minimized.
402      * 
403      * @param literals
404      *            the sum of those literals must be minimized.
405      */
406     public void addLiteralsToMinimize(IVecInt literals) {
407         for (IteratorInt it = literals.iterator(); it.hasNext();) {
408             this.lits.push(it.next());
409             this.coefs.push(BigInteger.ONE);
410         }
411     }
412 
413     /**
414      * Set some literals whose sum must be minimized.
415      * 
416      * @param literals
417      *            the sum of those literals must be minimized.
418      * @param coefficients
419      *            the weight of the literals.
420      */
421     public void addWeightedLiteralsToMinimize(IVecInt literals,
422             IVec<BigInteger> coefficients) {
423         if (literals.size() != this.coefs.size()) {
424             throw new IllegalArgumentException();
425         }
426         for (int i = 0; i < literals.size(); i++) {
427             this.lits.push(literals.get(i));
428             this.coefs.push(coefficients.get(i));
429         }
430     }
431 
432     /**
433      * Set some literals whose sum must be minimized.
434      * 
435      * @param literals
436      *            the sum of those literals must be minimized.
437      * @param coefficients
438      *            the weight of the literals.
439      */
440     public void addWeightedLiteralsToMinimize(IVecInt literals,
441             IVecInt coefficients) {
442         if (literals.size() != coefficients.size()) {
443             throw new IllegalArgumentException();
444         }
445         for (int i = 0; i < literals.size(); i++) {
446             this.lits.push(literals.get(i));
447             this.coefs.push(BigInteger.valueOf(coefficients.get(i)));
448         }
449     }
450 
451     @Override
452     public void reset() {
453         this.coefs.clear();
454         this.lits.clear();
455         this.nbnewvar = 0;
456         super.reset();
457     }
458 
459     /**
460      * Force the solver to find a solution with a specific value (nice to find
461      * all optimal solutions for instance).
462      * 
463      * @param forcedValue
464      *            the expected value of the solution
465      * @throws ContradictionException
466      *             if that value is trivially not possible.
467      */
468     public void forceObjectiveValueTo(Number forcedValue)
469             throws ContradictionException {
470         if (this.lits.size() > 0) {
471             // there is at least one soft clause
472             super.addPseudoBoolean(this.lits, this.coefs, false,
473                     (BigInteger) forcedValue);
474         }
475     }
476 
477     /**
478      * Returns the set of unit clauses added to the solver.
479      */
480     public Set<Integer> getUnitClauses() {
481         return this.unitClauses;
482     }
483 
484     /**
485      * Returns true if no new variable should be created when adding a soft unit
486      * clause, false otherwise. By default, this is set to true.
487      * 
488      * @return
489      */
490     public boolean isNoNewVarForUnitSoftClauses() {
491         return noNewVarForUnitSoftClauses;
492     }
493 
494     /**
495      * Sets whether new variables should be created when adding new clauses. By
496      * default, this is set to true. This can be useful when computing all
497      * muses.
498      * 
499      * @param noNewVarForUnitSoftClauses
500      */
501     public void setNoNewVarForUnitSoftClauses(boolean noNewVarForUnitSoftClauses) {
502         this.noNewVarForUnitSoftClauses = noNewVarForUnitSoftClauses;
503     }
504 
505 }