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 }
108 int varID = Integer.parseInt(s.substring(cpt));
109 if (varID > this.nbVars) {
110 throw new ParseFormatException(
111 "Variable identifier larger than #variables in metadata.");
112 }
113 }
114
115 /**
116 * contains the number of new symbols generated to linearize products
117 */
118 protected int nbNewSymbols;
119
120 @Override
121 protected void readTerm(StringBuffer coeff, StringBuffer var)
122 throws IOException, ParseFormatException {
123 readInteger(coeff);
124
125 skipSpaces();
126
127 var.setLength(0);
128 IVec<String> tmpLit = new Vec<String>();
129 StringBuffer tmpVar = new StringBuffer();
130 while (readIdentifier(tmpVar)) {
131 tmpLit = tmpLit.push(tmpVar.toString());
132 skipSpaces();
133 }
134 if (tmpLit.size() == 0) {
135 throw new ParseFormatException("identifier expected");
136 }
137 if (tmpLit.size() == 1) {
138 // it is a "normal" term
139 var.append(tmpLit.last());
140 tmpLit.pop();
141 } else {
142 // it is a product term
143 try {
144 var.append(linearizeProduct(tmpLit));
145 } catch (ContradictionException e) {
146 throw new ParseFormatException(e);
147 }
148 }
149 }
150
151 /**
152 * callback called when we read a term of a constraint
153 *
154 * @param var
155 * the identifier of the variable
156 * @param lits
157 * a set of literals in DIMACS format in which var once
158 * translated will be added.
159 * @throws ParseFormatException
160 */
161 protected void literalInAProduct(String var, IVecInt lits)
162 throws ParseFormatException {
163 int beginning = var.charAt(0) == '~' ? 2 : 1;
164 int id = Integer.parseInt(var.substring(beginning));
165 int lid = (var.charAt(0) == '~' ? -1 : 1) * id;
166 if (lid == 0 || Math.abs(lid) >= this.nbNewSymbols) {
167 throw new ParseFormatException("Wrong variable id");
168 }
169 lits.push(lid);
170 }
171
172 /**
173 * callback called when we read a term of a constraint
174 *
175 * @param var
176 * the identifier of the variable
177 * @param lits
178 * a set of literals in DIMACS format in which var once
179 * translated will be added.
180 */
181 protected void negateLiteralInAProduct(String var, IVecInt lits) {
182 int beginning = var.charAt(0) == '~' ? 2 : 1;
183 int id = Integer.parseInt(var.substring(beginning));
184 int lid = (var.charAt(0) == '~' ? 1 : -1) * id;
185 lits.push(lid);
186 }
187
188 /**
189 * read the first comment line to get the number of variables and the number
190 * of constraints in the file calls metaData with the data that was read
191 *
192 * @throws IOException
193 * @throws ParseException
194 */
195 @Override
196 protected void readMetaData() throws IOException, ParseFormatException {
197 char c;
198 String s;
199
200 // get the number of variables and constraints
201 c = get();
202 if (c != '*') {
203 throw new ParseFormatException(
204 "First line of input file should be a comment");
205 }
206 s = readWord();
207 if (eof() || !"#variable=".equals(s)) {
208 throw new ParseFormatException(
209 "First line should contain #variable= as first keyword");
210 }
211
212 this.nbVars = Integer.parseInt(readWord());
213 this.nbNewSymbols = this.nbVars + 1;
214
215 s = readWord();
216 if (eof() || !"#constraint=".equals(s)) {
217 throw new ParseFormatException(
218 "First line should contain #constraint= as second keyword");
219 }
220
221 this.nbConstr = Integer.parseInt(readWord());
222 this.charAvailable = false;
223 if (!eol()) {
224 String rest = this.in.readLine();
225
226 if (rest != null && rest.indexOf("#product=") != -1) {
227 String[] splitted = rest.trim().split(" ");
228 if (splitted[0].equals("#product=")) {
229 Integer.parseInt(splitted[1]);
230 }
231
232 // if (splitted[2].equals("sizeproduct="))
233 // readWord();
234
235 }
236 }
237 // callback to transmit the data
238 metaData(this.nbVars, this.nbConstr);
239 }
240
241 @Override
242 protected int translateVarToId(String var) throws ParseFormatException {
243 int beginning = var.charAt(0) == '~' ? 2 : 1;
244 int id = Integer.parseInt(var.substring(beginning));
245 if (id == 0 || id >= this.nbNewSymbols) {
246 throw new ParseFormatException("Wrong variable id format: " + var);
247 }
248 return (var.charAt(0) == '~' ? -1 : 1) * id;
249 }
250
251 private String linearizeProduct(IVec<String> tmpLit)
252 throws ContradictionException, ParseFormatException {
253 tmpLit.sort(String.CASE_INSENSITIVE_ORDER);
254 String newVar = getProductVariable(tmpLit);
255 if (newVar == null) {
256 // generate a new symbol
257 newVar = "X" + this.nbNewSymbols++;
258 // linearization proposed by O. Roussel (PB07)
259 // generate the clause
260 // product => newSymbol (this is a clause)
261 // not x1 or not x2 ... or not xn or newSymbol
262 if (tmpLit.size() == 2) {
263 Map<String, String> map1 = this.binaryProductToVar.get(tmpLit
264 .get(0));
265 if (map1 == null) {
266 map1 = new HashMap<String, String>();
267 this.binaryProductToVar.put(tmpLit.get(0), map1);
268 }
269 map1.put(tmpLit.get(1), newVar);
270 }
271 this.varToProduct.put(newVar, tmpLit);
272 IVecInt newLits = new VecInt();
273 for (Iterator<String> iterator = tmpLit.iterator(); iterator
274 .hasNext();) {
275 negateLiteralInAProduct(iterator.next(), newLits);
276 }
277 literalInAProduct(newVar, newLits);
278 this.solver.addClause(newLits);
279 // generate the PB-constraint
280 // newSymbol => product translated as
281 // x1+x2+x3...+xn-n*newSymbol>=0
282 newLits.clear();
283 IVec<BigInteger> newCoefs = new Vec<BigInteger>();
284 for (Iterator<String> iterator = tmpLit.iterator(); iterator
285 .hasNext();) {
286 literalInAProduct(iterator.next(), newLits);
287 newCoefs.push(BigInteger.ONE);
288 }
289 literalInAProduct(newVar, newLits);
290 newCoefs.push(new BigInteger(String.valueOf(-tmpLit.size())));
291 this.solver.addPseudoBoolean(newLits, newCoefs, true,
292 BigInteger.ZERO);
293 // nbConstraintsRead += 2;
294 }
295 return newVar;
296 }
297
298 private final Map<String, IVec<String>> varToProduct = new HashMap<String, IVec<String>>();
299
300 private final Map<String, Map<String, String>> binaryProductToVar = new HashMap<String, Map<String, String>>();
301
302 private String getProductVariable(IVec<String> lits) {
303 if (lits.size() == 2) {
304 Map<String, String> map = this.binaryProductToVar.get(lits.get(0));
305 if (map == null) {
306 return null;
307 }
308 return map.get(lits.get(1));
309 }
310 for (Map.Entry<String, IVec<String>> c : this.varToProduct.entrySet()) {
311 if (c.getValue().equals(lits)) {
312 return c.getKey();
313 }
314 }
315 return null;
316 }
317
318 @Override
319 public String decode(int[] model) {
320 StringBuffer stb = new StringBuffer();
321 int p;
322 for (int element : model) {
323 p = element;
324 if (Math.abs(p) <= this.nbVars) {
325 if (p < 0) {
326 stb.append("-x");
327 stb.append(-p);
328 } else {
329 stb.append("x");
330 stb.append(p);
331 }
332 stb.append(" ");
333 }
334 }
335 return stb.toString();
336 }
337
338 @Override
339 public void decode(int[] model, PrintWriter out) {
340 int p;
341 for (int element : model) {
342 p = element;
343 if (Math.abs(p) <= this.nbVars) {
344 if (element < 0) {
345 out.print("-x");
346 out.print(-p);
347 } else {
348 out.print("x");
349 out.print(p);
350 }
351 out.print(" ");
352 }
353 }
354 }
355
356 }