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 }