View Javadoc

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.pb;
31  
32  import java.io.IOException;
33  import java.io.InputStream;
34  import java.io.InputStreamReader;
35  import java.io.LineNumberReader;
36  import java.io.PrintWriter;
37  import java.io.Serializable;
38  import java.math.BigInteger;
39  import java.util.HashMap;
40  import java.util.Map;
41  import java.util.Scanner;
42  
43  import org.sat4j.core.Vec;
44  import org.sat4j.core.VecInt;
45  import org.sat4j.reader.ParseFormatException;
46  import org.sat4j.reader.Reader;
47  import org.sat4j.specs.ContradictionException;
48  import org.sat4j.specs.IProblem;
49  import org.sat4j.specs.IVec;
50  import org.sat4j.specs.IVecInt;
51  
52  /**
53   * This class is a quick hack to read opb formatted files. The reader skip
54   * commented lines (beginning with COMMENT_SYMBOL) and expect constraints of the
55   * form: [name :] [[+|-]COEF] [*] [+|-]LIT >=|<=|= DEGREE where COEF and DEGREE
56   * are plain integer and LIT is an identifier.
57   * 
58   * @author leberre
59   */
60  public class GoodOPBReader extends Reader implements Serializable {
61  
62      /**
63       * Comment for <code>serialVersionUID</code>
64       */
65      private static final long serialVersionUID = 1L;
66  
67      private static final String COMMENT_SYMBOL = "*";
68  
69      private final IPBSolver solver;
70  
71      private final Map<String, Integer> map = new HashMap<String, Integer>();
72  
73      private final IVec<String> decode = new Vec<String>();
74  
75      /**
76       * 
77       */
78      public GoodOPBReader(IPBSolver solver) {
79          this.solver = solver;
80      }
81  
82      @Override
83      public final IProblem parseInstance(final java.io.Reader in)
84              throws ParseFormatException, ContradictionException, IOException {
85          return parseInstance(new LineNumberReader(in));
86      }
87  
88      private IProblem parseInstance(LineNumberReader in)
89              throws ContradictionException, IOException {
90          this.solver.reset();
91          String line;
92          while ((line = in.readLine()) != null) {
93              // cannot trim is line is null
94              line = line.trim();
95              if (line.endsWith(";")) {
96                  line = line.substring(0, line.length() - 1);
97              }
98              parseLine(line);
99          }
100         return this.solver;
101     }
102 
103     void parseLine(String line) throws ContradictionException {
104         // Skip commented line
105         if (line.startsWith(COMMENT_SYMBOL)) {
106             return;
107         }
108         if (line.startsWith("p")) {
109             return;
110         }
111         if (line.startsWith("min:") || line.startsWith("min :")) {
112             return; // we will use that case later
113         }
114         if (line.startsWith("max:") || line.startsWith("max :")) {
115             return; // we will use that case later
116         }
117 
118         // skip name of constraints:
119         int index = line.indexOf(":");
120         if (index != -1) {
121             line = line.substring(index + 1);
122         }
123 
124         IVecInt lits = new VecInt();
125         IVec<BigInteger> coeffs = new Vec<BigInteger>();
126         Scanner stk = new Scanner(line)
127                 .useDelimiter("\\s*\\*\\s*|\\s*\\+\\s*|\\s+");
128         while (stk.hasNext()) {
129             String token = stk.next();
130             if (">=".equals(token) || "<=".equals(token) || "=".equals(token)) {
131                 assert stk.hasNext();
132                 String tok = stk.next();
133                 // we need to remove + from the integer
134                 if (tok.startsWith("+")) {
135                     tok = tok.substring(1);
136                 }
137                 BigInteger d = new BigInteger(tok);
138 
139                 try {
140                     if (">=".equals(token) || "=".equals(token)) {
141                         this.solver.addPseudoBoolean(lits, coeffs, true, d);
142                     }
143                     if ("<=".equals(token) || "=".equals(token)) {
144                         this.solver.addPseudoBoolean(lits, coeffs, false, d);
145                     }
146                 } catch (ContradictionException ce) {
147                     throw ce;
148                 }
149             } else {
150                 // on est toujours en train de lire la partie gauche de la
151                 // contrainte
152                 if ("+".equals(token)) {
153                     assert stk.hasNext();
154                     token = stk.next();
155                 } else if ("-".equals(token)) {
156                     assert stk.hasNext();
157                     token = token + stk.next();
158                 }
159                 BigInteger coef;
160                 // should contain a coef and a literal
161                 try {
162                     // we need to remove + from the integer
163                     if (token.startsWith("+")) {
164                         token = token.substring(1);
165                     }
166                     coef = new BigInteger(token);
167                     assert stk.hasNext();
168                     token = stk.next();
169                 } catch (NumberFormatException nfe) {
170                     // its only an identifier
171                     coef = BigInteger.ONE;
172                 }
173                 if ("-".equals(token) || "~".equals(token)) {
174                     assert stk.hasNext();
175                     token = token + stk.next();
176                 }
177                 boolean negative = false;
178                 if (token.startsWith("+")) {
179                     token = token.substring(1);
180                 } else if (token.startsWith("-")) {
181                     token = token.substring(1);
182                     assert coef.equals(BigInteger.ONE);
183                     coef = BigInteger.ONE.negate();
184                 } else if (token.startsWith("~")) {
185                     token = token.substring(1);
186                     negative = true;
187                 }
188                 Integer id = this.map.get(token);
189                 if (id == null) {
190                     id = this.decode.size() + 1;
191                     this.map.put(token, id);
192                     this.decode.push(token);
193                 }
194                 coeffs.push(coef);
195                 int lid = (negative ? -1 : 1) * id.intValue();
196                 lits.push(lid);
197                 assert coeffs.size() == lits.size();
198             }
199         }
200     }
201 
202     @Override
203     public String decode(int[] model) {
204         StringBuffer stb = new StringBuffer();
205         for (int i = 0; i < model.length; i++) {
206             if (model[i] < 0) {
207                 stb.append("-");
208                 stb.append(this.decode.get(-model[i] - 1));
209             } else {
210                 stb.append(this.decode.get(model[i] - 1));
211             }
212             stb.append(" ");
213         }
214         return stb.toString();
215     }
216 
217     @Override
218     public void decode(int[] model, PrintWriter out) {
219         for (int i = 0; i < model.length; i++) {
220             if (model[i] < 0) {
221                 out.print("-");
222                 out.print(this.decode.get(-model[i] - 1));
223             } else {
224                 out.print(this.decode.get(model[i] - 1));
225             }
226             out.print(" ");
227         }
228     }
229 
230     @Override
231     public IProblem parseInstance(final InputStream in)
232             throws ParseFormatException, ContradictionException, IOException {
233         return parseInstance(new InputStreamReader(in));
234     }
235 }