1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 package org.sat4j.pb.reader;
53
54 import java.io.BufferedReader;
55 import java.io.IOException;
56 import java.io.InputStream;
57 import java.io.InputStreamReader;
58 import java.io.LineNumberReader;
59 import java.io.PrintWriter;
60 import java.io.Serializable;
61 import java.math.BigInteger;
62
63 import org.sat4j.core.Vec;
64 import org.sat4j.core.VecInt;
65 import org.sat4j.pb.IPBSolver;
66 import org.sat4j.pb.ObjectiveFunction;
67 import org.sat4j.reader.ParseFormatException;
68 import org.sat4j.reader.Reader;
69 import org.sat4j.specs.ContradictionException;
70 import org.sat4j.specs.IProblem;
71 import org.sat4j.specs.IVec;
72 import org.sat4j.specs.IVecInt;
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 public class OPBReader2005 extends Reader implements Serializable {
89
90
91
92
93 private static final long serialVersionUID = 1L;
94
95 protected final IPBSolver solver;
96
97 protected final IVecInt lits;
98
99 protected final IVec<BigInteger> coeffs;
100
101 protected BigInteger d;
102
103 protected String operator;
104
105 private final IVecInt objectiveVars = new VecInt();
106
107 private final IVec<BigInteger> objectiveCoeffs = new Vec<BigInteger>();
108
109
110 protected boolean hasObjFunc = false;
111
112
113 protected boolean hasVariablesExplanation = false;
114
115 protected int nbVars, nbConstr;
116
117
118 protected int nbConstraintsRead;
119
120
121
122
123
124
125
126
127
128
129 protected void metaData(int nbvar, int nbconstr) {
130 this.solver.newVar(nbvar);
131 }
132
133
134
135
136 protected void beginObjective() {
137 }
138
139
140
141
142 protected void endObjective() {
143 assert this.lits.size() == this.coeffs.size();
144 assert this.lits.size() == this.coeffs.size();
145 for (int i = 0; i < this.lits.size(); i++) {
146 this.objectiveVars.push(this.lits.get(i));
147 this.objectiveCoeffs.push(this.coeffs.get(i));
148 }
149 }
150
151
152
153
154 protected void beginConstraint() {
155 this.lits.clear();
156 this.coeffs.clear();
157 assert this.lits.size() == 0;
158 assert this.coeffs.size() == 0;
159 }
160
161
162
163
164
165
166
167 protected void endConstraint() throws ContradictionException {
168
169 assert !(this.lits.size() == 0);
170 assert !(this.coeffs.size() == 0);
171 assert this.lits.size() == this.coeffs.size();
172
173 if ("=".equals(this.operator)) {
174 this.solver.addExactly(this.lits, this.coeffs, this.d);
175 } else if ("<=".equals(this.operator)) {
176 this.solver.addAtMost(this.lits, this.coeffs, this.d);
177 } else {
178 assert ">=".equals(this.operator);
179 this.solver.addAtLeast(this.lits, this.coeffs, this.d);
180 }
181 this.nbConstraintsRead++;
182 }
183
184
185
186
187
188
189
190
191
192
193 private void constraintTerm(BigInteger coeff, String var)
194 throws ParseFormatException {
195 this.coeffs.push(coeff);
196 this.lits.push(translateVarToId(var));
197 }
198
199 protected int translateVarToId(String var) throws ParseFormatException {
200 int id = Integer.parseInt(var.substring(1));
201 return (this.savedChar == '-' ? -1 : 1) * id;
202 }
203
204
205
206
207
208
209
210 protected void constraintRelOp(String relop) {
211 this.operator = relop;
212 }
213
214
215
216
217
218
219
220
221 protected void constraintRightTerm(BigInteger val) {
222 this.d = val;
223 }
224
225 transient BufferedReader in;
226
227 char savedChar;
228
229 boolean charAvailable = false;
230
231 boolean eofReached = false;
232
233 private boolean eolReached = false;
234
235
236
237
238
239
240 protected char get() throws IOException {
241 int c;
242
243 if (this.charAvailable) {
244 this.charAvailable = false;
245 return this.savedChar;
246 }
247
248 c = this.in.read();
249 if (c == -1) {
250 this.eofReached = true;
251 }
252 if (c == '\n' || c == '\r') {
253 this.eolReached = true;
254 } else {
255 this.eolReached = false;
256 }
257 return (char) c;
258 }
259
260 public IVecInt getVars() {
261 return this.objectiveVars;
262 }
263
264 public IVec<BigInteger> getCoeffs() {
265 return this.objectiveCoeffs;
266 }
267
268
269
270
271 protected void putback(char c) {
272 this.savedChar = c;
273 this.charAvailable = true;
274 }
275
276
277
278
279 protected boolean eof() {
280 return this.eofReached;
281 }
282
283 protected boolean eol() {
284 return this.eolReached;
285 }
286
287
288
289
290
291
292 protected void skipSpaces() throws IOException {
293 char c;
294
295 do {
296 c = get();
297 } while (Character.isWhitespace(c));
298
299 putback(c);
300 }
301
302
303
304
305
306
307
308 public String readWord() throws IOException {
309 StringBuffer s = new StringBuffer();
310 char c;
311
312 skipSpaces();
313
314 while (!Character.isWhitespace(c = get()) && !eol() && !eof()) {
315 s.append(c);
316 }
317
318 putback(c);
319 return s.toString();
320 }
321
322
323
324
325
326
327
328
329 public void readInteger(StringBuffer s) throws IOException {
330 char c;
331
332 skipSpaces();
333 s.setLength(0);
334
335 c = get();
336 if (c == '-' || Character.isDigit(c)) {
337 s.append(c);
338
339
340 }
341
342 while (Character.isDigit(c = get()) && !eol() && !eof()) {
343 s.append(c);
344 }
345
346 putback(c);
347 }
348
349
350
351
352
353
354
355
356 protected boolean readIdentifier(StringBuffer s) throws IOException,
357 ParseFormatException {
358 char c;
359
360 s.setLength(0);
361
362 skipSpaces();
363
364
365 c = get();
366 if (eof()) {
367 return false;
368 }
369
370 if (!isGoodFirstCharacter(c)) {
371 putback(c);
372 return false;
373 }
374
375 s.append(c);
376
377
378 while (true) {
379 c = get();
380 if (eof()) {
381 break;
382 }
383
384 if (isGoodFollowingCharacter(c)) {
385 s.append(c);
386 } else {
387 putback(c);
388 break;
389 }
390 }
391 checkId(s);
392 return true;
393 }
394
395 protected boolean isGoodFirstCharacter(char c) {
396 return Character.isLetter(c) || c == '_';
397 }
398
399 protected boolean isGoodFollowingCharacter(char c) {
400 return Character.isLetter(c) || Character.isDigit(c) || c == '_';
401 }
402
403 protected void checkId(StringBuffer s) throws ParseFormatException {
404
405 int varID = Integer.parseInt(s.substring(1));
406 if (varID > this.nbVars) {
407 throw new ParseFormatException(
408 "Variable identifier larger than #variables in metadata.");
409 }
410 }
411
412
413
414
415
416
417
418 private String readRelOp() throws IOException {
419 char c;
420
421 skipSpaces();
422
423 c = get();
424 if (eof()) {
425 return null;
426 }
427
428 if (c == '=') {
429 return "=";
430 }
431
432 char following = get();
433 if (c == '>' && following == '=') {
434 return ">=";
435 }
436 if (c == '<' && following == '=') {
437 return "<=";
438 }
439
440 return null;
441 }
442
443
444
445
446
447
448
449
450 protected void readMetaData() throws IOException, ParseFormatException {
451 char c;
452 String s;
453
454
455 c = get();
456 if (c != '*') {
457 throw new ParseFormatException(
458 "First line of input file should be a comment");
459 }
460
461 s = readWord();
462 if (eof() || !"#variable=".equals(s)) {
463 throw new ParseFormatException(
464 "First line should contain #variable= as first keyword");
465 }
466
467 this.nbVars = Integer.parseInt(readWord());
468
469 s = readWord();
470 if (eof() || !"#constraint=".equals(s)) {
471 throw new ParseFormatException(
472 "First line should contain #constraint= as second keyword");
473 }
474
475 this.nbConstr = Integer.parseInt(readWord());
476
477
478 this.in.readLine();
479
480
481 metaData(this.nbVars, this.nbConstr);
482 }
483
484
485
486
487
488
489 private void skipComments() throws IOException {
490 char c = ' ';
491
492
493
494 while (!eof() && (c = get()) == '*') {
495 this.in.readLine();
496 }
497
498 putback(c);
499 }
500
501
502
503
504
505
506
507
508
509
510
511 protected void readTerm(StringBuffer coeff, StringBuffer var)
512 throws IOException, ParseFormatException {
513 char c;
514
515 readInteger(coeff);
516
517 skipSpaces();
518 c = get();
519 if (c != '*') {
520 throw new ParseFormatException(
521 "'*' expected between a coefficient and a variable");
522 }
523
524 if (!readIdentifier(var)) {
525 throw new ParseFormatException("identifier expected");
526 }
527 }
528
529
530
531
532
533 protected void readVariablesExplanation() throws IOException,
534 ParseFormatException {
535 }
536
537
538
539
540
541
542
543
544 protected void readObjective() throws IOException, ParseFormatException {
545 char c;
546 StringBuffer var = new StringBuffer();
547 StringBuffer coeff = new StringBuffer();
548
549
550
551 skipSpaces();
552 c = get();
553 if (c != 'm') {
554
555 putback(c);
556 return;
557 }
558
559 this.hasObjFunc = true;
560 if (get() == 'i' && get() == 'n' && get() == ':') {
561 beginObjective();
562
563 while (!eof()) {
564 readTerm(coeff, var);
565 constraintTerm(new BigInteger(coeff.toString()), var.toString());
566
567 skipSpaces();
568 c = get();
569 if (c == ';') {
570 break;
571 } else if (c == '-' || c == '+' || Character.isDigit(c)) {
572 putback(c);
573 } else {
574 throw new ParseFormatException(
575 "unexpected character in objective function");
576 }
577 }
578
579 endObjective();
580 } else {
581 throw new ParseFormatException(
582 "input format error: 'min:' expected");
583 }
584 }
585
586
587
588
589
590
591
592
593 protected void readConstraint() throws IOException, ParseFormatException,
594 ContradictionException {
595 StringBuffer var = new StringBuffer();
596 StringBuffer coeff = new StringBuffer();
597 char c;
598
599 beginConstraint();
600
601 while (!eof()) {
602 readTerm(coeff, var);
603 constraintTerm(new BigInteger(coeff.toString()), var.toString());
604
605 skipSpaces();
606 c = get();
607 if (c == '>' || c == '=' || c == '<') {
608
609 putback(c);
610 break;
611 } else if (c == '-' || c == '+' || Character.isDigit(c)) {
612 putback(c);
613 } else {
614 throw new ParseFormatException(
615 "unexpected character in constraint");
616 }
617 }
618
619 if (eof()) {
620 throw new ParseFormatException(
621 "unexpected EOF before end of constraint");
622 }
623
624 String relop;
625 if ((relop = readRelOp()) == null) {
626 throw new ParseFormatException(
627 "unexpected relational operator in constraint");
628
629 }
630 constraintRelOp(relop);
631 readInteger(coeff);
632 constraintRightTerm(new BigInteger(coeff.toString()));
633
634 skipSpaces();
635 c = get();
636 if (eof() || c != ';') {
637 throw new ParseFormatException(
638 "semicolon expected at end of constraint");
639 }
640
641 endConstraint();
642 }
643
644 public OPBReader2005(IPBSolver solver) {
645 this.solver = solver;
646 this.lits = new VecInt();
647 this.coeffs = new Vec<BigInteger>();
648 }
649
650
651
652
653
654
655
656
657
658 public void parse() throws IOException, ParseFormatException,
659 ContradictionException {
660 readMetaData();
661
662 skipComments();
663
664 readObjective();
665
666 readVariablesExplanation();
667
668
669 this.nbConstraintsRead = 0;
670 char c;
671 while (!eof()) {
672 skipSpaces();
673 if (eof()) {
674 break;
675 }
676
677 c = get();
678 putback(c);
679 if (c == '*') {
680 skipComments();
681 }
682
683 if (eof()) {
684 break;
685 }
686
687 readConstraint();
688
689 }
690
691 if (this.nbConstraintsRead != this.nbConstr) {
692 throw new ParseFormatException("Number of constraints read ("
693 + this.nbConstraintsRead + ") is different from metadata ("
694 + this.nbConstr + ")");
695 }
696 }
697
698 @Override
699 public IProblem parseInstance(final java.io.Reader input)
700 throws ParseFormatException, ContradictionException {
701 IProblem problem = parseInstance(new LineNumberReader(input));
702 this.solver.setObjectiveFunction(getObjectiveFunction());
703 return problem;
704 }
705
706 private IProblem parseInstance(LineNumberReader input)
707 throws ParseFormatException, ContradictionException {
708 this.solver.reset();
709 this.in = input;
710 try {
711 parse();
712 return this.solver;
713 } catch (ContradictionException ce) {
714 throw ce;
715 } catch (Exception e) {
716 String message;
717
718 if (e instanceof ParseFormatException) {
719 message = e.getMessage().substring(
720 ParseFormatException.PARSING_ERROR.length());
721 } else {
722 message = e.toString();
723 }
724 throw new ParseFormatException(" line "
725 + (input.getLineNumber() + 1) + ", " + message);
726
727 }
728 }
729
730 @Override
731 public String decode(int[] model) {
732 StringBuffer stb = new StringBuffer();
733
734 for (int i = 0; i < model.length; i++) {
735 if (model[i] < 0) {
736 stb.append("-x");
737 stb.append(-model[i]);
738 } else {
739 stb.append("x");
740 stb.append(model[i]);
741 }
742 stb.append(" ");
743 }
744 return stb.toString();
745 }
746
747 @Override
748 public void decode(int[] model, PrintWriter out) {
749 for (int i = 0; i < model.length; i++) {
750 if (model[i] < 0) {
751 out.print("-x");
752 out.print(-model[i]);
753 } else {
754 out.print("x");
755 out.print(model[i]);
756 }
757 out.print(" ");
758 }
759 }
760
761 public ObjectiveFunction getObjectiveFunction() {
762 if (this.hasObjFunc) {
763 return new ObjectiveFunction(getVars(), getCoeffs());
764 }
765 return null;
766 }
767
768 public IVecInt getListOfVariables() {
769 return null;
770 }
771
772 @Override
773 public IProblem parseInstance(final InputStream in)
774 throws ParseFormatException, ContradictionException, IOException {
775 return parseInstance(new InputStreamReader(in));
776 }
777 }