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  package org.sat4j.pb.constraints.pb;
29  
30  import java.math.BigInteger;
31  
32  import org.sat4j.core.VecInt;
33  import org.sat4j.minisat.constraints.cnf.Lits;
34  import org.sat4j.minisat.core.ILits;
35  import org.sat4j.minisat.core.VarActivityListener;
36  import org.sat4j.specs.IteratorInt;
37  
38  /**
39   * @author parrain TODO To change the template for this generated type comment
40   *         go to Window - Preferences - Java - Code Style - Code Templates
41   */
42  public class ConflictMap extends MapPb implements IConflict {
43  
44      private final ILits voc;
45  
46      /**
47       * to store the slack of the current resolvant
48       */
49      protected BigInteger currentSlack;
50  
51      protected int currentLevel;
52  
53      /**
54       * allows to access directly to all variables belonging to a particular
55       * level At index 0, unassigned literals are stored (usually level -1); so
56       * there is always a step between index and levels.
57       */
58      protected VecInt[] byLevel;
59  
60      /**
61       * constructs the data structure needed to perform cutting planes
62       * 
63       * @param cpb
64       *                pseudo-boolean constraint which rosed the conflict
65       * @param level
66       *                current decision level
67       * @return a conflict on which cutting plane can be performed.
68       */
69      public static IConflict createConflict(PBConstr cpb, int level) {
70          return new ConflictMap(cpb, level);
71      }
72  
73      ConflictMap(PBConstr cpb, int level) {
74          super(cpb);
75          this.voc = cpb.getVocabulary();
76          this.currentLevel = level;
77          initStructures();
78      }
79  
80      private void initStructures() {
81          currentSlack = BigInteger.ZERO;
82          byLevel = new VecInt[levelToIndex(currentLevel) + 1];
83          int ilit, litLevel, index;
84          BigInteger tmp;
85          for (int i = 0; i < size(); i++) {
86              ilit = weightedLits.getLit(i);
87              litLevel = voc.getLevel(ilit);
88              // eventually add to slack
89              tmp = weightedLits.getCoef(i);
90              if ((tmp.signum() > 0)
91                      && (((!voc.isFalsified(ilit)) || litLevel == currentLevel)))
92                  currentSlack = currentSlack.add(tmp);
93              // add to byLevel structure
94              index = levelToIndex(litLevel);
95              if (byLevel[index] == null) {
96                  byLevel[index] = new VecInt();
97              }
98              byLevel[index].push(ilit);
99          }
100     }
101 
102     /**
103      * convert level into an index in the byLevel structure
104      * 
105      * @param level
106      * @return
107      */
108     private static final int levelToIndex(int level) {
109         return level + 1;
110     }
111 
112     /**
113      * convert index in the byLevel structure into a level
114      * 
115      * @param indLevel
116      * @return
117      */
118     private static final int indexToLevel(int indLevel) {
119         return indLevel - 1;
120     }
121 
122     /*
123      * coefficient to be computed.
124      * 
125      */
126     protected BigInteger coefMult = BigInteger.ZERO;
127 
128     protected BigInteger coefMultCons = BigInteger.ZERO;
129 
130     /**
131      * computes a cutting plane with a pseudo-boolean constraint. this method
132      * updates the current instance (of ConflictMap).
133      * 
134      * @param cpb
135      *                constraint to compute with the cutting plane
136      * @param litImplied
137      *                literal that must be resolved by the cutting plane
138      * @return an update of the degree of the current instance
139      */
140     public BigInteger resolve(PBConstr cpb, int litImplied,
141             VarActivityListener val) {
142         assert litImplied > 1;
143         int nLitImplied = litImplied ^ 1;
144         if (cpb == null || !weightedLits.containsKey(nLitImplied)) {
145             // no resolution
146             // undo operation should be anticipated
147             int litLevel = levelToIndex(voc.getLevel(litImplied));
148             int lit = 0;
149             if (byLevel[litLevel] != null) {
150                 if (byLevel[litLevel].contains(litImplied)) {
151                     lit = litImplied;
152                     assert weightedLits.containsKey(litImplied);
153                 } else if (byLevel[litLevel].contains(nLitImplied)) {
154                     lit = nLitImplied;
155                     assert weightedLits.containsKey(nLitImplied);
156                 }
157             }
158 
159             if (lit > 0) {
160                 byLevel[litLevel].remove(lit);
161                 if (byLevel[0] == null)
162                     byLevel[0] = new VecInt();
163                 byLevel[0].push(lit);
164             }
165             return degree;
166         }
167 
168         assert slackConflict().signum() <= 0;
169         assert degree.signum() >= 0;
170 
171         // coefficients of the constraint must be copied in an other structure
172         // in order to make reduction operations.
173         BigInteger[] coefsCons = null;
174         BigInteger degreeCons = cpb.getDegree();
175 
176         // search of the index of the implied literal
177         int ind = 0;
178         while (cpb.get(ind) != litImplied)
179             ind++;
180 
181         assert cpb.get(ind) == litImplied;
182         assert cpb.getCoef(ind) != BigInteger.ZERO;
183 
184         if (cpb.getCoef(ind).equals(BigInteger.ONE)) {
185             // then we know that the resolvant will still be a conflict (cf.
186             // Dixon's property)
187             coefMultCons = weightedLits.get(nLitImplied);
188             coefMult = BigInteger.ONE;
189             // updating of the degree of the conflict
190             degreeCons = degreeCons.multiply(coefMultCons);
191         } else {
192             if (weightedLits.get(nLitImplied).equals(BigInteger.ONE)) {
193                 // then we know that the resolvant will still be a conflict (cf.
194                 // Dixon's property)
195                 coefMult = cpb.getCoef(ind);
196                 coefMultCons = BigInteger.ONE;
197                 // updating of the degree of the conflict
198                 degree = degree.multiply(coefMult);
199             } else {
200                 // pb-constraint has to be reduced
201                 // to obtain a conflictual result from the cutting plane
202                 WatchPb wpb = (WatchPb) cpb; // DLB Findbugs warning ok
203                 coefsCons = wpb.getCoefs();
204                 assert positiveCoefs(coefsCons);
205                 degreeCons = reduceUntilConflict(litImplied, ind, coefsCons,
206                         wpb);
207                 // updating of the degree of the conflict
208                 degreeCons = degreeCons.multiply(coefMultCons);
209                 degree = degree.multiply(coefMult);
210             }
211 
212             // coefficients of the conflict must be multiplied by coefMult
213             if (!coefMult.equals(BigInteger.ONE))
214                 for (int i = 0; i < size(); i++) {
215                     changeCoef(i, weightedLits.getCoef(i).multiply(coefMult));
216                 }
217         }
218 
219         assert slackConflict().signum() <= 0;
220 
221         // cutting plane
222         degree = cuttingPlane(cpb, degreeCons, coefsCons, coefMultCons, val);
223 
224         // neither litImplied nor nLitImplied is present in coefs structure
225         assert !weightedLits.containsKey(litImplied);
226         assert !weightedLits.containsKey(nLitImplied);
227         // neither litImplied nor nLitImplied is present in byLevel structure
228         assert getLevelByLevel(litImplied) == -1;
229         assert getLevelByLevel(nLitImplied) == -1;
230         assert degree.signum() > 0;
231         assert slackConflict().signum() <= 0;
232 
233         // saturation
234         degree = saturation();
235         assert slackConflict().signum() <= 0;
236 
237         return degree;
238     }
239 
240     protected BigInteger reduceUntilConflict(int litImplied, int ind,
241             BigInteger[] reducedCoefs, WatchPb wpb) {
242         BigInteger slackResolve = BigInteger.ONE.negate();
243         BigInteger slackThis = BigInteger.ZERO;
244         BigInteger slackIndex;
245         BigInteger slackConflict = slackConflict();
246         BigInteger ppcm;
247         BigInteger reducedDegree = wpb.getDegree();
248         BigInteger previousCoefLitImplied = BigInteger.ZERO;
249         BigInteger tmp;
250         BigInteger coefLitImplied = weightedLits.get(litImplied ^ 1);
251 
252         do {
253             if (slackResolve.signum() >= 0) {
254                 assert slackThis.signum() > 0;
255                 tmp = reduceInConstraint(wpb, reducedCoefs, ind, reducedDegree);
256                 assert ((tmp.compareTo(reducedDegree) < 0) && (tmp
257                         .compareTo(BigInteger.ONE) >= 0));
258                 reducedDegree = tmp;
259             }
260             // search of the multiplying coefficients
261             assert weightedLits.get(litImplied ^ 1).signum() > 0;
262             assert reducedCoefs[ind].signum() > 0;
263 
264             if (!reducedCoefs[ind].equals(previousCoefLitImplied)) {
265                 assert coefLitImplied.equals(weightedLits.get(litImplied ^ 1));
266                 ppcm = ppcm(reducedCoefs[ind], coefLitImplied);
267                 assert ppcm.signum() > 0;
268                 coefMult = ppcm.divide(coefLitImplied);
269                 coefMultCons = ppcm.divide(reducedCoefs[ind]);
270 
271                 assert coefMultCons.signum() > 0;
272                 assert coefMult.signum() > 0;
273                 assert coefMult.multiply(coefLitImplied).equals(
274                         coefMultCons.multiply(reducedCoefs[ind]));
275                 previousCoefLitImplied = reducedCoefs[ind];
276             }
277 
278             // slacks computed for each constraint
279             slackThis = wpb.slackConstraint(reducedCoefs, reducedDegree)
280                     .multiply(coefMultCons);
281             assert slackConflict.equals(slackConflict());
282             slackIndex = slackConflict.multiply(coefMult);
283             assert slackIndex.signum() <= 0;
284             // estimate of the slack after the cutting plane
285             slackResolve = slackThis.add(slackIndex);
286         } while (slackResolve.signum() >= 0);
287         assert coefMult.multiply(weightedLits.get(litImplied ^ 1)).equals(
288                 coefMultCons.multiply(reducedCoefs[ind]));
289         return reducedDegree;
290 
291     }
292 
293     /**
294      * computes the slack of the current instance
295      */
296     public BigInteger slackConflict() {
297         BigInteger poss = BigInteger.ZERO;
298         BigInteger tmp;
299         // for each literal
300         for (int i = 0; i < size(); i++) {
301             tmp = weightedLits.getCoef(i);
302             if (tmp.signum() != 0 && !voc.isFalsified(weightedLits.getLit(i)))
303                 poss = poss.add(tmp);
304         }
305         return poss.subtract(degree);
306     }
307 
308     public boolean oldIsAssertive(int dl) {
309         BigInteger tmp;
310         int lit;
311         BigInteger slack = computeSlack(dl).subtract(degree);
312         if (slack.signum() < 0)
313             return false;
314         for (int i = 0; i < size(); i++) {
315             tmp = weightedLits.getCoef(i);
316             lit = weightedLits.getLit(i);
317             if ((tmp.signum() > 0)
318                     && (voc.isUnassigned(lit) || voc.getLevel(lit) >= dl)
319                     && (slack.compareTo(tmp) < 0))
320                 return true;
321         }
322         return false;
323     }
324 
325     // computes a slack with respect to a particular decision level
326     private BigInteger computeSlack(int dl) {
327         BigInteger slack = BigInteger.ZERO;
328         int lit;
329         BigInteger tmp;
330         for (int i = 0; i < size(); i++) {
331             tmp = weightedLits.getCoef(i);
332             lit = weightedLits.getLit(i);
333             if ((tmp.signum() > 0)
334                     && (((!voc.isFalsified(lit)) || voc.getLevel(lit) >= dl)))
335                 slack = slack.add(tmp);
336         }
337         return slack;
338     }
339 
340     /**
341      * tests if the conflict is assertive (allows to imply a literal) at a
342      * particular decision level
343      * 
344      * @param dl
345      *                the decision level
346      * @return true if the conflict is assertive at the decision level
347      */
348     public boolean isAssertive(int dl) {
349         assert dl <= currentLevel;
350         assert dl <= currentLevel;
351 
352         currentLevel = dl;
353         // assert currentSlack.equals(computeSlack(dl));
354         BigInteger slack = currentSlack.subtract(degree);
355         if (slack.signum() < 0)
356             return false;
357         return isImplyingLiteral(slack);
358     }
359 
360     // given the slack already computed, tests if a literal could be implied at
361     // a particular level
362     // uses the byLevel data structure to parse each literal by decision level
363     private boolean isImplyingLiteral(BigInteger slack) {
364         // unassigned literals are tried first
365         int unassigned = levelToIndex(-1);
366         if (byLevel[unassigned] != null) {
367             for (IteratorInt iterator = byLevel[unassigned].iterator(); iterator
368                     .hasNext();) {
369                 if (slack.compareTo(weightedLits.get(iterator.next())) < 0)
370                     return true;
371             }
372         }
373         // then we have to look at every literal at a decision level >=dl
374         BigInteger tmp;
375         int level = levelToIndex(currentLevel);
376         if (byLevel[level] != null)
377             for (IteratorInt iterator = byLevel[level].iterator(); iterator
378                     .hasNext();) {
379                 tmp = weightedLits.get(iterator.next());
380                 if (tmp != null && slack.compareTo(tmp) < 0)
381                     return true;
382             }
383         return false;
384     }
385 
386     // given the slack already computed, tests if a literal could be implied at
387     // a particular level
388     // uses the coefs data structure (where coefficients are decreasing ordered)
389     // to parse each literal
390     private boolean isImplyingLiteralOrdered(int dl, BigInteger slack) {
391         int ilit, litLevel;
392         for (int i = 0; i < size(); i++) {
393             ilit = weightedLits.getLit(i);
394             litLevel = voc.getLevel(ilit);
395             if ((litLevel >= dl || voc.isUnassigned(ilit))
396                     && (slack.compareTo(weightedLits.getCoef(i)) < 0))
397                 return true;
398         }
399         return false;
400     }
401 
402     /**
403      * computes the least common factor of two integers (Plus Petit Commun
404      * Multiple in french)
405      * 
406      * @param a
407      *                first integer
408      * @param b
409      *                second integer
410      * @return the least common factor
411      */
412     protected static BigInteger ppcm(BigInteger a, BigInteger b) {
413         return a.divide(a.gcd(b)).multiply(b);
414     }
415 
416     /**
417      * constraint reduction : removes a literal of the constraint. The literal
418      * should be either unassigned or satisfied. The literal can not be the
419      * literal that should be resolved.
420      * 
421      * @param wpb
422      *                the initial constraint to reduce
423      * @param coefsBis
424      *                the coefficients of the constraint wrt which the reduction
425      *                will be proposed
426      * @param indLitImplied
427      *                index in wpb of the literal that should be resolved
428      * @param degreeBis
429      *                the degree of the constraint wrt which the reduction will
430      *                be proposed
431      * @return new degree of the reduced constraint
432      */
433     public BigInteger reduceInConstraint(WatchPb wpb,
434             final BigInteger[] coefsBis, final int indLitImplied,
435             final BigInteger degreeBis) {
436         // logger.entering(this.getClass().getName(),"reduceInConstraint");
437         assert degreeBis.compareTo(BigInteger.ONE) > 0;
438         // search of an unassigned literal
439         int lit = -1;
440         for (int ind = 0; (ind < wpb.lits.length) && (lit == -1); ind++)
441             if (coefsBis[ind].signum() != 0 && voc.isUnassigned(wpb.lits[ind])) {
442                 assert coefsBis[ind].compareTo(degreeBis) < 0;
443                 lit = ind;
444             }
445 
446         // else, search of a satisfied literal
447         if (lit == -1)
448             for (int ind = 0; (ind < wpb.lits.length) && (lit == -1); ind++)
449                 if ((coefsBis[ind].signum() != 0)
450                         && (voc.isSatisfied(wpb.lits[ind]))
451                         && (ind != indLitImplied))
452                     lit = ind;
453 
454         // a literal has been found
455         assert lit != -1;
456 
457         assert lit != indLitImplied;
458         // logger.finer("Found literal "+Lits.toString(lits[lit]));
459         // reduction can be done
460         BigInteger degUpdate = degreeBis.subtract(coefsBis[lit]);
461         coefsBis[lit] = BigInteger.ZERO;
462 
463         // saturation of the constraint
464         degUpdate = saturation(coefsBis, degUpdate);
465 
466         assert coefsBis[indLitImplied].signum() > 0;
467         assert degreeBis.compareTo(degUpdate) > 0;
468         return degUpdate;
469     }
470 
471     static BigInteger saturation(BigInteger[] coefs, BigInteger degree) {
472         assert degree.signum() > 0;
473         BigInteger minimum = degree;
474         for (int i = 0; i < coefs.length; i++) {
475             if (coefs[i].signum() > 0)
476                 minimum = minimum.min(coefs[i]);
477             coefs[i] = degree.min(coefs[i]);
478         }
479         if (minimum.equals(degree) && !degree.equals(BigInteger.ONE)) {
480             // the result is a clause
481             // there is no more possible reduction
482             degree = BigInteger.ONE;
483             for (int i = 0; i < coefs.length; i++)
484                 if (coefs[i].signum() > 0)
485                     coefs[i] = degree;
486         }
487         return degree;
488     }
489 
490     private static boolean positiveCoefs(final BigInteger[] coefsCons) {
491         for (int i = 0; i < coefsCons.length; i++) {
492             if (coefsCons[i].signum() <= 0)
493                 return false;
494         }
495         return true;
496     }
497 
498     /**
499      * computes the level for the backtrack : the highest decision level for
500      * which the conflict is assertive.
501      * 
502      * @param maxLevel
503      *                the lowest level for which the conflict is assertive
504      * @return the highest level (smaller int) for which the constraint is
505      *         assertive.
506      */
507     public int getBacktrackLevel(int maxLevel) {
508         // we are looking for a level higher than maxLevel
509         // where the constraint is still assertive
510         VecInt lits;
511         int level;
512         int indStop = levelToIndex(maxLevel) - 1;
513         int indStart = levelToIndex(0);
514         BigInteger slack = computeSlack(0).subtract(degree);
515         int previous = 0;
516         for (int indLevel = indStart; indLevel <= indStop; indLevel++) {
517             if (byLevel[indLevel] != null) {
518                 level = indexToLevel(indLevel);
519                 assert computeSlack(level).subtract(degree).equals(slack);
520                 if (isImplyingLiteralOrdered(level, slack)) {
521                     break;
522                 }
523                 // updating the new slack
524                 lits = byLevel[indLevel];
525                 int lit;
526                 for (IteratorInt iterator = lits.iterator(); iterator.hasNext();) {
527                     lit = iterator.next();
528                     if (voc.isFalsified(lit)
529                             && voc.getLevel(lit) == indexToLevel(indLevel))
530                         slack = slack.subtract(weightedLits.get(lit));
531                 }
532                 if (!lits.isEmpty())
533                     previous = level;
534             }
535         }
536         assert previous == oldGetBacktrackLevel(maxLevel);
537         return previous;
538     }
539 
540     public int oldGetBacktrackLevel(int maxLevel) {
541         int litLevel;
542         int borneMax = maxLevel;
543         assert oldIsAssertive(borneMax);
544         int borneMin = -1;
545         // borneMax is the highest level in the search tree where the constraint is assertive
546         for (int i = 0; i < size() ; i++) {
547             litLevel = voc.getLevel(weightedLits.getLit(i));
548             if (litLevel < borneMax && litLevel > borneMin
549                     && oldIsAssertive(litLevel))
550                 borneMax = litLevel;
551         }
552         // the level returned is the first level below borneMax
553         // where there is a literal belonging to the constraint
554         int retour = 0;
555         for (int i = 0; i < size(); i++) {
556             litLevel = voc.getLevel(weightedLits.getLit(i));
557             if (litLevel > retour && litLevel < borneMax) {
558                 retour = litLevel;
559             }
560         }
561         return retour;
562     }
563 
564     public void updateSlack(int level) {
565         int dl = levelToIndex(level);
566         if (byLevel[dl] != null) {
567             int lit;
568             for (IteratorInt iterator = byLevel[dl].iterator(); iterator
569                     .hasNext();) {
570                 lit = iterator.next();
571                 if (voc.isFalsified(lit))
572                     currentSlack = currentSlack.add(weightedLits.get(lit));
573             }
574         }
575     }
576 
577     @Override
578     void increaseCoef(int lit, BigInteger incCoef) {
579         if ((!voc.isFalsified(lit)) || voc.getLevel(lit) == currentLevel) {
580             currentSlack = currentSlack.add(incCoef);
581         }
582         assert byLevel[levelToIndex(voc.getLevel(lit))].contains(lit);
583         super.increaseCoef(lit, incCoef);
584     }
585 
586     @Override
587     void decreaseCoef(int lit, BigInteger decCoef) {
588         if ((!voc.isFalsified(lit)) || voc.getLevel(lit) == currentLevel) {
589             currentSlack = currentSlack.subtract(decCoef);
590         }
591         assert byLevel[levelToIndex(voc.getLevel(lit))].contains(lit);
592         super.decreaseCoef(lit, decCoef);
593     }
594 
595     @Override
596     void setCoef(int lit, BigInteger newValue) {
597         int litLevel = voc.getLevel(lit);
598         if ((!voc.isFalsified(lit)) || litLevel == currentLevel) {
599             if (weightedLits.containsKey(lit))
600                 currentSlack = currentSlack.subtract(weightedLits.get(lit));
601             currentSlack = currentSlack.add(newValue);
602         }
603         int indLitLevel = levelToIndex(litLevel);
604         if (!weightedLits.containsKey(lit)) {
605             if (byLevel[indLitLevel] == null) {
606                 byLevel[indLitLevel] = new VecInt();
607             }
608             byLevel[indLitLevel].push(lit);
609 
610         }
611         assert byLevel[indLitLevel] != null;
612         assert byLevel[indLitLevel].contains(lit);
613         super.setCoef(lit, newValue);
614     }
615 
616     @Override
617     void changeCoef(int indLit, BigInteger newValue) {
618     	int lit = weightedLits.getLit(indLit);
619         int litLevel = voc.getLevel(lit);
620         if ((!voc.isFalsified(lit)) || litLevel == currentLevel) {
621             if (weightedLits.containsKey(lit))
622                 currentSlack = currentSlack.subtract(weightedLits.get(lit));
623             currentSlack = currentSlack.add(newValue);
624         }
625         int indLitLevel = levelToIndex(litLevel);
626         assert weightedLits.containsKey(lit);
627         assert byLevel[indLitLevel] != null;
628         assert byLevel[indLitLevel].contains(lit);
629         super.changeCoef(indLit, newValue);
630     }
631 
632 
633     @Override
634     void removeCoef(int lit) {
635         int litLevel = voc.getLevel(lit);
636         if ((!voc.isFalsified(lit)) || litLevel == currentLevel) {
637             currentSlack = currentSlack.subtract(weightedLits.get(lit));
638         }
639         int indLitLevel = levelToIndex(litLevel);
640         assert indLitLevel < byLevel.length;
641         assert byLevel[indLitLevel] != null;
642         assert byLevel[indLitLevel].contains(lit);
643         byLevel[indLitLevel].remove(lit);
644         super.removeCoef(lit);
645     }
646     
647     
648     private int getLevelByLevel(int lit) {
649         for (int i = 0; i < byLevel.length; i++)
650             if (byLevel[i] != null && byLevel[i].contains(lit))
651                 return i;
652         return -1;
653     }
654 
655     public boolean slackIsCorrect(int dl) {
656         return currentSlack.equals(computeSlack(dl));
657     }
658 
659     @Override
660     public String toString() {
661         int lit;
662         StringBuffer stb = new StringBuffer();
663         for (int i = 0; i < size(); i++) {
664             lit = weightedLits.getLit(i);
665             stb.append(weightedLits.getCoef(i));
666             stb.append(".");
667             stb.append(Lits.toString(lit));
668             stb.append(" ");
669             stb.append("[");
670             stb.append(voc.valueToString(lit));
671             stb.append("@");
672             stb.append(voc.getLevel(lit));
673             stb.append("]");
674         }
675         return stb.toString() + " >= " + degree; //$NON-NLS-1$
676     }
677 
678 }