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  
34  import org.sat4j.core.ConstrGroup;
35  import org.sat4j.core.Vec;
36  import org.sat4j.core.VecInt;
37  import org.sat4j.pb.IPBSolver;
38  import org.sat4j.pb.ObjectiveFunction;
39  import org.sat4j.pb.PBSolverDecorator;
40  import org.sat4j.specs.ContradictionException;
41  import org.sat4j.specs.IConstr;
42  import org.sat4j.specs.IVec;
43  import org.sat4j.specs.IVecInt;
44  import org.sat4j.specs.IteratorInt;
45  
46  /**
47   * A decorator for solving weighted MAX SAT problems.
48   * 
49   * The first value of the list of literals in the addClause() method contains
50   * the weight of the clause.
51   * 
52   * @author daniel
53   * 
54   */
55  public class WeightedMaxSatDecorator extends PBSolverDecorator {
56  
57      public static final BigInteger SAT4J_MAX_BIG_INTEGER = new BigInteger(
58              "100000000000000000000000000000000000000000");
59  
60      protected int nbnewvar;
61  
62      private static final long serialVersionUID = 1L;
63  
64      private BigInteger falsifiedWeight = BigInteger.ZERO;
65  
66      private boolean maxVarIdFixed = false;
67      private final boolean equivalence;
68  
69      private final IVecInt lits = new VecInt();
70  
71      private final IVec<BigInteger> coefs = new Vec<BigInteger>();
72  
73      private final ObjectiveFunction obj = new ObjectiveFunction(this.lits,
74              this.coefs);
75  
76  
77      public WeightedMaxSatDecorator(IPBSolver solver) {
78          this(solver, false);
79      }
80  
81      public WeightedMaxSatDecorator(IPBSolver solver, boolean equivalence) {
82          super(solver);
83          solver.setObjectiveFunction(this.obj);
84          this.equivalence = equivalence;
85      }
86  
87      @Override
88      public int newVar(int howmany) {
89          int res = super.newVar(howmany);
90          this.maxVarIdFixed = true;
91          return res;
92      }
93  
94      @Override
95      public void setExpectedNumberOfClauses(int nb) {
96          this.lits.ensure(nb);
97          this.falsifiedWeight = BigInteger.ZERO;
98          super.setExpectedNumberOfClauses(nb);
99      }
100 
101     protected BigInteger top = SAT4J_MAX_BIG_INTEGER;
102 
103     public void setTopWeight(BigInteger top) {
104         this.top = top;
105     }
106 
107     protected void checkMaxVarId() {
108         if (!this.maxVarIdFixed) {
109             throw new IllegalStateException(
110                     "Please call newVar(int) before adding constraints!!!");
111         }
112     }
113 
114     /**
115      * Add a soft clause to the solver.
116      * 
117      * That method allows to read a clause in a CNF and to consider it as soft,
118      * in order to solve MAXSAT problems.
119      * 
120      * Note that the behavior of that method changed in release 2.3.1. Prior to
121      * that, the method was expecting a weight as first element of the list of
122      * literals.
123      * 
124      * @param literals
125      *            a weighted clause, the weight being the first element of the
126      *            vector.
127      * @see #setTopWeight(int)
128      */
129     @Override
130     public IConstr addClause(IVecInt literals) throws ContradictionException {
131         return addSoftClause(1, literals);
132     }
133 
134     /**
135      * Add a hard clause in the solver, i.e. a clause that must be satisfied.
136      * 
137      * @param literals
138      *            the clause
139      * @return the constraint is it is not trivially satisfied.
140      * @throws ContradictionException
141      */
142     public IConstr addHardClause(IVecInt literals)
143             throws ContradictionException {
144         return super.addClause(literals);
145     }
146 
147     /**
148      * Add a soft clause in the solver, i.e. a clause with a weight of 1.
149      * 
150      * @param literals
151      *            the clause.
152      * @return the constraint is it is not trivially satisfied.
153      * @throws ContradictionException
154      */
155     public IConstr addSoftClause(IVecInt literals)
156             throws ContradictionException {
157         return addSoftClause(1, literals);
158     }
159 
160     /**
161      * Add a soft clause to the solver.
162      * 
163      * if the weight of the clause is greater of equal to the top weight, the
164      * clause will be considered as a hard clause.
165      * 
166      * @param weight
167      *            the weight of the clause
168      * @param literals
169      *            the clause
170      * @return the constraint is it is not trivially satisfied.
171      * @throws ContradictionException
172      */
173     public IConstr addSoftClause(int weight, IVecInt literals)
174             throws ContradictionException {
175         return addSoftClause(BigInteger.valueOf(weight), literals);
176     }
177 
178     public IConstr addSoftClause(BigInteger weight, IVecInt literals)
179             throws ContradictionException {
180         checkMaxVarId();
181         if (weight.compareTo(this.top) < 0) {
182 
183             if (literals.size() == 1) {
184                 // if there is only a coefficient and a literal, no need to
185                 // create
186                 // a new variable
187                 // check first if the literal is already in the list:
188                 int lit = -literals.get(0);
189                 int index = this.lits.containsAt(lit);
190                 if (index == -1) {
191                     // check if the opposite literal is already there
192                     index = this.lits.containsAt(-lit);
193                     if (index != -1) {
194                         this.falsifiedWeight = this.falsifiedWeight.add(weight);
195                         BigInteger oldw = this.coefs.get(index);
196                         BigInteger diff = oldw.subtract(weight);
197                         if (diff.signum() > 0) {
198                             this.coefs.set(index, diff);
199                         } else if (diff.signum() < 0) {
200                             this.lits.set(index, lit);
201                             this.coefs.set(index, diff.abs());
202                             // remove from falsifiedWeight the
203                             // part of the weight that will remain
204                             // in the objective function
205                             this.falsifiedWeight = this.falsifiedWeight
206                                     .add(diff);
207                         } else {
208                             assert diff.signum() == 0;
209                             this.lits.delete(index);
210                             this.coefs.delete(index);
211                         }
212                         this.obj.setCorrection(this.falsifiedWeight);
213                     } else {
214                         registerLiteral(lit);
215                         this.lits.push(lit);
216                         this.coefs.push(weight);
217                     }
218                 } else {
219                     this.coefs.set(index, this.coefs.get(index).add(weight));
220                 }
221                 return UnitWeightedClause.instance();
222             }
223             this.coefs.push(weight);
224             int newvar = nextFreeVarId(true);
225             literals.push(newvar);
226             this.lits.push(newvar);
227             if (this.equivalence) {
228                 ConstrGroup constrs = new ConstrGroup();
229                 constrs.add(super.addClause(literals));
230                 IVecInt clause = new VecInt(2);
231                 clause.push(-newvar);
232                 for (int i = 0; i < literals.size() - 1; i++) {
233                     clause.push(-literals.get(i));
234                     constrs.add(super.addClause(clause));
235                     clause.pop();
236                 }         
237                 return constrs;
238             }
239         }
240         return super.addClause(literals);
241     }
242 
243     /**
244      * Allow adding a new soft cardinality constraint in the solver.
245      * 
246      * @param literals
247      *            the literals of the cardinality constraint.
248      * @param degree
249      *            the degree of the cardinality constraint.
250      * @return a pseudo boolean constraint encoding that soft constraint.
251      * @throws ContradictionException
252      *             if a trivial contradiction is found.
253      * @since 2.3
254      */
255     public IConstr addSoftAtLeast(IVecInt literals, int degree)
256             throws ContradictionException {
257         return addSoftAtLeast(BigInteger.ONE, literals, degree);
258     }
259 
260     /**
261      * Allow adding a new soft cardinality constraint in the solver.
262      * 
263      * @param weight
264      *            the weight of the constraint.
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(int weight, IVecInt literals, int degree)
275             throws ContradictionException {
276         return addSoftAtLeast(BigInteger.valueOf(weight), 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(BigInteger weight, IVecInt literals,
294             int degree) throws ContradictionException {
295         checkMaxVarId();
296         if (weight.compareTo(this.top) < 0) {
297             this.coefs.push(weight);
298             int newvar = nextFreeVarId(true);
299             this.lits.push(newvar);
300             IVec<BigInteger> cardcoeffs = new Vec<BigInteger>(
301                     literals.size() + 1);
302             cardcoeffs.growTo(literals.size(), BigInteger.ONE);
303             literals.push(newvar);
304             BigInteger bigDegree = BigInteger.valueOf(degree);
305             cardcoeffs.push(bigDegree);
306             return addPseudoBoolean(literals, cardcoeffs, true, bigDegree);
307         } else {
308             return addAtLeast(literals, degree);
309         }
310     }
311 
312     /**
313      * Allow adding a new soft cardinality constraint in the solver.
314      * 
315      * @param literals
316      *            the literals of the cardinality constraint.
317      * @param degree
318      *            the degree of the cardinality constraint.
319      * @return a pseudo boolean constraint encoding that soft constraint.
320      * @throws ContradictionException
321      *             if a trivial contradiction is found.
322      * @since 2.3
323      */
324     public IConstr addSoftAtMost(IVecInt literals, int degree)
325             throws ContradictionException {
326         return addSoftAtMost(BigInteger.ONE, literals, degree);
327     }
328 
329     /**
330      * Allow adding a new soft cardinality constraint in the solver.
331      * 
332      * @param weight
333      *            the weight of the constraint.
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(int weight, IVecInt literals, int degree)
344             throws ContradictionException {
345         return addSoftAtMost(BigInteger.valueOf(weight), 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(BigInteger weight, IVecInt literals, int degree)
363             throws ContradictionException {
364         checkMaxVarId();
365         if (weight.compareTo(this.top) < 0) {
366             this.coefs.push(weight);
367             int newvar = nextFreeVarId(true);
368             this.lits.push(newvar);
369             IVec<BigInteger> cardcoeffs = new Vec<BigInteger>(
370                     literals.size() + 1);
371             cardcoeffs.growTo(literals.size(), BigInteger.ONE);
372             literals.push(newvar);
373             BigInteger bigDegree = BigInteger.valueOf(degree);
374             cardcoeffs.push(bigDegree.negate());
375             return addPseudoBoolean(literals, cardcoeffs, true, bigDegree);
376         } else {
377             return addAtMost(literals, degree);
378         }
379     }
380 
381     /**
382      * Set some literals whose sum must be minimized.
383      * 
384      * @param literals
385      *            the sum of those literals must be minimized.
386      */
387     public void addLiteralsToMinimize(IVecInt literals) {
388         for (IteratorInt it = literals.iterator(); it.hasNext();) {
389             this.lits.push(it.next());
390             this.coefs.push(BigInteger.ONE);
391         }
392     }
393 
394     /**
395      * Set some literals whose sum must be minimized.
396      * 
397      * @param literals
398      *            the sum of those literals must be minimized.
399      * @param coefficients
400      *            the weight of the literals.
401      */
402     public void addWeightedLiteralsToMinimize(IVecInt literals,
403             IVec<BigInteger> coefficients) {
404         if (literals.size() != this.coefs.size()) {
405             throw new IllegalArgumentException();
406         }
407         for (int i = 0; i < literals.size(); i++) {
408             this.lits.push(literals.get(i));
409             this.coefs.push(coefficients.get(i));
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             IVecInt coefficients) {
423         if (literals.size() != coefficients.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(BigInteger.valueOf(coefficients.get(i)));
429         }
430     }
431 
432     @Override
433     public void reset() {
434         this.coefs.clear();
435         this.lits.clear();
436         this.nbnewvar = 0;
437         super.reset();
438     }
439 
440     /**
441      * Force the solver to find a solution with a specific value
442      * (nice to find all optimal solutions for instance).
443      * 
444      * @param forcedValue the expected value of the solution
445      * @throws ContradictionException if that value is trivially not possible.
446      */
447     public void forceObjectiveValueTo(Number forcedValue)
448             throws ContradictionException {
449         if (this.lits.size() > 0) {
450             // there is at least one soft clause
451             super.addPseudoBoolean(this.lits, this.coefs, false,
452                     (BigInteger) forcedValue);
453         }
454     }
455 }