View Javadoc

1   /*******************************************************************************
2    * SAT4J: a SATisfiability library for Java Copyright (C) 2004-2008 Daniel Le
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  package org.sat4j.maxsat;
20  
21  import java.math.BigInteger;
22  
23  import org.sat4j.core.Vec;
24  import org.sat4j.core.VecInt;
25  import org.sat4j.minisat.core.DataStructureFactory;
26  import org.sat4j.minisat.core.IOrder;
27  import org.sat4j.minisat.core.Solver;
28  import org.sat4j.pb.IPBSolver;
29  import org.sat4j.pb.ObjectiveFunction;
30  import org.sat4j.pb.PBSolverDecorator;
31  import org.sat4j.pb.orders.VarOrderHeapObjective;
32  import org.sat4j.specs.ContradictionException;
33  import org.sat4j.specs.IConstr;
34  import org.sat4j.specs.IOptimizationProblem;
35  import org.sat4j.specs.IVec;
36  import org.sat4j.specs.IVecInt;
37  import org.sat4j.specs.IteratorInt;
38  import org.sat4j.specs.TimeoutException;
39  
40  /**
41   * A decorator for solving weighted MAX SAT problems.
42   * 
43   * The first value of the list of literals in the addClause() method contains
44   * the weight of the clause.
45   * 
46   * @author daniel
47   * 
48   */
49  public class WeightedMaxSatDecorator extends PBSolverDecorator implements
50  		IOptimizationProblem {
51  
52  	/**
53       * 
54       */
55  	private static final long serialVersionUID = 1L;
56  
57  	protected int nborigvars;
58  
59  	private int nbexpectedclauses;
60  
61  	private long falsifiedWeight;
62  
63  	protected int nbnewvar;
64  
65  	protected int[] prevmodel;
66  	protected boolean[] prevboolmodel;
67  
68  	protected int[] prevfullmodel;
69  
70  	public WeightedMaxSatDecorator(IPBSolver solver) {
71  		super(solver);
72  		IOrder<?> order = ((Solver<?, ? extends DataStructureFactory<?>>) solver)
73  				.getOrder();
74  		if (order instanceof VarOrderHeapObjective) {
75  			((VarOrderHeapObjective) order).setObjectiveFunction(obj);
76  		}
77  	}
78  
79  	@Override
80  	public int newVar(int howmany) {
81  		nborigvars = super.newVar(howmany);
82  		return nborigvars;
83  	}
84  
85  	@Override
86  	public void setExpectedNumberOfClauses(int nb) {
87  		nbexpectedclauses = nb;
88  		lits.ensure(nb);
89  		falsifiedWeight = 0;
90  		super.setExpectedNumberOfClauses(nb);
91  		super.newVar(nborigvars + nbexpectedclauses);
92  	}
93  
94  	@Override
95  	public int[] model() {
96  		return prevmodel;
97  	}
98  
99  	@Override
100 	public boolean model(int var) {
101 		return prevboolmodel[var - 1];
102 	}
103 
104 	protected int top = Integer.MAX_VALUE;
105 
106 	public void setTopWeight(int top) {
107 		this.top = top;
108 	}
109 
110 	/**
111 	 * Add a set of literals to the solver.
112 	 * 
113 	 * Here the assumption is that the first literal (literals[0]) is the weight
114 	 * of the constraint as found in the MAXSAT evaluation. if the weight is
115 	 * greater or equal to the top weight, then the clause is hard, else it is
116 	 * soft.
117 	 * 
118 	 * @param literals
119 	 *            a weighted clause, the weight being the first element of the
120 	 *            vector.
121 	 * @see #setTopWeight(int)
122 	 */
123 	@Override
124 	public IConstr addClause(IVecInt literals) throws ContradictionException {
125 		int weight = literals.get(0);
126 		literals.delete(0);
127 		return addSoftClause(weight, literals);
128 	}
129 
130 	/**
131 	 * Add a hard clause in the solver, i.e. a clause that must be satisfied.
132 	 * 
133 	 * @param literals
134 	 *            the clause
135 	 * @return the constraint is it is not trivially satisfied.
136 	 * @throws ContradictionException
137 	 */
138 	public IConstr addHardClause(IVecInt literals)
139 			throws ContradictionException {
140 		return super.addClause(literals);
141 	}
142 
143 	/**
144 	 * Add a soft clause in the solver, i.e. a clause with a weight of 1.
145 	 * 
146 	 * @param literals
147 	 *            the clause.
148 	 * @return the constraint is it is not trivially satisfied.
149 	 * @throws ContradictionException
150 	 */
151 	public IConstr addSoftClause(IVecInt literals)
152 			throws ContradictionException {
153 		return addSoftClause(1, literals);
154 	}
155 
156 	/**
157 	 * Add a soft clause to the solver.
158 	 * 
159 	 * if the weight of the clause is greater of equal to the top weight, the
160 	 * clause will be considered as a hard clause.
161 	 * 
162 	 * @param weight
163 	 *            the weight of the clause
164 	 * @param literals
165 	 *            the clause
166 	 * @return the constraint is it is not trivially satisfied.
167 	 * @throws ContradictionException
168 	 */
169 	public IConstr addSoftClause(int weight, IVecInt literals)
170 			throws ContradictionException {
171 		if (weight < top) {
172 			BigInteger bigweight = BigInteger.valueOf(weight);
173 			if (literals.size() == 2) {
174 				// if there is only a coefficient and a literal, no need to
175 				// create
176 				// a new variable
177 				// check first if the literal is already in the list:
178 				int lit = -literals.get(1);
179 				int index = lits.containsAt(lit);
180 				if (index != -1) {
181 					coefs.set(index, coefs.get(index).add(bigweight));
182 				} else {
183 					// check if the opposite literal is already there
184 					index = lits.containsAt(-lit);
185 					if (index != -1) {
186 						falsifiedWeight += weight;
187 						BigInteger oldw = coefs.get(index);
188 						BigInteger diff = oldw.subtract(bigweight);
189 						if (diff.signum() > 0) {
190 							coefs.set(index, diff);
191 						} else if (diff.signum() < 0) {
192 							lits.set(index, lit);
193 							coefs.set(index, diff.abs());
194 							// remove from falsifiedWeight the
195 							// part of the weight that will remain
196 							// in the objective function
197 							falsifiedWeight += diff.intValue();
198 						} else {
199 							assert diff.signum() == 0;
200 							lits.delete(index);
201 							coefs.delete(index);
202 						}
203 					} else {
204 						lits.push(lit);
205 						coefs.push(bigweight);
206 					}
207 				}
208 				return null;
209 			}
210 			coefs.push(bigweight);
211 			int newvar = nborigvars + ++nbnewvar;
212 			literals.push(newvar);
213 			lits.push(newvar);
214 		}
215 		return super.addClause(literals);
216 	}
217 
218 	/**
219 	 * Set some literals whose sum must be minimized.
220 	 * 
221 	 * @param literals
222 	 *            the sum of those literals must be minimized.
223 	 */
224 	public void addLiteralsToMinimize(IVecInt literals) {
225 		for (IteratorInt it = literals.iterator(); it.hasNext();) {
226 			lits.push(it.next());
227 			coefs.push(BigInteger.ONE);
228 		}
229 	}
230 
231 	/**
232 	 * Set some literals whose sum must be minimized.
233 	 * 
234 	 * @param literals
235 	 *            the sum of those literals must be minimized.
236 	 * @param coefficients
237 	 *            the weight of the literals.
238 	 */
239 	public void addWeightedLiteralsToMinimize(IVecInt literals,
240 			IVec<BigInteger> coefficients) {
241 		if (literals.size() != coefs.size())
242 			throw new IllegalArgumentException();
243 		for (int i = 0; i < literals.size(); i++) {
244 			lits.push(literals.get(i));
245 			coefs.push(coefficients.get(i));
246 		}
247 	}
248 
249 	/**
250 	 * Set some literals whose sum must be minimized.
251 	 * 
252 	 * @param literals
253 	 *            the sum of those literals must be minimized.
254 	 * @param coefficients
255 	 *            the weight of the literals.
256 	 */
257 	public void addWeightedLiteralsToMinimize(IVecInt literals,
258 			IVecInt coefficients) {
259 		if (literals.size() != coefficients.size())
260 			throw new IllegalArgumentException();
261 		for (int i = 0; i < literals.size(); i++) {
262 			lits.push(literals.get(i));
263 			coefs.push(BigInteger.valueOf(coefficients.get(i)));
264 		}
265 	}
266 
267 	public boolean admitABetterSolution() throws TimeoutException {
268 		boolean result = super.isSatisfiable(true);
269 		if (result) {
270 			prevboolmodel = new boolean[nVars()];
271 			for (int i = 0; i < nVars(); i++) {
272 				prevboolmodel[i] = decorated().model(i + 1);
273 			}
274 			int nbtotalvars = nborigvars + nbnewvar;
275 			if (prevfullmodel == null)
276 				prevfullmodel = new int[nbtotalvars];
277 			for (int i = 1; i <= nbtotalvars; i++) {
278 				prevfullmodel[i - 1] = super.model(i) ? i : -i;
279 			}
280 			prevmodel = new int[nborigvars];
281 			for (int i = 0; i < nborigvars; i++) {
282 				prevmodel[i] = prevfullmodel[i];
283 			}
284 		}
285 		return result;
286 	}
287 
288 	@Override
289 	public void reset() {
290 		coefs.clear();
291 		lits.clear();
292 		nbnewvar = 0;
293 		super.reset();
294 	}
295 
296 	public boolean hasNoObjectiveFunction() {
297 		return false;
298 	}
299 
300 	public boolean nonOptimalMeansSatisfiable() {
301 		return false;
302 	}
303 
304 	private int counter;
305 
306 	public Number calculateObjective() {
307 		counter = 0;
308 		for (int q : prevfullmodel) {
309 			int index = lits.containsAt(q);
310 			if (index != -1) {
311 				counter += coefs.get(index).intValue();
312 			}
313 		}
314 		return falsifiedWeight + counter;
315 	}
316 
317 	private final IVecInt lits = new VecInt();
318 
319 	private final IVec<BigInteger> coefs = new Vec<BigInteger>();
320 
321 	private final ObjectiveFunction obj = new ObjectiveFunction(lits, coefs);
322 
323 	public void discard() throws ContradictionException {
324 		assert lits.size() == coefs.size();
325 		super.addPseudoBoolean(lits, coefs, false, BigInteger
326 				.valueOf(counter - 1));
327 	}
328 
329 }