View Javadoc

1   /*******************************************************************************
2    * SAT4J: a SATisfiability library for Java Copyright (C) 2004, 2012 Artois University and CNRS
3    *
4    * All rights reserved. This program and the accompanying materials
5    * are made available under the terms of the Eclipse Public License v1.0
6    * which accompanies this distribution, and is available at
7    *  http://www.eclipse.org/legal/epl-v10.html
8    *
9    * Alternatively, the contents of this file may be used under the terms of
10   * either the GNU Lesser General Public License Version 2.1 or later (the
11   * "LGPL"), in which case the provisions of the LGPL are applicable instead
12   * of those above. If you wish to allow use of your version of this file only
13   * under the terms of the LGPL, and not to allow others to use your version of
14   * this file under the terms of the EPL, indicate your decision by deleting
15   * the provisions above and replace them with the notice and other provisions
16   * required by the LGPL. If you do not delete the provisions above, a recipient
17   * may use your version of this file under the terms of the EPL or the LGPL.
18   *
19   * Based on the original MiniSat specification from:
20   *
21   * An extensible SAT solver. Niklas Een and Niklas Sorensson. Proceedings of the
22   * Sixth International Conference on Theory and Applications of Satisfiability
23   * Testing, LNCS 2919, pp 502-518, 2003.
24   *
25   * See www.minisat.se for the original solver in C++.
26   *
27   * Contributors:
28   *   CRIL - initial API and implementation
29   *******************************************************************************/
30  package org.sat4j.tools;
31  
32  import java.io.PrintStream;
33  import java.io.PrintWriter;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.concurrent.atomic.AtomicInteger;
38  
39  import org.sat4j.core.ASolverFactory;
40  import org.sat4j.core.ConstrGroup;
41  import org.sat4j.core.LiteralsUtils;
42  import org.sat4j.core.Vec;
43  import org.sat4j.core.VecInt;
44  import org.sat4j.minisat.core.Counter;
45  import org.sat4j.specs.ContradictionException;
46  import org.sat4j.specs.IConstr;
47  import org.sat4j.specs.ISolver;
48  import org.sat4j.specs.ISolverService;
49  import org.sat4j.specs.IVec;
50  import org.sat4j.specs.IVecInt;
51  import org.sat4j.specs.SearchListener;
52  import org.sat4j.specs.TimeoutException;
53  import org.sat4j.specs.UnitClauseProvider;
54  import org.sat4j.specs.UnitPropagationListener;
55  
56  /**
57   * A class allowing to run several solvers in parallel.
58   * 
59   * Note that each solver will have its own copy of the CNF, so it is not a
60   * memory efficient implementation. There is no sharing of information yet
61   * between the solvers.
62   * 
63   * @author leberre
64   * 
65   * @param <S>
66   *            the type of the solver (ISolver of IPBSolver)
67   */
68  public class ManyCore<S extends ISolver> extends
69          SearchListenerAdapter<ISolverService> implements ISolver,
70          OutcomeListener, UnitClauseProvider {
71  
72      private static final int NORMAL_SLEEP = 500;
73  
74      private static final int FAST_SLEEP = 50;
75  
76      /**
77  	 * 
78  	 */
79      private static final long serialVersionUID = 1L;
80  
81      private final String[] availableSolvers; // = { };
82  
83      protected final List<S> solvers;
84      protected final int numberOfSolvers;
85      private int winnerId;
86      private boolean resultFound;
87      private AtomicInteger remainingSolvers;
88      private volatile int sleepTime;
89      private volatile boolean solved;
90      private final IVecInt sharedUnitClauses = new VecInt();
91  
92      private final IVec<Counter> solversStats = new Vec<Counter>();
93  
94      public ManyCore(ASolverFactory<S> factory, String... solverNames) {
95          this.availableSolvers = solverNames;
96          this.numberOfSolvers = solverNames.length;
97          this.solvers = new ArrayList<S>(this.numberOfSolvers);
98          S solver;
99          for (int i = 0; i < this.numberOfSolvers; i++) {
100             solver = factory.createSolverByName(this.availableSolvers[i]);
101             solver.setSearchListener(this);
102             solver.setUnitClauseProvider(this);
103             this.solvers.add(solver);
104             this.solversStats.push(new Counter(0));
105         }
106     }
107 
108     /**
109      * Create a parallel solver from a list of solvers and a list of names.
110      * 
111      * @param names
112      *            a String to describe each solver in the messages.
113      * @param solverObjects
114      *            the solvers
115      */
116     public ManyCore(String[] names, S... solverObjects) {
117         this(solverObjects);
118         for (int i = 0; i < names.length; i++) {
119             this.availableSolvers[i] = names[i];
120         }
121     }
122 
123     public ManyCore(S... solverObjects) {
124         this.availableSolvers = new String[solverObjects.length];
125         for (int i = 0; i < solverObjects.length; i++) {
126             this.availableSolvers[i] = "solver" + i;
127         }
128         this.numberOfSolvers = solverObjects.length;
129         this.solvers = new ArrayList<S>(this.numberOfSolvers);
130         for (int i = 0; i < this.numberOfSolvers; i++) {
131             this.solvers.add(solverObjects[i]);
132             solverObjects[i].setSearchListener(this);
133             solverObjects[i].setUnitClauseProvider(this);
134             this.solversStats.push(new Counter(0));
135         }
136     }
137 
138     public void addAllClauses(IVec<IVecInt> clauses)
139             throws ContradictionException {
140         for (int i = 0; i < this.numberOfSolvers; i++) {
141             this.solvers.get(i).addAllClauses(clauses);
142         }
143     }
144 
145     public IConstr addAtLeast(IVecInt literals, int degree)
146             throws ContradictionException {
147         ConstrGroup group = new ConstrGroup(false);
148         for (int i = 0; i < this.numberOfSolvers; i++) {
149             group.add(this.solvers.get(i).addAtLeast(literals, degree));
150         }
151         return group;
152     }
153 
154     public IConstr addAtMost(IVecInt literals, int degree)
155             throws ContradictionException {
156         ConstrGroup group = new ConstrGroup(false);
157         for (int i = 0; i < this.numberOfSolvers; i++) {
158             group.add(this.solvers.get(i).addAtMost(literals, degree));
159         }
160         return group;
161     }
162 
163     public IConstr addExactly(IVecInt literals, int n)
164             throws ContradictionException {
165         ConstrGroup group = new ConstrGroup(false);
166         for (int i = 0; i < this.numberOfSolvers; i++) {
167             group.add(this.solvers.get(i).addExactly(literals, n));
168         }
169         return group;
170     }
171 
172     public IConstr addClause(IVecInt literals) throws ContradictionException {
173         ConstrGroup group = new ConstrGroup(false);
174         for (int i = 0; i < this.numberOfSolvers; i++) {
175             group.add(this.solvers.get(i).addClause(literals));
176         }
177         return group;
178     }
179 
180     public void clearLearntClauses() {
181         for (int i = 0; i < this.numberOfSolvers; i++) {
182             this.solvers.get(i).clearLearntClauses();
183         }
184     }
185 
186     public void expireTimeout() {
187         for (int i = 0; i < this.numberOfSolvers; i++) {
188             this.solvers.get(i).expireTimeout();
189         }
190         this.sleepTime = FAST_SLEEP;
191     }
192 
193     public Map<String, Number> getStat() {
194         return this.solvers.get(this.winnerId).getStat();
195     }
196 
197     public int getTimeout() {
198         return this.solvers.get(0).getTimeout();
199     }
200 
201     public long getTimeoutMs() {
202         return this.solvers.get(0).getTimeoutMs();
203     }
204 
205     public int newVar() {
206         throw new UnsupportedOperationException();
207     }
208 
209     public int newVar(int howmany) {
210         int result = 0;
211         for (int i = 0; i < this.numberOfSolvers; i++) {
212             result = this.solvers.get(i).newVar(howmany);
213         }
214         return result;
215     }
216 
217     @Deprecated
218     public void printStat(PrintStream out, String prefix) {
219         for (int i = 0; i < this.numberOfSolvers; i++) {
220             out.printf(
221                     "%s>>>>>>>>>> Solver number %d (%d answers) <<<<<<<<<<<<<<<<<<%n",
222                     prefix, i, this.solversStats.get(i).getValue());
223             this.solvers.get(i).printStat(out, prefix);
224         }
225     }
226 
227     public void printStat(PrintWriter out, String prefix) {
228         for (int i = 0; i < this.numberOfSolvers; i++) {
229             out.printf(
230                     "%s>>>>>>>>>> Solver number %d (%d answers) <<<<<<<<<<<<<<<<<<%n",
231                     prefix, i, this.solversStats.get(i).getValue());
232             this.solvers.get(i).printStat(out, prefix);
233         }
234     }
235 
236     public boolean removeConstr(IConstr c) {
237         if (c instanceof ConstrGroup) {
238             ConstrGroup group = (ConstrGroup) c;
239             boolean removed = true;
240             IConstr toRemove;
241             for (int i = 0; i < this.numberOfSolvers; i++) {
242                 toRemove = group.getConstr(i);
243                 if (toRemove != null) {
244                     removed = removed
245                             & this.solvers.get(i).removeConstr(toRemove);
246                 }
247             }
248             return removed;
249         }
250         throw new IllegalArgumentException(
251                 "Can only remove a group of constraints!");
252     }
253 
254     public void reset() {
255         for (int i = 0; i < this.numberOfSolvers; i++) {
256             this.solvers.get(i).reset();
257         }
258         sharedUnitClauses.clear();
259     }
260 
261     public void setExpectedNumberOfClauses(int nb) {
262         for (int i = 0; i < this.numberOfSolvers; i++) {
263             this.solvers.get(i).setExpectedNumberOfClauses(nb);
264         }
265     }
266 
267     public void setTimeout(int t) {
268         for (int i = 0; i < this.numberOfSolvers; i++) {
269             this.solvers.get(i).setTimeout(t);
270         }
271     }
272 
273     public void setTimeoutMs(long t) {
274         for (int i = 0; i < this.numberOfSolvers; i++) {
275             this.solvers.get(i).setTimeoutMs(t);
276         }
277     }
278 
279     public void setTimeoutOnConflicts(int count) {
280         for (int i = 0; i < this.numberOfSolvers; i++) {
281             this.solvers.get(i).setTimeoutOnConflicts(count);
282         }
283     }
284 
285     public String toString(String prefix) {
286         StringBuffer res = new StringBuffer();
287         res.append(prefix);
288         res.append("ManyCore solver with ");
289         res.append(this.numberOfSolvers);
290         res.append(" solvers running in parallel");
291         res.append("\n");
292         for (int i = 0; i < this.numberOfSolvers; i++) {
293             res.append(prefix);
294             res.append(">>>>>>>>>> Solver number ");
295             res.append(i);
296             res.append(" <<<<<<<<<<<<<<<<<<\n");
297             res.append(this.solvers.get(i).toString(prefix));
298             if (i < this.numberOfSolvers - 1) {
299                 res.append("\n");
300             }
301         }
302         return res.toString();
303     }
304 
305     public int[] findModel() throws TimeoutException {
306         if (isSatisfiable()) {
307             return model();
308         }
309         // A zero length array would mean that the formula is a tautology.
310         return null;
311     }
312 
313     public int[] findModel(IVecInt assumps) throws TimeoutException {
314         if (isSatisfiable(assumps)) {
315             return model();
316         }
317         // A zero length array would mean that the formula is a tautology.
318         return null;
319     }
320 
321     public boolean isSatisfiable() throws TimeoutException {
322         return isSatisfiable(VecInt.EMPTY, false);
323     }
324 
325     public synchronized boolean isSatisfiable(IVecInt assumps,
326             boolean globalTimeout) throws TimeoutException {
327         this.remainingSolvers = new AtomicInteger(this.numberOfSolvers);
328         this.solved = false;
329         for (int i = 0; i < this.numberOfSolvers; i++) {
330             new Thread(new RunnableSolver(i, this.solvers.get(i), assumps,
331                     globalTimeout, this)).start();
332         }
333         try {
334             this.sleepTime = NORMAL_SLEEP;
335             do {
336                 wait(this.sleepTime);
337             } while (this.remainingSolvers.get() > 0);
338         } catch (InterruptedException e) {
339             // will happen when one solver found a solution
340         }
341         if (!this.solved) {
342             assert this.remainingSolvers.get() == 0;
343             throw new TimeoutException();
344         }
345         return this.resultFound;
346     }
347 
348     public boolean isSatisfiable(boolean globalTimeout) throws TimeoutException {
349         throw new UnsupportedOperationException();
350     }
351 
352     public boolean isSatisfiable(IVecInt assumps) throws TimeoutException {
353         throw new UnsupportedOperationException();
354     }
355 
356     public int[] model() {
357         return this.solvers.get(this.winnerId).model();
358     }
359 
360     public boolean model(int var) {
361         return this.solvers.get(this.winnerId).model(var);
362     }
363 
364     public int nConstraints() {
365         return this.solvers.get(0).nConstraints();
366     }
367 
368     public int nVars() {
369         return this.solvers.get(0).nVars();
370     }
371 
372     public void printInfos(PrintWriter out, String prefix) {
373         for (int i = 0; i < this.numberOfSolvers; i++) {
374             out.printf("%s>>>>>>>>>> Solver number %d <<<<<<<<<<<<<<<<<<%n",
375                     prefix, i);
376             this.solvers.get(i).printInfos(out, prefix);
377         }
378     }
379 
380     public synchronized void onFinishWithAnswer(boolean finished,
381             boolean result, int index) {
382         if (finished && !this.solved) {
383             this.winnerId = index;
384             this.solversStats.get(index).inc();
385             this.solved = true;
386             this.resultFound = result;
387             for (int i = 0; i < this.numberOfSolvers; i++) {
388                 if (i != this.winnerId) {
389                     this.solvers.get(i).expireTimeout();
390                 }
391             }
392             this.sleepTime = FAST_SLEEP;
393             System.out.println(getLogPrefix() + "And the winner is "
394                     + this.availableSolvers[this.winnerId]);
395         }
396         this.remainingSolvers.getAndDecrement();
397     }
398 
399     public boolean isDBSimplificationAllowed() {
400         return this.solvers.get(0).isDBSimplificationAllowed();
401     }
402 
403     public void setDBSimplificationAllowed(boolean status) {
404         for (int i = 0; i < this.numberOfSolvers; i++) {
405             this.solvers.get(0).setDBSimplificationAllowed(status);
406         }
407     }
408 
409     public <I extends ISolverService> void setSearchListener(
410             SearchListener<I> sl) {
411         for (int i = 0; i < this.numberOfSolvers; i++) {
412             this.solvers.get(i).setSearchListener(sl);
413         }
414     }
415 
416     /**
417      * @since 2.2
418      */
419     public <I extends ISolverService> SearchListener<I> getSearchListener() {
420         return this.solvers.get(0).getSearchListener();
421     }
422 
423     public int nextFreeVarId(boolean reserve) {
424         int res = -1;
425         for (int i = 0; i < this.numberOfSolvers; ++i) {
426             res = this.solvers.get(i).nextFreeVarId(reserve);
427         }
428         return res;
429     }
430 
431     public IConstr addBlockingClause(IVecInt literals)
432             throws ContradictionException {
433         ConstrGroup group = new ConstrGroup(false);
434         for (int i = 0; i < this.numberOfSolvers; i++) {
435             group.add(this.solvers.get(i).addBlockingClause(literals));
436         }
437         return group;
438     }
439 
440     public boolean removeSubsumedConstr(IConstr c) {
441         if (c instanceof ConstrGroup) {
442             ConstrGroup group = (ConstrGroup) c;
443             boolean removed = true;
444             IConstr toRemove;
445             for (int i = 0; i < this.numberOfSolvers; i++) {
446                 toRemove = group.getConstr(i);
447                 if (toRemove != null) {
448                     removed = removed
449                             & this.solvers.get(i)
450                                     .removeSubsumedConstr(toRemove);
451                 }
452             }
453             return removed;
454         }
455         throw new IllegalArgumentException(
456                 "Can only remove a group of constraints!");
457     }
458 
459     public boolean isVerbose() {
460         return this.solvers.get(0).isVerbose();
461     }
462 
463     public void setVerbose(boolean value) {
464         for (int i = 0; i < this.numberOfSolvers; i++) {
465             this.solvers.get(i).setVerbose(value);
466         }
467     }
468 
469     public void setLogPrefix(String prefix) {
470         for (int i = 0; i < this.numberOfSolvers; i++) {
471             this.solvers.get(i).setLogPrefix(prefix);
472         }
473 
474     }
475 
476     public String getLogPrefix() {
477         return this.solvers.get(0).getLogPrefix();
478     }
479 
480     public IVecInt unsatExplanation() {
481         return this.solvers.get(this.winnerId).unsatExplanation();
482     }
483 
484     public int[] primeImplicant() {
485         return this.solvers.get(this.winnerId).primeImplicant();
486     }
487 
488     /**
489      * @since 2.3.2
490      */
491     public boolean primeImplicant(int p) {
492         return this.solvers.get(this.winnerId).primeImplicant(p);
493     }
494 
495     public List<S> getSolvers() {
496         return new ArrayList<S>(this.solvers);
497     }
498 
499     public int[] modelWithInternalVariables() {
500         return this.solvers.get(this.winnerId).modelWithInternalVariables();
501     }
502 
503     public int realNumberOfVariables() {
504         return this.solvers.get(0).realNumberOfVariables();
505     }
506 
507     public void registerLiteral(int p) {
508         for (int i = 0; i < this.numberOfSolvers; i++) {
509             this.solvers.get(i).registerLiteral(p);
510         }
511 
512     }
513 
514     public boolean isSolverKeptHot() {
515         return this.solvers.get(0).isSolverKeptHot();
516     }
517 
518     public void setKeepSolverHot(boolean value) {
519         for (int i = 0; i < this.numberOfSolvers; i++) {
520             this.solvers.get(i).setKeepSolverHot(value);
521         }
522 
523     }
524 
525     public ISolver getSolvingEngine() {
526         throw new UnsupportedOperationException("Not supported yet in ManyCore");
527     }
528 
529     /**
530      * @since 2.3.3
531      */
532     public void printStat(PrintWriter out) {
533         printStat(out, getLogPrefix());
534     }
535 
536     /**
537      * @since 2.3.3
538      */
539     public void printInfos(PrintWriter out) {
540         for (int i = 0; i < this.numberOfSolvers; i++) {
541             out.printf("%s>>>>>>>>>> Solver number %d <<<<<<<<<<<<<<<<<<%n",
542                     getLogPrefix(), i);
543             this.solvers.get(i).printInfos(out);
544         }
545 
546     }
547 
548     @Override
549     public synchronized void learnUnit(int p) {
550         sharedUnitClauses.push(LiteralsUtils.toInternal(p));
551     }
552 
553     public synchronized void provideUnitClauses(UnitPropagationListener upl) {
554         for (int i = 0; i < sharedUnitClauses.size(); i++) {
555             upl.enqueue(sharedUnitClauses.get(i));
556         }
557     }
558 
559     public void setUnitClauseProvider(UnitClauseProvider ucp) {
560         throw new UnsupportedOperationException(
561                 "Does not make sense in the parallel context");
562 
563     }
564 }
565 
566 class RunnableSolver implements Runnable {
567 
568     private final int index;
569     private final ISolver solver;
570     private final OutcomeListener ol;
571     private final IVecInt assumps;
572     private final boolean globalTimeout;
573 
574     public RunnableSolver(int i, ISolver solver, IVecInt assumps,
575             boolean globalTimeout, OutcomeListener ol) {
576         this.index = i;
577         this.solver = solver;
578         this.ol = ol;
579         this.assumps = assumps;
580         this.globalTimeout = globalTimeout;
581     }
582 
583     public void run() {
584         try {
585             boolean result = this.solver.isSatisfiable(this.assumps,
586                     this.globalTimeout);
587             this.ol.onFinishWithAnswer(true, result, this.index);
588         } catch (Exception e) {
589             this.ol.onFinishWithAnswer(false, false, this.index);
590         }
591     }
592 
593 }