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.reader;
31  
32  import java.io.BufferedReader;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.InputStreamReader;
36  import java.io.PrintWriter;
37  import java.io.StringWriter;
38  import java.util.regex.Matcher;
39  import java.util.regex.Pattern;
40  
41  import org.sat4j.core.VecInt;
42  import org.sat4j.specs.ContradictionException;
43  import org.sat4j.specs.IProblem;
44  import org.sat4j.specs.ISolver;
45  import org.sat4j.specs.IVecInt;
46  
47  /**
48   * Simple JSON reader for clauses and cardinality constraints.
49   * 
50   * Clauses are represented as an array of Dimacs literals (non zero integers).
51   * Cardinality constraints are represented like a clause for its left hand side,
52   * a comparator (a string) and a number.
53   * <code>[[-1,-2,-3],[[1,-2,3],'>',2],[4,-3,6]]</code> for instance represents
54   * three constraints, two clauses and the cardinality constraint x1 + not x2 +
55   * x3 > 2.
56   * 
57   * @author leberre
58   * 
59   * @param <S>
60   *            the type of solver to feed.
61   * @since 2.3.3
62   */
63  public class JSONReader<S extends ISolver> extends Reader {
64  
65      protected final S solver;
66  
67      public static final String CLAUSE = "(\\[(-?(\\d+)(,-?(\\d+))*)?\\])";
68  
69      public static final String CARD = "(\\[" + CLAUSE + ",'[=<>]=?',-?\\d+\\])";
70  
71      public final String constraint;
72  
73      public final String formula;
74  
75      private static final Pattern CLAUSE_PATTERN = Pattern.compile(CLAUSE);
76  
77      private static final Pattern CARD_PATTERN = Pattern.compile(CARD);
78  
79      private final Pattern constraintPattern;
80  
81      public JSONReader(S solver) {
82          this.solver = solver;
83          constraint = constraintRegexp();
84          formula = "^\\[(" + constraint + "(," + constraint + ")*)?\\]$";
85          constraintPattern = Pattern.compile(constraint);
86      }
87  
88      protected String constraintRegexp() {
89          return "(" + CLAUSE + "|" + CARD + ")";
90      }
91  
92      private void handleConstraint(String constraint)
93              throws ParseFormatException, ContradictionException {
94          if (CARD_PATTERN.matcher(constraint).matches()) {
95              handleCard(constraint);
96          } else if (CLAUSE_PATTERN.matcher(constraint).matches()) {
97              handleClause(constraint);
98          } else {
99              handleNotHandled(constraint);
100         }
101     }
102 
103     protected void handleNotHandled(String constraint)
104             throws ParseFormatException, ContradictionException {
105         throw new ParseFormatException("Unknown constraint: " + constraint);
106     }
107 
108     private void handleClause(String constraint) throws ParseFormatException,
109             ContradictionException {
110         solver.addClause(getLiterals(constraint));
111     }
112 
113     protected IVecInt getLiterals(String constraint)
114             throws ParseFormatException {
115         String trimmed = constraint.trim();
116         trimmed = trimmed.substring(1, trimmed.length() - 1);
117         String[] literals = trimmed.split(",");
118         IVecInt clause = new VecInt();
119         for (String literal : literals) {
120             if (literal.length() > 0)
121                 clause.push(Integer.valueOf(literal.trim()));
122         }
123         return clause;
124     }
125 
126     protected void handleCard(String constraint) throws ParseFormatException,
127             ContradictionException {
128         String trimmed = constraint.trim();
129         trimmed = trimmed.substring(1, trimmed.length() - 1);
130         Matcher matcher = CLAUSE_PATTERN.matcher(trimmed);
131         if (matcher.find()) {
132             IVecInt clause = getLiterals(matcher.group());
133             trimmed = matcher.replaceFirst("");
134             String[] str = trimmed.split(",");
135 
136             int degree = Integer.valueOf(str[2]);
137             String comparator = str[1].substring(1, str[1].length() - 1);
138             if ("=".equals(comparator) || ("==".equals(comparator))) {
139                 solver.addExactly(clause, degree);
140             } else if ("<=".equals(comparator)) {
141                 solver.addAtMost(clause, degree);
142             } else if ("<".equals(comparator)) {
143                 solver.addAtMost(clause, degree - 1);
144             } else if (">=".equals(comparator)) {
145                 solver.addAtLeast(clause, degree);
146             } else if (">".equals(comparator)) {
147                 solver.addAtLeast(clause, degree + 1);
148             }
149         }
150     }
151 
152     @Override
153     public IProblem parseInstance(InputStream in) throws ParseFormatException,
154             ContradictionException, IOException {
155         StringWriter out = new StringWriter();
156         BufferedReader reader = new BufferedReader(new InputStreamReader(in));
157         String line;
158         while ((line = reader.readLine()) != null) {
159             out.append(line);
160         }
161         return parseString(out.toString());
162     }
163 
164     public ISolver parseString(String json) throws ParseFormatException,
165             ContradictionException {
166         String trimmed = json.trim();
167         if (!trimmed.matches(formula)) {
168             throw new ParseFormatException("Wrong input " + json);
169         }
170         Matcher matcher = constraintPattern.matcher(trimmed);
171         while (matcher.find()) {
172             handleConstraint(matcher.group());
173         }
174         return solver;
175     }
176 
177     @Override
178     @Deprecated
179     public String decode(int[] model) {
180         return "[" + new VecInt(model) + "]";
181     }
182 
183     @Override
184     public void decode(int[] model, PrintWriter out) {
185         out.print("[");
186         out.print(new VecInt(model));
187         out.print("]");
188     }
189 
190 }