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 }