View Javadoc

1   /*******************************************************************************
2    * SAT4J: a SATisfiability library for Java Copyright (C) 2004-2008 Daniel Le Berre
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 pseudo boolean algorithms described in:
20   * A fast pseudo-Boolean constraint solver Chai, D.; Kuehlmann, A.
21   * Computer-Aided Design of Integrated Circuits and Systems, IEEE Transactions on
22   * Volume 24, Issue 3, March 2005 Page(s): 305 - 317
23   * 
24   * and 
25   * Heidi E. Dixon, 2004. Automating Pseudo-Boolean Inference within a DPLL 
26   * Framework. Ph.D. Dissertation, University of Oregon.
27   *******************************************************************************/
28  /*=============================================================================
29   * parser for CSP instances represented in XML format
30   * 
31   * Copyright (c) 2006 Olivier ROUSSEL (olivier.roussel <at> cril.univ-artois.fr)
32   * 
33   * Permission is hereby granted, free of charge, to any person obtaining a copy
34   * of this software and associated documentation files (the "Software"), to deal
35   * in the Software without restriction, including without limitation the rights
36   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37   * copies of the Software, and to permit persons to whom the Software is
38   * furnished to do so, subject to the following conditions:
39   * 
40   * The above copyright notice and this permission notice shall be included in
41   * all copies or substantial portions of the Software.
42   * 
43   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49   * THE SOFTWARE.
50   *=============================================================================
51   */
52  package org.sat4j.pb.reader;
53  
54  import java.io.IOException;
55  import java.io.PrintWriter;
56  import java.math.BigInteger;
57  import java.util.HashMap;
58  import java.util.Iterator;
59  import java.util.Map;
60  
61  import org.sat4j.core.Vec;
62  import org.sat4j.core.VecInt;
63  import org.sat4j.pb.IPBSolver;
64  import org.sat4j.reader.ParseFormatException;
65  import org.sat4j.specs.ContradictionException;
66  import org.sat4j.specs.IVec;
67  import org.sat4j.specs.IVecInt;
68  
69  /**
70   * Reader complying with the PB07 input format.
71   * 
72   * Non-linear to linear translation adapted from the PB07 readers provided by
73   * Olivier Roussel and Vasco Manquinho (was available in C++, not in Java)
74   * 
75   * http://www.cril.univ-artois.fr/PB07/parser/SimpleParser.java
76   * http://www.cril.univ-artois.fr/PB07/parser/SimpleParser.cc
77   * 
78   * @author parrain
79   * @author daniel
80   * 
81   */
82  public class OPBReader2007 extends OPBReader2006 {
83  
84  	/**
85       * 
86       */
87  	private static final long serialVersionUID = 1L;
88  
89  	/**
90  	 * @param solver
91  	 */
92  	public OPBReader2007(IPBSolver solver) {
93  		super(solver);
94  	}
95  
96  	@Override
97  	protected boolean isGoodFirstCharacter(char c) {
98  		return Character.isLetter(c) || c == '_' || c == '~';
99  	}
100 
101 	@Override
102 	protected void checkId(StringBuffer s) throws ParseFormatException {
103 		// Small check on the coefficient ID to make sure everything is ok
104 		int cpt = 1;
105 		if (s.charAt(0) == '~')
106 			cpt = 2;
107 		int varID = Integer.parseInt(s.substring(cpt));
108 		if (varID > nbVars) {
109 			throw new ParseFormatException(
110 					"Variable identifier larger than #variables in metadata.");
111 		}
112 	}
113 
114 	/**
115 	 * contains the number of new symbols generated to linearize products
116 	 */
117 	protected int nbNewSymbols;
118 
119 	@Override
120 	protected void readTerm(StringBuffer coeff, StringBuffer var)
121 			throws IOException, ParseFormatException {
122 		readInteger(coeff);
123 
124 		skipSpaces();
125 
126 		var.setLength(0);
127 		IVec<String> tmpLit = new Vec<String>();
128 		StringBuffer tmpVar = new StringBuffer();
129 		while (readIdentifier(tmpVar)) {
130 			tmpLit = tmpLit.push(tmpVar.toString());
131 			skipSpaces();
132 		}
133 		if (tmpLit.size() == 0)
134 			throw new ParseFormatException("identifier expected");
135 		if (tmpLit.size() == 1) {
136 			// it is a "normal" term
137 			var.append(tmpLit.last());
138 			tmpLit.pop();
139 		} else {
140 			// it is a product term
141 			try {
142 				var.append(linearizeProduct(tmpLit));
143 			} catch (ContradictionException e) {
144 				throw new ParseFormatException(e);
145 			}
146 		}
147 	}
148 
149 	/**
150 	 * callback called when we read a term of a constraint
151 	 * 
152 	 * @param var
153 	 *            the identifier of the variable
154 	 * @param lits
155 	 *            a set of literals in DIMACS format in which var once
156 	 *            translated will be added.
157 	 * @throws ParseFormatException
158 	 */
159 	protected void literalInAProduct(String var, IVecInt lits)
160 			throws ParseFormatException {
161 		int beginning = ((var.charAt(0) == '~') ? 2 : 1);
162 		int id = Integer.parseInt(var.substring(beginning));
163 		int lid = ((var.charAt(0) == '~') ? -1 : 1) * id;
164 		if (lid == 0 || Math.abs(lid) >= nbNewSymbols) {
165 			throw new ParseFormatException("Wrong variable id");
166 		}
167 		lits.push(lid);
168 	}
169 
170 	/**
171 	 * callback called when we read a term of a constraint
172 	 * 
173 	 * @param var
174 	 *            the identifier of the variable
175 	 * @param lits
176 	 *            a set of literals in DIMACS format in which var once
177 	 *            translated will be added.
178 	 */
179 	protected void negateLiteralInAProduct(String var, IVecInt lits) {
180 		int beginning = ((var.charAt(0) == '~') ? 2 : 1);
181 		int id = Integer.parseInt(var.substring(beginning));
182 		int lid = ((var.charAt(0) == '~') ? 1 : -1) * id;
183 		lits.push(lid);
184 	}
185 
186 	/**
187 	 * read the first comment line to get the number of variables and the number
188 	 * of constraints in the file calls metaData with the data that was read
189 	 * 
190 	 * @throws IOException
191 	 * @throws ParseException
192 	 */
193 	@Override
194 	protected void readMetaData() throws IOException, ParseFormatException {
195 		char c;
196 		String s;
197 
198 		// get the number of variables and constraints
199 		c = get();
200 		if (c != '*')
201 			throw new ParseFormatException(
202 					"First line of input file should be a comment");
203 		s = readWord();
204 		if (eof() || !"#variable=".equals(s))
205 			throw new ParseFormatException(
206 					"First line should contain #variable= as first keyword");
207 
208 		nbVars = Integer.parseInt(readWord());
209 		nbNewSymbols = nbVars + 1;
210 
211 		s = readWord();
212 		if (eof() || !"#constraint=".equals(s))
213 			throw new ParseFormatException(
214 					"First line should contain #constraint= as second keyword");
215 
216 		nbConstr = Integer.parseInt(readWord());
217 		charAvailable = false;
218 		if (!eol()) {
219 			String rest = in.readLine();
220 
221 			if (rest != null && rest.indexOf("#product=") != -1) {
222 				String[] splitted = rest.trim().split(" ");
223 				if (splitted[0].equals("#product=")) {
224 					Integer.parseInt(splitted[1]);
225 				}
226 
227 				// if (splitted[2].equals("sizeproduct="))
228 				// readWord();
229 
230 			}
231 		}
232 		// callback to transmit the data
233 		metaData(nbVars, nbConstr);
234 	}
235 
236 	@Override
237 	protected int translateVarToId(String var) throws ParseFormatException {
238 		int beginning = ((var.charAt(0) == '~') ? 2 : 1);
239 		int id = Integer.parseInt(var.substring(beginning));
240 		if (id == 0 || id >= nbNewSymbols) {
241 			throw new ParseFormatException("Wrong variable id format: " + var);
242 		}
243 		return ((var.charAt(0) == '~') ? -1 : 1) * id;
244 	}
245 
246 	private String linearizeProduct(IVec<String> tmpLit)
247 			throws ContradictionException, ParseFormatException {
248 		tmpLit.sort(String.CASE_INSENSITIVE_ORDER);
249 		String newVar = getProductVariable(tmpLit);
250 		if (newVar == null) {
251 			// generate a new symbol
252 			newVar = "X" + nbNewSymbols++;
253 			// linearization proposed by O. Roussel (PB07)
254 			// generate the clause
255 			// product => newSymbol (this is a clause)
256 			// not x1 or not x2 ... or not xn or newSymbol
257 			if (tmpLit.size() == 2) {
258 				Map<String, String> map1 = binaryProductToVar
259 						.get(tmpLit.get(0));
260 				if (map1 == null) {
261 					map1 = new HashMap<String, String>();
262 					binaryProductToVar.put(tmpLit.get(0), map1);
263 				}
264 				map1.put(tmpLit.get(1), newVar);
265 			}
266 			varToProduct.put(newVar, tmpLit);
267 			IVecInt newLits = new VecInt();
268 			for (Iterator<String> iterator = tmpLit.iterator(); iterator
269 					.hasNext();)
270 				negateLiteralInAProduct(iterator.next(), newLits);
271 			literalInAProduct(newVar, newLits);
272 			solver.addClause(newLits);
273 			// generate the PB-constraint
274 			// newSymbol => product translated as
275 			// x1+x2+x3...+xn-n*newSymbol>=0
276 			newLits.clear();
277 			IVec<BigInteger> newCoefs = new Vec<BigInteger>();
278 			for (Iterator<String> iterator = tmpLit.iterator(); iterator
279 					.hasNext();) {
280 				literalInAProduct(iterator.next(), newLits);
281 				newCoefs.push(BigInteger.ONE);
282 			}
283 			literalInAProduct(newVar, newLits);
284 			newCoefs.push(new BigInteger(String.valueOf(-tmpLit.size())));
285 			solver.addPseudoBoolean(newLits, newCoefs, true, BigInteger.ZERO);
286 			// nbConstraintsRead += 2;
287 		}
288 		return newVar;
289 	}
290 
291 	private final Map<String, IVec<String>> varToProduct = new HashMap<String, IVec<String>>();
292 
293 	private final Map<String, Map<String, String>> binaryProductToVar = new HashMap<String, Map<String, String>>();
294 
295 	private String getProductVariable(IVec<String> lits) {
296 		if (lits.size() == 2) {
297 			Map<String, String> map = binaryProductToVar.get(lits.get(0));
298 			if (map == null)
299 				return null;
300 			return map.get(lits.get(1));
301 		}
302 		for (Map.Entry<String, IVec<String>> c : varToProduct.entrySet())
303 			if (c.getValue().equals(lits))
304 				return c.getKey();
305 		return null;
306 	}
307 
308 	@Override
309 	public String decode(int[] model) {
310 		StringBuffer stb = new StringBuffer();
311 		int p;
312 		for (int i = 0; i < model.length; i++) {
313 			p = model[i];
314 			if (Math.abs(p) <= nbVars) {
315 				if (p < 0) {
316 					stb.append("-x");
317 					stb.append(-p);
318 				} else {
319 					stb.append("x");
320 					stb.append(p);
321 				}
322 				stb.append(" ");
323 			}
324 		}
325 		return stb.toString();
326 	}
327 
328 	@Override
329 	public void decode(int[] model, PrintWriter out) {
330 		int p;
331 		for (int i = 0; i < model.length; i++) {
332 			p = model[i];
333 			if (Math.abs(p) <= nbVars) {
334 				if (model[i] < 0) {
335 					out.print("-x");
336 					out.print(-p);
337 				} else {
338 					out.print("x");
339 					out.print(p);
340 				}
341 				out.print(" ");
342 			}
343 		}
344 	}
345 
346 }