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         }
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 }