View Javadoc

1   /*
2    * SAT4J: a SATisfiability library for Java Copyright (C) 2004-2006 Daniel Le Berre
3    * 
4    * Based on the original minisat specification from:
5    * 
6    * An extensible SAT solver. Niklas E?n and Niklas S?rensson. Proceedings of the
7    * Sixth International Conference on Theory and Applications of Satisfiability
8    * Testing, LNCS 2919, pp 502-518, 2003.
9    * 
10   * This library is free software; you can redistribute it and/or modify it under
11   * the terms of the GNU Lesser General Public License as published by the Free
12   * Software Foundation; either version 2.1 of the License, or (at your option)
13   * any later version.
14   * 
15   * This library is distributed in the hope that it will be useful, but WITHOUT
16   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18   * details.
19   * 
20   * You should have received a copy of the GNU Lesser General Public License
21   * along with this library; if not, write to the Free Software Foundation, Inc.,
22   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23   * 
24   */
25  
26  package org.sat4j.minisat.core;
27  
28  import static org.sat4j.minisat.core.LiteralsUtils.neg;
29  import static org.sat4j.minisat.core.LiteralsUtils.var;
30  
31  import java.io.PrintStream;
32  import java.io.PrintWriter;
33  import java.io.Serializable;
34  import java.lang.reflect.Field;
35  import java.math.BigInteger;
36  import java.util.Comparator;
37  import java.util.Map;
38  import java.util.Timer;
39  import java.util.TimerTask;
40  
41  import org.sat4j.core.Vec;
42  import org.sat4j.core.VecInt;
43  import org.sat4j.specs.ContradictionException;
44  import org.sat4j.specs.IConstr;
45  import org.sat4j.specs.ISolver;
46  import org.sat4j.specs.IVec;
47  import org.sat4j.specs.IVecInt;
48  import org.sat4j.specs.TimeoutException;
49  
50  /**
51   * The backbone of the library providing the modular implementation of a MiniSAT
52   * (Chaff) like solver.
53   * 
54   * @author leberre
55   */
56  public class Solver<L extends ILits> implements ISolver, UnitPropagationListener,
57          ActivityListener, Learner {
58  
59      private static final long serialVersionUID = 1L;
60  
61      private static final double CLAUSE_RESCALE_FACTOR = 1e-20;
62  
63      private static final double CLAUSE_RESCALE_BOUND = 1 / CLAUSE_RESCALE_FACTOR;
64  
65      /**
66       * List des contraintes du probl?me.
67       */
68      private final IVec<Constr> constrs = new Vec<Constr>(); // Constr
69  
70      /**
71       * Liste des clauses apprises.
72       */
73      private final IVec<Constr> learnts = new Vec<Constr>(); // Clause
74  
75      /**
76       * incr?ment pour l'activit? des clauses.
77       */
78      private double claInc = 1.0;
79  
80      /**
81       * decay factor pour l'activit? des clauses.
82       */
83      private double claDecay = 1.0;
84  
85      /**
86       * Queue de propagation
87       */
88      // private final IntQueue propQ = new IntQueue(); // Lit
89      // head of the queue in trail ... (taken from MiniSAT 1.14)
90      private int qhead = 0;
91  
92      // queue
93  
94      /**
95       * affectation en ordre chronologique
96       */
97      protected final IVecInt trail = new VecInt(); // lit
98  
99      // vector
100 
101     /**
102      * indice des s?parateurs des diff?rents niveau de d?cision dans trail
103      */
104     protected final IVecInt trailLim = new VecInt(); // int
105 
106     // vector
107 
108     /**
109      * S?pare les hypoth?ses incr?mentale et recherche
110      */
111     protected int rootLevel;
112 
113     private int[] model = null;
114 
115     protected L voc;
116 
117     private IOrder<L> order;
118 
119     private final ActivityComparator comparator = new ActivityComparator();
120 
121     private final SolverStats stats = new SolverStats();
122 
123     private final LearningStrategy<L> learner;
124 
125     protected final AssertingClauseGenerator analyzer;
126 
127     private boolean undertimeout;
128 
129     private long timeout = Integer.MAX_VALUE;
130 
131     protected DataStructureFactory<L> dsfactory;
132 
133     private SearchParams params;
134 
135     private final IVecInt __dimacs_out = new VecInt();
136 
137     private SearchListener slistener = new NullSearchListener();
138 
139     private RestartStrategy restarter;
140     
141     protected IVecInt dimacs2internal(IVecInt in) {
142         if (voc.nVars() == 0) {
143             throw new RuntimeException(
144                     "Please set the number of variables (solver.newVar() or solver.newVar(maxvar)) before adding constraints!");
145         }
146         __dimacs_out.clear();
147         __dimacs_out.ensure(in.size());
148         for (int i = 0; i < in.size(); i++) {
149             assert (in.get(i) != 0) && (Math.abs(in.get(i)) <= voc.nVars());
150             __dimacs_out.unsafePush(voc.getFromPool(in.get(i)));
151         }
152         return __dimacs_out;
153     }
154 
155     /**
156      * creates a Solver without LearningListener. A learningListener must be
157      * added to the solver, else it won't backtrack!!! A data structure factory
158      * must be provided, else it won't work either.
159      * 
160      * @param acg
161      *            an asserting clause generator
162      */
163 
164     public Solver(AssertingClauseGenerator acg, LearningStrategy<L> learner,
165             DataStructureFactory<L> dsf, IOrder<L> order, RestartStrategy restarter) {
166         this(acg, learner, dsf, new SearchParams(), order,restarter);
167     }
168     
169     public Solver(AssertingClauseGenerator acg, LearningStrategy<L> learner,
170             DataStructureFactory<L> dsf, SearchParams params, IOrder<L> order, RestartStrategy restarter) {
171         analyzer = acg;
172         this.learner = learner;
173         this.order = order;
174         this.params = params;
175         setDataStructureFactory(dsf);
176         this.restarter = restarter;
177     }
178 
179     /**
180      * Change the internal representation of the contraints. Note that the
181      * heuristics must be changed prior to calling that method.
182      * 
183      * @param dsf
184      *            the internal factory
185      */
186     public final void setDataStructureFactory(DataStructureFactory<L> dsf) {
187         dsfactory = dsf;
188         dsfactory.setUnitPropagationListener(this);
189         dsfactory.setLearner(this);
190         voc = dsf.getVocabulary();
191         order.setLits(voc);
192     }
193 
194     public void setSearchListener(SearchListener sl) {
195         slistener = sl;
196     }
197 
198     public void setTimeout(int t) {
199         timeout = t*1000L;
200     }
201 
202     public void setTimeoutMs(long t) {
203         timeout = t;
204     }
205     
206     public void setSearchParams(SearchParams sp) {
207         params = sp;
208     }
209 
210     public void setRestartStrategy(RestartStrategy restarter) {
211         this.restarter = restarter;
212     }
213     
214     protected int nAssigns() {
215         return trail.size();
216     }
217 
218     public int nConstraints() {
219         return constrs.size();
220     }
221 
222     public void learn(Constr c) {
223         learnts.push(c);
224         c.setLearnt();
225         c.register();
226         stats.learnedclauses++;
227         switch (c.size()) {
228         case 2:
229             stats.learnedbinaryclauses++;
230             break;
231         case 3:
232             stats.learnedternaryclauses++;
233             break;
234         default:
235             // do nothing
236         }
237     }
238 
239     public int decisionLevel() {
240         return trailLim.size();
241     }
242 
243     public int newVar() {
244         int index = voc.nVars() + 1;
245         voc.ensurePool(index);
246         mseen = new boolean[index + 1];
247         trail.ensure(index);
248         trailLim.ensure(index);
249         order.newVar();
250         return index;
251     }
252 
253     public int newVar(int howmany) {
254         voc.ensurePool(howmany);
255         order.newVar(howmany);
256         mseen = new boolean[howmany + 1];
257         trail.ensure(howmany);
258         trailLim.ensure(howmany);
259         return voc.nVars();
260     }
261 
262     public IConstr addClause(IVecInt literals) throws ContradictionException {
263         IVecInt vlits = dimacs2internal(literals);
264         return addConstr(dsfactory.createClause(vlits));
265     }
266 
267     public boolean removeConstr(IConstr co) {
268         if (co == null) {
269             throw new UnsupportedOperationException(
270                     "Reference to the constraint to remove needed!"); //$NON-NLS-1$
271         }
272         Constr c = (Constr) co;
273         c.remove();
274         constrs.remove(c);
275         clearLearntClauses();
276         cancelLearntLiterals();
277         return true;
278     }
279 
280     public IConstr addPseudoBoolean(IVecInt literals, IVec<BigInteger> coeffs,
281             boolean moreThan, BigInteger degree) throws ContradictionException {
282         IVecInt vlits = dimacs2internal(literals);
283         assert vlits.size() == literals.size();
284         assert literals.size() == coeffs.size();
285         return addConstr(dsfactory.createPseudoBooleanConstraint(vlits, coeffs,
286                 moreThan, degree));
287     }
288 
289     public void addAllClauses(IVec<IVecInt> clauses)
290             throws ContradictionException {
291         for (IVecInt clause : clauses) {
292             addClause(clause);
293         }
294     }
295 
296     public IConstr addAtMost(IVecInt literals, int degree)
297             throws ContradictionException {
298         int n = literals.size();
299         IVecInt opliterals = new VecInt(n);
300         for (int p : literals) {
301             opliterals.push(-p);
302         }
303         return addAtLeast(opliterals, n - degree);
304     }
305 
306     public IConstr addAtLeast(IVecInt literals, int degree)
307             throws ContradictionException {
308         IVecInt vlits = dimacs2internal(literals);
309         return addConstr(dsfactory.createCardinalityConstraint(vlits, degree));
310     }
311 
312     @SuppressWarnings("unchecked")
313     public boolean simplifyDB() {
314         // aucune raison de recommencer un propagate?
315         // if (propagate() != null) {
316         // // Un conflit est d?couvert, la base est inconsistante
317         // return false;
318         // }
319 
320         // Simplifie la base de clauses apres la premiere propagation des
321         // clauses unitaires
322         IVec<Constr>[] cs = new IVec[] { constrs, learnts };
323         for (int type = 0; type < 2; type++) {
324             int j = 0;
325             for (int i = 0; i < cs[type].size(); i++) {
326                 if (cs[type].get(i).simplify()) {
327                     // enleve les contraintes satisfaites de la base
328                     cs[type].get(i).remove();
329                 } else {
330                     cs[type].moveTo(j++, i);
331                 }
332             }
333             cs[type].shrinkTo(j);
334         }
335         return true;
336     }
337 
338     /**
339      * Si un mod?le est trouv?, ce vecteur contient le mod?le.
340      * 
341      * @return un mod?le de la formule.
342      */
343     public int[] model() {
344         if (model == null) {
345             throw new UnsupportedOperationException(
346                     "Call the solve method first!!!"); //$NON-NLS-1$
347         }
348         int[] nmodel = new int[model.length];
349         System.arraycopy(model, 0, nmodel, 0, model.length);
350         return nmodel;
351     }
352 
353     /**
354      * Satisfait un litt?ral
355      * 
356      * @param p
357      *            le litt?ral
358      * @return true si tout se passe bien, false si un conflit appara?t.
359      */
360     public boolean enqueue(int p) {
361         return enqueue(p, null);
362     }
363 
364     /**
365      * Put the literal on the queue of assignments to be done.
366      * 
367      * @param p
368      *            the literal.
369      * @param from
370      *            the reason to propagate that literal, else null
371      * @return true if the asignment can be made, false if a conflict is
372      *         detected.
373      */
374     public boolean enqueue(int p, Constr from) {
375         assert p > 1;
376         if (voc.isSatisfied(p)) {
377             // literal is already satisfied. Skipping.
378             return true;
379         }
380         if (voc.isFalsified(p)) {
381             // conflicting enqueued assignment
382             return false;
383         }
384         // new fact, store it
385         voc.satisfies(p);
386         voc.setLevel(p, decisionLevel());
387         voc.setReason(p, from);
388         trail.push(p);
389         return true;
390     }
391 
392     private boolean[] mseen = new boolean[0];
393 
394     private final IVecInt preason = new VecInt();
395 
396     private final IVecInt outLearnt = new VecInt();
397 
398     public int analyze(Constr confl, Handle<Constr> outLearntRef) {
399         assert confl != null;
400         outLearnt.clear();
401 
402         final boolean[] seen = mseen;
403 
404         assert outLearnt.size() == 0;
405         for (int i = 0; i < seen.length; i++) {
406             seen[i] = false;
407         }
408 
409         analyzer.initAnalyze();
410         int p = ILits.UNDEFINED;
411 
412         outLearnt.push(ILits.UNDEFINED);
413         // reserve de la place pour le litteral falsifie
414         int outBtlevel = 0;
415 
416         do {
417             preason.clear();
418             assert confl != null;
419             confl.calcReason(p, preason);
420             if (confl.learnt())
421                 claBumpActivity(confl);
422             // Trace reason for p
423             for (int j = 0; j < preason.size(); j++) {
424                 int q = preason.get(j);
425                 order.updateVar(q);
426                 if (!seen[q >> 1]) {
427                     // order.updateVar(q); // MINISAT
428                     seen[q >> 1] = true;
429                     if (voc.getLevel(q) == decisionLevel()) {
430                         analyzer.onCurrentDecisionLevelLiteral(q);
431                     } else if (voc.getLevel(q) > 0) {
432                         // ajoute les variables depuis le niveau de d?cision 0
433                         outLearnt.push(q ^ 1);
434                         outBtlevel = Math.max(outBtlevel, voc.getLevel(q));
435                     }
436                 }
437             }
438 
439             // select next reason to look at
440             do {
441                 p = trail.last();
442                 // System.err.print((Clause.lastid()+1)+"
443                 // "+((Clause)confl).getId()+" ");
444                 confl = voc.getReason(p);
445                 // System.err.println(((Clause)confl).getId());
446                 // assert(confl != null) || counter == 1;
447                 undoOne();
448             } while (!seen[p >> 1]);
449             // seen[p.var] indique que p se trouve dans outLearnt ou dans
450             // le dernier niveau de d?cision
451         } while (analyzer.clauseNonAssertive(confl));
452 
453         outLearnt.set(0, p ^ 1);
454         simplifier.simplify(outLearnt);
455 
456         Constr c = dsfactory.createUnregisteredClause(outLearnt);
457         slistener.learn(c);
458 
459         outLearntRef.obj = c;
460 
461         assert outBtlevel > -1;
462         return outBtlevel;
463     }
464 
465     interface ISimplifier extends Serializable {
466         void simplify(IVecInt outLearnt);
467     }
468 
469     public static final ISimplifier NO_SIMPLIFICATION = new ISimplifier() {
470         /**
471          * 
472          */
473         private static final long serialVersionUID = 1L;
474 
475         public void simplify(IVecInt outLearnt) {
476         }
477 
478         @Override
479         public String toString() {
480             return "No reason simplification"; //$NON-NLS-1$
481         }
482     };
483 
484     public final ISimplifier SIMPLE_SIMPLIFICATION = new ISimplifier() {
485         /**
486          * 
487          */
488         private static final long serialVersionUID = 1L;
489 
490         public void simplify(IVecInt outLearnt) {
491             simpleSimplification(outLearnt);
492         }
493 
494         @Override
495         public String toString() {
496             return "Simple reason simplification"; //$NON-NLS-1$
497         }
498     };
499 
500     public final ISimplifier EXPENSIVE_SIMPLIFICATION = new ISimplifier() {
501 
502         /**
503          * 
504          */
505         private static final long serialVersionUID = 1L;
506 
507         public void simplify(IVecInt outLearnt) {
508             expensiveSimplification(outLearnt);
509         }
510 
511         @Override
512         public String toString() {
513             return "Expensive reason simplification"; //$NON-NLS-1$
514         }
515     };
516 
517     private ISimplifier simplifier = NO_SIMPLIFICATION;
518 
519     /**
520      * Setup the reason simplification strategy.
521      * By default, there is no reason simplification.
522      * NOTE THAT REASON SIMPLIFICATION IS ONLY ALLOWED FOR WL 
523      * CLAUSAL data structures. USING REASON SIMPLIFICATION
524      * ON CB CLAUSES, CARDINALITY CONSTRAINTS OR PB CONSTRAINTS
525      * MIGHT RESULT IN INCORRECT RESULTS. 
526      * 
527      * @param simp the name of the simplifier (one of NO_SIMPLIFICATION, SIMPLE_SIMPLIFICATION, EXPENSIVE_SIMPLIFICATION).
528      */
529     public void setSimplifier(String simp) {
530         Field f;
531         try {
532             f = Solver.class.getDeclaredField(simp);
533             simplifier = (ISimplifier) f.get(this);
534         } catch (Exception e) {
535             e.printStackTrace();
536             simplifier = NO_SIMPLIFICATION;
537         }
538     }
539 
540     /**
541      * Setup the reason simplification strategy.
542      * By default, there is no reason simplification.
543      * NOTE THAT REASON SIMPLIFICATION IS ONLY ALLOWED FOR WL 
544      * CLAUSAL data structures. USING REASON SIMPLIFICATION
545      * ON CB CLAUSES, CARDINALITY CONSTRAINTS OR PB CONSTRAINTS
546      * MIGHT RESULT IN INCORRECT RESULTS. 
547      * 
548      * @param simp
549      */
550     public void setSimplifier(ISimplifier simp) {
551         simplifier = simp;
552     }
553 
554     // Taken from MiniSAT 1.14: Simplify conflict clause (a little):
555     private void simpleSimplification(IVecInt outLearnt) {
556         int i, j;
557         final boolean[] seen = mseen;
558         for (i = j = 1; i < outLearnt.size(); i++) {
559             IConstr r = voc.getReason(outLearnt.get(i));
560             if (r == null) {
561                 outLearnt.moveTo(j++, i);
562             } else {
563                 assert r.get(0) == neg(outLearnt.get(i));
564                 for (int k = 1; k < r.size(); k++)
565                     if (!seen[r.get(k) >> 1] && (voc.getLevel(r.get(k)) != 0)) {
566                         outLearnt.moveTo(j++, i);
567                         break;
568                     }
569             }
570         }
571         outLearnt.shrink(i - j);
572         stats.reducedliterals += (i - j);
573     }
574 
575     private final IVecInt analyzetoclear = new VecInt();
576 
577     private final IVecInt analyzestack = new VecInt();
578 
579     // Taken from MiniSAT 1.14
580     private void expensiveSimplification(IVecInt outLearnt) {
581         // Simplify conflict clause (a lot):
582         //
583         int i, j;
584         // (maintain an abstraction of levels involved in conflict)
585         analyzetoclear.clear();
586         outLearnt.copyTo(analyzetoclear);
587         for (i = 1, j = 1; i < outLearnt.size(); i++)
588             if (voc.getReason(outLearnt.get(i)) == null
589                     || !analyzeRemovable(outLearnt.get(i)))
590                 outLearnt.moveTo(j++, i);
591         outLearnt.shrink(i - j);
592         stats.reducedliterals += (i - j);
593     }
594 
595     // Check if 'p' can be removed. 'min_level' is used to abort early if
596     // visiting literals at a level that cannot be removed.
597     //
598     private boolean analyzeRemovable(int p) {
599         assert voc.getReason(p) != null;
600         analyzestack.clear();
601         analyzestack.push(p);
602         final boolean[] seen = mseen;
603         int top = analyzetoclear.size();
604         while (analyzestack.size() > 0) {
605             int q = analyzestack.last();
606             assert voc.getReason(q) != null;
607             Constr c = voc.getReason(q);
608             analyzestack.pop();
609             assert c.get(0) == neg(q);
610             for (int i = 1; i < c.size(); i++) {
611                 int l = c.get(i);
612                 if (!seen[var(l)] && voc.getLevel(l) != 0) {
613                     if (voc.getReason(l) == null) {
614                         for (int j = top; j < analyzetoclear.size(); j++)
615                             seen[analyzetoclear.get(j) >> 1] = false;
616                         analyzetoclear.shrink(analyzetoclear.size() - top);
617                         return false;
618                     }
619                     seen[l >> 1] = true;
620                     analyzestack.push(l);
621                     analyzetoclear.push(l);
622                 }
623             }
624         }
625 
626         return true;
627     }
628 
629     /**
630      * decode the internal representation of a literal into Dimacs format.
631      * 
632      * @param p
633      *            the literal in internal representation
634      * @return the literal in dimacs representation
635      */
636     public static int decode2dimacs(int p) {
637         return ((p & 1) == 0 ? 1 : -1) * (p >> 1);
638     }
639 
640     /**
641      * 
642      */
643     protected void undoOne() {
644         // recupere le dernier litteral affecte
645         int p = trail.last();
646         assert p > 1;
647         assert voc.getLevel(p) >= 0;
648         int x = p >> 1;
649         // desaffecte la variable
650         voc.unassign(p);
651         voc.setReason(p, null);
652         voc.setLevel(p, -1);
653         // met a jour l'heuristique
654         order.undo(x);
655         // depile le litteral des affectations
656         trail.pop();
657         // met a jour les contraintes apres desaffectation du litteral :
658         // normalement, il n'y a rien a faire ici pour les prouveurs de type
659         // Chaff??
660         IVec<Undoable> undos = voc.undos(p);
661         assert undos != null;
662         while (undos.size() > 0) {
663             undos.last().undo(p);
664             undos.pop();
665         }
666     }
667 
668     /**
669      * Propagate activity to a constraint
670      * 
671      * @param confl
672      *            a constraint
673      */
674     public void claBumpActivity(Constr confl) {
675         confl.incActivity(claInc);
676         if (confl.getActivity() > CLAUSE_RESCALE_BOUND)
677             claRescalActivity();
678         // for (int i = 0; i < confl.size(); i++) {
679         // varBumpActivity(confl.get(i));
680         // }
681     }
682 
683     public void varBumpActivity(int p) {
684         order.updateVar(p);
685     }
686 
687     private void claRescalActivity() {
688         for (int i = 0; i < learnts.size(); i++) {
689             learnts.get(i).rescaleBy(CLAUSE_RESCALE_FACTOR);
690         }
691         claInc *= CLAUSE_RESCALE_FACTOR;
692     }
693 
694     /**
695      * @return null if not conflict is found, else a conflicting constraint.
696      */
697     public Constr propagate() {
698         while (qhead < trail.size()) {
699             stats.propagations++;
700             int p = trail.get(qhead++);
701             slistener.propagating(decode2dimacs(p));
702             order.assignLiteral(p);
703             // p is the literal to propagate
704             // Moved original MiniSAT code to dsfactory to avoid
705             // watches manipulation in counter Based clauses for instance.
706             assert p > 1;
707             IVec<Propagatable> constrs = dsfactory.getWatchesFor(p);
708 
709             final int size = constrs.size();
710             for (int i = 0; i < size; i++) {
711                 stats.inspects++;
712                 if (!constrs.get(i).propagate(this, p)) {
713                     // Constraint is conflicting: copy remaining watches to
714                     // watches[p]
715                     // and return constraint
716                     dsfactory.conflictDetectedInWatchesFor(p, i);
717                     qhead = trail.size(); // propQ.clear();
718                     // FIXME enlever le transtypage
719                     return (Constr) constrs.get(i);
720                 }
721             }
722         }
723         return null;
724     }
725 
726     void record(Constr constr) {
727         constr.assertConstraint(this);
728         slistener.adding(decode2dimacs(constr.get(0)));
729         if (constr.size() == 1) {
730             stats.learnedliterals++;
731         } else {
732             learner.learns(constr);
733         }
734     }
735 
736     /**
737      * @return false ssi conflit imm?diat.
738      */
739     public boolean assume(int p) {
740         // Precondition: assume propagation queue is empty
741         assert trail.size() == qhead;
742         trailLim.push(trail.size());
743         return enqueue(p);
744     }
745 
746     /**
747      * Revert to the state before the last push()
748      */
749     private void cancel() {
750         // assert trail.size() == qhead || !undertimeout;
751         int decisionvar = trail.unsafeGet(trailLim.last());
752         slistener.backtracking(decode2dimacs(decisionvar));
753         for (int c = trail.size() - trailLim.last(); c > 0; c--) {
754             undoOne();
755         }
756         trailLim.pop();
757     }
758 
759     /**
760      * Restore literals
761      */
762     private void cancelLearntLiterals() {
763         // assert trail.size() == qhead || !undertimeout;
764 
765         for (int c = trail.size() - rootLevel; c > 0; c--) {
766             undoOne();
767         }
768         qhead = trail.size();
769     }
770 
771     /**
772      * Cancel several levels of assumptions
773      * 
774      * @param level
775      */
776     protected void cancelUntil(int level) {
777         while (decisionLevel() > level) {
778             cancel();
779         }
780         qhead = trail.size();
781     }
782 
783     private final Handle<Constr> learntConstraint = new Handle<Constr>();
784 
785     private boolean[] fullmodel;
786 
787     Lbool search(long nofConflicts) {
788         assert rootLevel == decisionLevel();
789         stats.starts++;
790         int conflictC = 0;
791 
792         // varDecay = 1 / params.varDecay;
793         order.setVarDecay(1 / params.getVarDecay());
794         claDecay = 1 / params.getClaDecay();
795 
796         do {
797             slistener.beginLoop();
798             // propage les clauses unitaires
799             Constr confl = propagate();
800             assert trail.size() == qhead;
801 
802             if (confl == null) {
803                 // No conflict found
804                 // simpliFYDB() prevents a correct use of
805                 // constraints removal.
806                 // if (decisionLevel() == 0) {
807                 // // Simplify the set of problem clause
808                 // // iff rootLevel==0
809                 // stats.rootSimplifications++;
810                 // boolean ret = simplifyDB();
811                 // assert ret;
812                 // }
813                 // was learnts.size() - nAssigns() > nofLearnts
814                 // if (nofLearnts.obj >= 0 && learnts.size() > nofLearnts.obj) {
815                 assert nAssigns() <= voc.realnVars();
816                 if (nAssigns() == voc.realnVars()) {
817                     slistener.solutionFound();
818                     modelFound();
819                     return Lbool.TRUE;
820                 }
821                 if (conflictC >= nofConflicts) {
822                     // Reached bound on number of conflicts
823                     // Force a restart
824                     cancelUntil(rootLevel);
825                     return Lbool.UNDEFINED;
826                 }
827                 if (needToReduceDB) {
828                     reduceDB();
829                     needToReduceDB = false;
830                     // Runtime.getRuntime().gc();
831                 }
832                 // New variable decision
833                 stats.decisions++;
834                 int p = order.select();
835                 assert p > 1;
836                 slistener.assuming(decode2dimacs(p));
837                 boolean ret = assume(p);
838                 assert ret;
839             } else {
840                 // un conflit apparait
841                 stats.conflicts++;
842                 conflictC++;
843                 slistener.conflictFound();
844                 freeMem.newConflict();
845                 if (decisionLevel() == rootLevel) {
846                     // on est a la racine, la formule est inconsistante
847                     return Lbool.FALSE;
848                 }
849                 // analyze conflict
850                 int backtrackLevel = analyze(confl, learntConstraint);
851                 assert backtrackLevel < decisionLevel();
852                 cancelUntil(Math.max(backtrackLevel, rootLevel));
853                 assert (decisionLevel() >= rootLevel)
854                         && (decisionLevel() >= backtrackLevel);
855                 if (learntConstraint.obj == null) {
856                     return Lbool.FALSE;
857                 }
858                 record(learntConstraint.obj);
859                 learntConstraint.obj = null;
860                 decayActivities();
861             }
862         } while (undertimeout);
863         return Lbool.UNDEFINED; // timeout occured
864     }
865 
866     /**
867      * 
868      */
869     void modelFound() {
870         model = new int[trail.size()];
871         fullmodel = new boolean[nVars()];
872         int index = 0;
873         for (int i = 1; i <= voc.nVars(); i++) {
874             if (voc.belongsToPool(i)) {
875                 int p = voc.getFromPool(i);
876                 if (!voc.isUnassigned(p)) {
877                     model[index++] = voc.isSatisfied(p) ? i : -i;
878                     fullmodel[i - 1] = voc.isSatisfied(p);
879                 }
880             }
881         }
882         assert index == model.length;
883         cancelUntil(rootLevel);
884     }
885 
886     public boolean model(int var) {
887         if (var <= 0 || var > nVars()) {
888             throw new IllegalArgumentException(
889                     "Use a valid Dimacs var id as argument!"); //$NON-NLS-1$
890         }
891         if (fullmodel == null) {
892             throw new UnsupportedOperationException(
893                     "Call the solve method first!!!"); //$NON-NLS-1$
894         }
895         return fullmodel[var - 1];
896     }
897 
898     /**
899      * 
900      */
901     protected void reduceDB() {
902         reduceDB(claInc / learnts.size());
903     }
904 
905     public void clearLearntClauses() {
906         for (Constr c : learnts)
907             c.remove();
908         learnts.clear();
909     }
910 
911     protected void reduceDB(double lim) {
912         int i, j;
913         sortOnActivity();
914         stats.reduceddb++;
915         for (i = j = 0; i < learnts.size() / 2; i++) {
916             Constr c = learnts.get(i);
917             if (c.locked()) {
918                 learnts.set(j++, learnts.get(i));
919             } else {
920                 c.remove();
921             }
922         }
923         for (; i < learnts.size(); i++) {
924             // Constr c = learnts.get(i);
925             // if (!c.locked() && (c.getActivity() < lim)) {
926             // c.remove();
927             // } else {
928             learnts.set(j++, learnts.get(i));
929             // }
930         }
931         System.out.println("c cleaning " + (learnts.size() - j) //$NON-NLS-1$
932                 + " clauses out of " + learnts.size() + " for limit " + lim); //$NON-NLS-1$ //$NON-NLS-2$
933         learnts.shrinkTo(j);
934     }
935 
936     /**
937      * @param learnts
938      */
939     private void sortOnActivity() {
940         learnts.sort(comparator);
941     }
942 
943     /**
944      * 
945      */
946     protected void decayActivities() {
947         order.varDecayActivity();
948         claDecayActivity();
949     }
950 
951     /**
952      * 
953      */
954     private void claDecayActivity() {
955         claInc *= claDecay;
956     }
957 
958     /**
959      * @return true iff the set of constraints is satisfiable, else false.
960      */
961     public boolean isSatisfiable() throws TimeoutException {
962         return isSatisfiable(VecInt.EMPTY);
963     }
964 
965     private double timebegin = 0;
966 
967     private boolean needToReduceDB;
968 
969     private ConflictTimer freeMem;
970     
971     public boolean isSatisfiable(IVecInt assumps) throws TimeoutException {
972         Lbool status = Lbool.UNDEFINED;
973         
974         order.init();
975         learner.init();
976         restarter.init(params);
977         timebegin = System.currentTimeMillis();
978         slistener.start();
979         model = null; // forget about previous model
980         fullmodel = null;
981 
982         // propagate constraints
983         if (propagate() != null) {
984             slistener.end(Lbool.FALSE);
985             cancelUntil(0);
986             return false;
987         }
988 
989         // push incremental assumptions
990         for (int q : assumps) {
991             if (!assume(voc.getFromPool(q)) || (propagate() != null)) {
992                 slistener.end(Lbool.FALSE);
993                 cancelUntil(0);
994                 return false;
995             }
996         }
997         rootLevel = decisionLevel();
998 
999         TimerTask stopMe = new TimerTask() {
1000             @Override
1001             public void run() {
1002                 undertimeout = false;
1003             }
1004         };
1005         undertimeout = true;
1006         Timer timer = new Timer(true);
1007         timer.schedule(stopMe, timeout);
1008         needToReduceDB = false;
1009 
1010         final long memorybound = Runtime.getRuntime().freeMemory() / 10;
1011         freeMem = new ConflictTimer(500) {
1012             private static final long serialVersionUID = 1L;
1013                     @Override
1014                     void run() {
1015                         long freemem = Runtime.getRuntime().freeMemory();
1016                         // System.out.println("c Free memory "+freemem);
1017                         if (freemem < memorybound) {
1018                             // Reduce the set of learnt clauses
1019                             needToReduceDB = true;
1020                         }
1021                     }
1022                 };
1023         // Solve
1024         while ((status == Lbool.UNDEFINED) && undertimeout) {
1025             status = search(restarter.nextRestartNumberOfConflict());
1026             // System.out.println("c speed "+(stats.decisions/((System.currentTimeMillis()-timebegin)/1000))+" dec/s, "+stats.starts+"/"+stats.conflicts);
1027             restarter.onRestart();
1028         }
1029 
1030         cancelUntil(0);
1031         timer.cancel();
1032         slistener.end(status);
1033         if (!undertimeout) {
1034             throw new TimeoutException(" Timeout (" + timeout + "s) exceeded"); //$NON-NLS-1$//$NON-NLS-2$
1035         }
1036         return status == Lbool.TRUE;
1037     }
1038 
1039     public SolverStats getStats() {
1040         return stats;
1041     }
1042 
1043     public IOrder<L> getOrder() {
1044         return order;
1045     }
1046 
1047     public void setOrder(IOrder<L> h) {
1048         order = h;
1049         order.setLits(voc);
1050     }
1051 
1052     public L getVocabulary() {
1053         return voc;
1054     }
1055 
1056     public void reset() {
1057         // FIXME verify that cleanup is OK
1058         voc.resetPool();
1059         dsfactory.reset();
1060         constrs.clear();
1061         learnts.clear();
1062         stats.reset();
1063     }
1064 
1065     public int nVars() {
1066         return voc.nVars();
1067     }
1068 
1069     /**
1070      * @param constr
1071      *            a constraint implementing the Constr interface.
1072      * @return a reference to the constraint for external use.
1073      */
1074     protected IConstr addConstr(Constr constr) {
1075         if (constr != null) {
1076             constrs.push(constr);
1077         }
1078         return constr;
1079     }
1080 
1081     public DataStructureFactory<L> getDSFactory() {
1082         return dsfactory;
1083     }
1084 
1085     public IVecInt getOutLearnt() {
1086         return outLearnt;
1087     }
1088 
1089     /**
1090      * returns the ith constraint in the solver.
1091      * 
1092      * @param i
1093      *            the constraint number (begins at 0)
1094      * @return the ith constraint
1095      */
1096     public IConstr getIthConstr(int i) {
1097         return constrs.get(i);
1098     }
1099 
1100     /*
1101      * (non-Javadoc)
1102      * 
1103      * @see org.sat4j.specs.ISolver#printStat(java.io.PrintStream,
1104      *      java.lang.String)
1105      */
1106     public void printStat(PrintStream out, String prefix) {
1107         printStat(new PrintWriter(out), prefix);
1108     }
1109 
1110     public void printStat(PrintWriter out, String prefix) {
1111         stats.printStat(out, prefix);
1112         double cputime = (System.currentTimeMillis() - timebegin) / 1000;
1113         out.println(prefix + "speed (decisions/second)\t: " + stats.decisions //$NON-NLS-1$
1114                 / cputime);
1115         order.printStat(out, prefix);
1116     }
1117 
1118     /*
1119      * (non-Javadoc)
1120      * 
1121      * @see java.lang.Object#toString()
1122      */
1123     public String toString(String prefix) {
1124         StringBuilder stb = new StringBuilder();
1125         Object[] objs = { analyzer, dsfactory, learner, params, order,
1126                 simplifier, restarter };
1127         // stb.append(prefix);
1128         stb.append("--- Begin Solver configuration ---"); //$NON-NLS-1$
1129         stb.append("\n"); //$NON-NLS-1$
1130         for (Object o : objs) {
1131             stb.append(prefix);
1132             stb.append(o.toString());
1133             stb.append("\n"); //$NON-NLS-1$
1134         }
1135         stb.append(prefix);
1136         stb.append("--- End Solver configuration ---"); //$NON-NLS-1$
1137         return stb.toString();
1138     }
1139 
1140     /*
1141      * (non-Javadoc)
1142      * 
1143      * @see java.lang.Object#toString()
1144      */
1145     @Override
1146     public String toString() {
1147         return toString(""); //$NON-NLS-1$
1148     }
1149 
1150     public int getTimeout() {
1151         return (int)(timeout/1000);
1152     }
1153 
1154     public void setExpectedNumberOfClauses(int nb) {
1155         constrs.ensure(nb);
1156     }
1157 
1158     public Map<String, Number> getStat() {
1159         return stats.toMap();
1160     }
1161 
1162     public int[] findModel() throws TimeoutException {
1163         if (isSatisfiable()) {
1164             return model();
1165         }
1166         // DLB findbugs ok
1167         // A zero length array would mean that the formula is a tautology.
1168         return null;
1169     }
1170 
1171     public int[] findModel(IVecInt assumps) throws TimeoutException {
1172         if (isSatisfiable(assumps)) {
1173             return model();
1174         }
1175         // DLB findbugs ok
1176         // A zero length array would mean that the formula is a tautology.
1177         return null;
1178     }
1179 
1180 }
1181 
1182 class ActivityComparator implements Comparator<Constr>, Serializable {
1183 
1184     private static final long serialVersionUID = 1L;
1185 
1186     /*
1187      * (non-Javadoc)
1188      * 
1189      * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
1190      */
1191     public int compare(Constr c1, Constr c2) {
1192         return (int) Math.round(c1.getActivity() - c2.getActivity());
1193     }
1194 }
1195 
1196 abstract class ConflictTimer implements Serializable {
1197     
1198     private int counter;
1199     private final int bound;
1200     
1201     ConflictTimer(final int bound)  {
1202         this.bound = bound;
1203         counter = 0;
1204     }
1205     
1206     void reset() {
1207         counter = 0;
1208     }
1209     
1210     void newConflict() {
1211         counter++;
1212         if (counter==bound) {
1213             run();
1214             counter = 0;
1215         }
1216     }
1217     
1218     abstract void run();
1219 }