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 pseudo-Boolean instances
30   * 
31   * Copyright (c) 2005-2007 Olivier ROUSSEL and Vasco MANQUINHO
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.BufferedReader;
55  import java.io.IOException;
56  import java.io.LineNumberReader;
57  import java.io.PrintWriter;
58  import java.io.Serializable;
59  import java.math.BigInteger;
60  
61  import org.sat4j.core.Vec;
62  import org.sat4j.core.VecInt;
63  import org.sat4j.pb.IPBSolver;
64  import org.sat4j.pb.ObjectiveFunction;
65  import org.sat4j.reader.ParseFormatException;
66  import org.sat4j.reader.Reader;
67  import org.sat4j.specs.ContradictionException;
68  import org.sat4j.specs.IProblem;
69  import org.sat4j.specs.IVec;
70  import org.sat4j.specs.IVecInt;
71  
72  /**
73   * Based on the "Official" reader for the Pseudo Boolean evaluation 2005.
74   * http://www.cril.univ-artois.fr/PB05/parser/SimpleParser.java provided by
75   * Olivier Roussel and Vasco Manquinho.
76   * 
77   * Modified to comply with SAT4J architecture by Mederic Baron
78   * 
79   * Updated since then by Daniel Le Berre
80   * 
81   * @author or
82   * @author vm
83   * @author mederic baron
84   * @author leberre
85   */
86  public class OPBReader2005 extends Reader implements Serializable {
87  
88  	/**
89  	 * Comment for <code>serialVersionUID</code>
90  	 */
91  	private static final long serialVersionUID = 1L;
92  
93  	protected final IPBSolver solver;
94  
95  	private final IVecInt lits;
96  
97  	private final IVec<BigInteger> coeffs;
98  
99  	private BigInteger d;
100 
101 	private String operator;
102 
103 	private final IVecInt objectiveVars = new VecInt();
104 
105 	private final IVec<BigInteger> objectiveCoeffs = new Vec<BigInteger>();
106 
107 	// does the instance have an objective function?
108 	private boolean hasObjFunc = false;
109 
110 	// does the instance need variables explanation?
111 	protected boolean hasVariablesExplanation = false;
112 
113 	protected int nbVars, nbConstr; // MetaData: #Variables and #Constraints in
114 
115 	// file.
116 	protected int nbConstraintsRead;
117 
118 	/**
119 	 * callback called when we get the number of variables and the expected
120 	 * number of constraints
121 	 * 
122 	 * @param nbvar
123 	 *            the number of variables
124 	 * @param nbconstr
125 	 *            the number of contraints
126 	 */
127 	protected void metaData(int nbvar, int nbconstr) {
128 		solver.newVar(nbvar);
129 	}
130 
131 	/**
132 	 * callback called before we read the objective function
133 	 */
134 	protected void beginObjective() {
135 	}
136 
137 	/**
138 	 * callback called after we've read the objective function
139 	 */
140 	protected void endObjective() {
141 		assert lits.size() == coeffs.size();
142 		assert lits.size() == coeffs.size();
143 		for (int i = 0; i < lits.size(); i++) {
144 			objectiveVars.push(lits.get(i));
145 			objectiveCoeffs.push(coeffs.get(i));
146 		}
147 	}
148 
149 	/**
150 	 * callback called before we read a constraint
151 	 */
152 	protected void beginConstraint() {
153 		lits.clear();
154 		coeffs.clear();
155 		assert lits.size() == 0;
156 		assert coeffs.size() == 0;
157 	}
158 
159 	/**
160 	 * callback called after we've read a constraint
161 	 */
162 	/**
163 	 * @throws ContradictionException
164 	 */
165 	protected void endConstraint() throws ContradictionException {
166 
167 		assert !(lits.size() == 0);
168 		assert !(coeffs.size() == 0);
169 		assert lits.size() == coeffs.size();
170 
171 		if ("<=".equals(operator) || "=".equals(operator)) {
172 			solver.addPseudoBoolean(lits, coeffs, false, d);
173 		}
174 		if (">=".equals(operator) || "=".equals(operator)) {
175 			solver.addPseudoBoolean(lits, coeffs, true, d);
176 		}
177 		nbConstraintsRead++;
178 	}
179 
180 	/**
181 	 * callback called when we read a term of a constraint
182 	 * 
183 	 * @param coeff
184 	 *            the coefficient of the term
185 	 * @param var
186 	 *            the identifier of the variable
187 	 */
188 	private void constraintTerm(BigInteger coeff, String var) {
189 		coeffs.push(coeff);
190 		lits.push(translateVarToId(var));
191 	}
192 
193 	protected int translateVarToId(String var) {
194 		int id = Integer.parseInt(var.substring(1));
195 		return ((savedChar == '-') ? -1 : 1) * id;
196 	}
197 
198 	/**
199 	 * callback called when we read the relational operator of a constraint
200 	 * 
201 	 * @param relop
202 	 *            the relational oerator (>= or =)
203 	 */
204 	protected void constraintRelOp(String relop) {
205 		operator = relop;
206 	}
207 
208 	/**
209 	 * callback called when we read the right term of a constraint (also known
210 	 * as the degree)
211 	 * 
212 	 * @param val
213 	 *            the degree of the constraint
214 	 */
215 	protected void constraintRightTerm(BigInteger val) {
216 		d = val;
217 	}
218 
219 	transient BufferedReader in; // the stream we're reading from
220 
221 	char savedChar; // a character read from the file but not yet consumed
222 
223 	boolean charAvailable = false; // true iff savedChar contains a character
224 
225 	boolean eofReached = false; // true iff we've reached EOF
226 
227 	private boolean eolReached = false;
228 
229 	/**
230 	 * get the next character from the stream
231 	 * 
232 	 * @throws IOException
233 	 */
234 	protected char get() throws IOException {
235 		int c;
236 
237 		if (charAvailable) {
238 			charAvailable = false;
239 			return savedChar;
240 		}
241 
242 		c = in.read();
243 		if (c == -1)
244 			eofReached = true;
245 		if (c == '\n' || c == '\r') {
246 			eolReached = true;
247 		} else {
248 			eolReached = false;
249 		}
250 		return (char) c;
251 	}
252 
253 	public IVecInt getVars() {
254 		return objectiveVars;
255 	}
256 
257 	public IVec<BigInteger> getCoeffs() {
258 		return objectiveCoeffs;
259 	}
260 
261 	/**
262 	 * put back a character into the stream (only one chr can be put back)
263 	 */
264 	protected void putback(char c) {
265 		savedChar = c;
266 		charAvailable = true;
267 	}
268 
269 	/**
270 	 * return true iff we've reached EOF
271 	 */
272 	protected boolean eof() {
273 		return eofReached;
274 	}
275 
276 	protected boolean eol() {
277 		return eolReached;
278 	}
279 
280 	/**
281 	 * skip white spaces
282 	 * 
283 	 * @throws IOException
284 	 */
285 	protected void skipSpaces() throws IOException {
286 		char c;
287 
288 		while (Character.isWhitespace(c = get()))
289 			;
290 
291 		putback(c);
292 	}
293 
294 	/**
295 	 * read a word from file
296 	 * 
297 	 * @return the word we read
298 	 * @throws IOException
299 	 */
300 	public String readWord() throws IOException {
301 		StringBuffer s = new StringBuffer();
302 		char c;
303 
304 		skipSpaces();
305 
306 		while (!Character.isWhitespace(c = get()) && !eol() && !eof())
307 			s.append(c);
308 
309 		putback(c);
310 		return s.toString();
311 	}
312 
313 	/**
314 	 * read a integer from file
315 	 * 
316 	 * @param s
317 	 *            a StringBuffer to store the integer that was read
318 	 * @throws IOException
319 	 */
320 	public void readInteger(StringBuffer s) throws IOException {
321 		char c;
322 
323 		skipSpaces();
324 		s.setLength(0);
325 
326 		c = get();
327 		if (c == '-' || Character.isDigit(c))
328 			s.append(c);
329 		// note: BigInteger don't like a '+' before the number, we just skip it
330 
331 		while (Character.isDigit(c = get()) && !eol() && !eof())
332 			s.append(c);
333 
334 		putback(c);
335 	}
336 
337 	/**
338 	 * read an identifier from stream and store it in s
339 	 * 
340 	 * @return the identifier we read or null
341 	 * @throws IOException
342 	 * @throws ParseFormatException
343 	 */
344 	protected boolean readIdentifier(StringBuffer s) throws IOException,
345 			ParseFormatException {
346 		char c;
347 
348 		s.setLength(0);
349 
350 		skipSpaces();
351 
352 		// first char (must be a letter or underscore)
353 		c = get();
354 		if (eof())
355 			return false;
356 
357 		if (!isGoodFirstCharacter(c)) {
358 			putback(c);
359 			return false;
360 		}
361 
362 		s.append(c);
363 
364 		// next chars (must be a letter, a digit or an underscore)
365 		while (true) {
366 			c = get();
367 			if (eof())
368 				break;
369 
370 			if (isGoodFollowingCharacter(c))
371 				s.append(c);
372 			else {
373 				putback(c);
374 				break;
375 			}
376 		}
377 		checkId(s);
378 		return true;
379 	}
380 
381 	protected boolean isGoodFirstCharacter(char c) {
382 		return Character.isLetter(c) || c == '_';
383 	}
384 
385 	protected boolean isGoodFollowingCharacter(char c) {
386 		return Character.isLetter(c) || Character.isDigit(c) || c == '_';
387 	}
388 
389 	protected void checkId(StringBuffer s) throws ParseFormatException {
390 		// Small check on the coefficient ID to make sure everything is ok
391 		int varID = Integer.parseInt(s.substring(1));
392 		if (varID > nbVars) {
393 			throw new ParseFormatException(
394 					"Variable identifier larger than #variables in metadata.");
395 		}
396 	}
397 
398 	/**
399 	 * read a relational operator from stream and store it in s
400 	 * 
401 	 * @return the relational operator we read or null
402 	 * @throws IOException
403 	 */
404 	private String readRelOp() throws IOException {
405 		char c;
406 
407 		skipSpaces();
408 
409 		c = get();
410 		if (eof())
411 			return null;
412 
413 		if (c == '=')
414 			return "=";
415 
416 		char following = get();
417 		if (c == '>' && following == '=')
418 			return ">=";
419 		if (c == '<' && following == '=')
420 			return "<=";
421 
422 		return null;
423 	}
424 
425 	/**
426 	 * read the first comment line to get the number of variables and the number
427 	 * of constraints in the file calls metaData with the data that was read
428 	 * 
429 	 * @throws IOException
430 	 * @throws ParseException
431 	 */
432 	protected void readMetaData() throws IOException, ParseFormatException {
433 		char c;
434 		String s;
435 
436 		// get the number of variables and constraints
437 		c = get();
438 		if (c != '*')
439 			throw new ParseFormatException(
440 					"First line of input file should be a comment");
441 
442 		s = readWord();
443 		if (eof() || !"#variable=".equals(s))
444 			throw new ParseFormatException(
445 					"First line should contain #variable= as first keyword");
446 
447 		nbVars = Integer.parseInt(readWord());
448 
449 		s = readWord();
450 		if (eof() || !"#constraint=".equals(s))
451 			throw new ParseFormatException(
452 					"First line should contain #constraint= as second keyword");
453 
454 		nbConstr = Integer.parseInt(readWord());
455 
456 		// skip the rest of the line
457 		in.readLine();
458 
459 		// callback to transmit the data
460 		metaData(nbVars, nbConstr);
461 	}
462 
463 	/**
464 	 * skip the comments at the beginning of the file
465 	 * 
466 	 * @throws IOException
467 	 */
468 	private void skipComments() throws IOException {
469 		char c = ' ';
470 
471 		// skip further comments
472 
473 		while (!eof() && (c = get()) == '*') {
474 			in.readLine();
475 		}
476 
477 		putback(c);
478 	}
479 
480 	/**
481 	 * read a term into coeff and var
482 	 * 
483 	 * @param coeff
484 	 *            the coefficient of the variable
485 	 * @param var
486 	 *            the identifier we read
487 	 * @throws IOException
488 	 * @throws ParseException
489 	 */
490 	protected void readTerm(StringBuffer coeff, StringBuffer var)
491 			throws IOException, ParseFormatException {
492 		char c;
493 
494 		readInteger(coeff);
495 
496 		skipSpaces();
497 		c = get();
498 		if (c != '*')
499 			throw new ParseFormatException(
500 					"'*' expected between a coefficient and a variable");
501 
502 		if (!readIdentifier(var))
503 			throw new ParseFormatException("identifier expected");
504 	}
505 
506 	/**
507 	 * @throws IOException 
508 	 * @throws ParseFormatException  
509 	 */
510 	protected void readVariablesExplanation() throws IOException,
511 			ParseFormatException {
512 	}
513 
514 	/**
515 	 * read the objective line (if any) calls beginObjective, objectiveTerm and
516 	 * endObjective
517 	 * 
518 	 * @throws IOException
519 	 * @throws ParseException
520 	 */
521 	private void readObjective() throws IOException, ParseFormatException {
522 		char c;
523 		StringBuffer var = new StringBuffer();
524 		StringBuffer coeff = new StringBuffer();
525 
526 		// read objective line (if any)
527 
528 		skipSpaces();
529 		c = get();
530 		if (c != 'm') {
531 			// no objective line
532 			putback(c);
533 			return;
534 		}
535 
536 		hasObjFunc = true;
537 		if (get() == 'i' && get() == 'n' && get() == ':') {
538 			beginObjective(); // callback
539 
540 			while (!eof()) {
541 				readTerm(coeff, var);
542 				constraintTerm(new BigInteger(coeff.toString()), var.toString()); // callback
543 
544 				skipSpaces();
545 				c = get();
546 				if (c == ';')
547 					break; // end of objective
548 
549 				else if (c == '-' || c == '+' || Character.isDigit(c))
550 					putback(c);
551 				else
552 					throw new ParseFormatException(
553 							"unexpected character in objective function");
554 			}
555 
556 			endObjective();
557 		} else
558 			throw new ParseFormatException(
559 					"input format error: 'min:' expected");
560 	}
561 
562 	/**
563 	 * read a constraint calls beginConstraint, constraintTerm and endConstraint
564 	 * 
565 	 * @throws ParseException
566 	 * @throws IOException
567 	 * @throws ContradictionException
568 	 */
569 	private void readConstraint() throws IOException, ParseFormatException,
570 			ContradictionException {
571 		StringBuffer var = new StringBuffer();
572 		StringBuffer coeff = new StringBuffer();
573 		char c;
574 
575 		beginConstraint();
576 
577 		while (!eof()) {
578 			readTerm(coeff, var);
579 			constraintTerm(new BigInteger(coeff.toString()), var.toString());
580 
581 			skipSpaces();
582 			c = get();
583 			if (c == '>' || c == '=' || c == '<') {
584 				// relational operator found
585 				putback(c);
586 				break;
587 			} else if (c == '-' || c == '+' || Character.isDigit(c))
588 				putback(c);
589 			else {
590 				throw new ParseFormatException(
591 						"unexpected character in constraint");
592 			}
593 		}
594 
595 		if (eof())
596 			throw new ParseFormatException(
597 					"unexpected EOF before end of constraint");
598 
599 		String relop;
600 		if ((relop = readRelOp()) == null) {
601 			throw new ParseFormatException(
602 					"unexpected relational operator in constraint");
603 
604 		}
605 		constraintRelOp(relop);
606 		readInteger(coeff);
607 		constraintRightTerm(new BigInteger(coeff.toString()));
608 
609 		skipSpaces();
610 		c = get();
611 		if (eof() || c != ';')
612 			throw new ParseFormatException(
613 					"semicolon expected at end of constraint");
614 
615 		endConstraint();
616 	}
617 
618 	public OPBReader2005(IPBSolver solver) {
619 		this.solver = solver;
620 		lits = new VecInt();
621 		coeffs = new Vec<BigInteger>();
622 	}
623 
624 	/**
625 	 * parses the file and uses the callbacks to send to send the data back to
626 	 * the program
627 	 * 
628 	 * @throws IOException
629 	 * @throws ParseException
630 	 * @throws ContradictionException
631 	 */
632 	public void parse() throws IOException, ParseFormatException,
633 			ContradictionException {
634 		readMetaData();
635 
636 		skipComments();
637 
638 		readObjective();
639 
640 		readVariablesExplanation();
641 
642 		// read constraints
643 		nbConstraintsRead = 0;
644 		char c;
645 		while (!eof()) {
646 			skipSpaces();
647 			if (eof())
648 				break;
649 
650 			c = get();
651 			putback(c);
652 			if (c == '*')
653 				skipComments();
654 
655 			if (eof())
656 				break;
657 
658 			readConstraint();
659 			// nbConstraintsRead++;
660 		}
661 		// Small check on the number of constraints
662 		if (nbConstraintsRead != nbConstr) {
663 			throw new ParseFormatException(
664 					"Number of constraints read ("+nbConstraintsRead+") is different from metadata ("+nbConstr+")");
665 		}
666 	}
667 
668 	@Override
669 	public final IProblem parseInstance(final java.io.Reader input)
670 			throws ParseFormatException, ContradictionException {
671 		IProblem problem = parseInstance(new LineNumberReader(input));
672 		solver.setObjectiveFunction(getObjectiveFunction());
673 		return problem;
674 	}
675 
676 	private IProblem parseInstance(LineNumberReader input)
677 			throws ParseFormatException, ContradictionException {
678 		solver.reset();
679 		this.in = input;
680 		try {
681 			parse();
682 			return solver;
683 		} catch (IOException e) {
684 			throw new ParseFormatException(e);
685 		}
686 	}
687 
688 	@Override
689 	public String decode(int[] model) {
690 		StringBuffer stb = new StringBuffer();
691 
692 		for (int i = 0; i < model.length; i++) {
693 			if (model[i] < 0) {
694 				stb.append("-x");
695 				stb.append(-model[i]);
696 			} else {
697 				stb.append("x");
698 				stb.append(model[i]);
699 			}
700 			stb.append(" ");
701 		}
702 		return stb.toString();
703 	}
704 
705 	@Override
706 	public void decode(int[] model, PrintWriter out) {
707 		for (int i = 0; i < model.length; i++) {
708 			if (model[i] < 0) {
709 				out.print("-x");
710 				out.print(-model[i]);
711 			} else {
712 				out.print("x");
713 				out.print(model[i]);
714 			}
715 			out.print(" ");
716 		}
717 	}
718 
719 	public ObjectiveFunction getObjectiveFunction() {
720 		if (hasObjFunc) {
721 			return new ObjectiveFunction(getVars(), getCoeffs());
722 		}
723 		return null;
724 	}
725 
726 	public IVecInt getListOfVariables() {
727 		return null;
728 	}
729 
730 }