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.Vec;
42  import org.sat4j.core.VecInt;
43  import org.sat4j.minisat.core.Counter;
44  import org.sat4j.specs.ContradictionException;
45  import org.sat4j.specs.IConstr;
46  import org.sat4j.specs.ISolver;
47  import org.sat4j.specs.ISolverService;
48  import org.sat4j.specs.IVec;
49  import org.sat4j.specs.IVecInt;
50  import org.sat4j.specs.SearchListener;
51  import org.sat4j.specs.TimeoutException;
52  
53  /**
54   * A class allowing to run several solvers in parallel.
55   * 
56   * Note that each solver will have its own copy of the CNF, so it is not a
57   * memory efficient implementation. There is no sharing of information yet
58   * between the solvers.
59   * 
60   * @author leberre
61   * 
62   * @param <S>
63   *            the type of the solver (ISolver of IPBSolver)
64   */
65  public class ManyCore<S extends ISolver> implements ISolver, OutcomeListener {
66  
67      private static final int NORMAL_SLEEP = 500;
68  
69      private static final int FAST_SLEEP = 50;
70  
71      /**
72  	 * 
73  	 */
74      private static final long serialVersionUID = 1L;
75  
76      private final String[] availableSolvers; // = { };
77  
78      protected final List<S> solvers;
79      protected final int numberOfSolvers;
80      private int winnerId;
81      private boolean resultFound;
82      private AtomicInteger remainingSolvers;
83      private volatile int sleepTime;
84      private volatile boolean solved;
85  
86      private final IVec<Counter> solversStats = new Vec<Counter>();
87  
88      public ManyCore(ASolverFactory<S> factory, String... solverNames) {
89          this.availableSolvers = solverNames;
90          this.numberOfSolvers = solverNames.length;
91          this.solvers = new ArrayList<S>(this.numberOfSolvers);
92          for (int i = 0; i < this.numberOfSolvers; i++) {
93              this.solvers.add(factory
94                      .createSolverByName(this.availableSolvers[i]));
95              this.solversStats.push(new Counter(0));
96          }
97      }
98  
99      /**
100      * Create a parallel solver from a list of solvers and a list of names.
101      * 
102      * @param names
103      *            a String to describe each solver in the messages.
104      * @param solverObjects
105      *            the solvers
106      */
107     public ManyCore(String[] names, S... solverObjects) {
108         this(solverObjects);
109         for (int i = 0; i < names.length; i++) {
110             this.availableSolvers[i] = names[i];
111         }
112     }
113 
114     public ManyCore(S... solverObjects) {
115         this.availableSolvers = new String[solverObjects.length];
116         for (int i = 0; i < solverObjects.length; i++) {
117             this.availableSolvers[i] = "solver" + i;
118         }
119         this.numberOfSolvers = solverObjects.length;
120         this.solvers = new ArrayList<S>(this.numberOfSolvers);
121         for (int i = 0; i < this.numberOfSolvers; i++) {
122             this.solvers.add(solverObjects[i]);
123             this.solversStats.push(new Counter());
124         }
125     }
126 
127     public void addAllClauses(IVec<IVecInt> clauses)
128             throws ContradictionException {
129         for (int i = 0; i < this.numberOfSolvers; i++) {
130             this.solvers.get(i).addAllClauses(clauses);
131         }
132     }
133 
134     public IConstr addAtLeast(IVecInt literals, int degree)
135             throws ContradictionException {
136         ConstrGroup group = new ConstrGroup(false);
137         for (int i = 0; i < this.numberOfSolvers; i++) {
138             group.add(this.solvers.get(i).addAtLeast(literals, degree));
139         }
140         return group;
141     }
142 
143     public IConstr addAtMost(IVecInt literals, int degree)
144             throws ContradictionException {
145         ConstrGroup group = new ConstrGroup(false);
146         for (int i = 0; i < this.numberOfSolvers; i++) {
147             group.add(this.solvers.get(i).addAtMost(literals, degree));
148         }
149         return group;
150     }
151 
152     public IConstr addExactly(IVecInt literals, int n)
153             throws ContradictionException {
154         ConstrGroup group = new ConstrGroup(false);
155         for (int i = 0; i < this.numberOfSolvers; i++) {
156             group.add(this.solvers.get(i).addExactly(literals, n));
157         }
158         return group;
159     }
160 
161     public IConstr addClause(IVecInt literals) throws ContradictionException {
162         ConstrGroup group = new ConstrGroup(false);
163         for (int i = 0; i < this.numberOfSolvers; i++) {
164             group.add(this.solvers.get(i).addClause(literals));
165         }
166         return group;
167     }
168 
169     public void clearLearntClauses() {
170         for (int i = 0; i < this.numberOfSolvers; i++) {
171             this.solvers.get(i).clearLearntClauses();
172         }
173     }
174 
175     public void expireTimeout() {
176         for (int i = 0; i < this.numberOfSolvers; i++) {
177             this.solvers.get(i).expireTimeout();
178         }
179         this.sleepTime = FAST_SLEEP;
180     }
181 
182     public Map<String, Number> getStat() {
183         return this.solvers.get(this.winnerId).getStat();
184     }
185 
186     public int getTimeout() {
187         return this.solvers.get(0).getTimeout();
188     }
189 
190     public long getTimeoutMs() {
191         return this.solvers.get(0).getTimeoutMs();
192     }
193 
194     public int newVar() {
195         throw new UnsupportedOperationException();
196     }
197 
198     public int newVar(int howmany) {
199         int result = 0;
200         for (int i = 0; i < this.numberOfSolvers; i++) {
201             result = this.solvers.get(i).newVar(howmany);
202         }
203         return result;
204     }
205 
206     @Deprecated
207     public void printStat(PrintStream out, String prefix) {
208         for (int i = 0; i < this.numberOfSolvers; i++) {
209             out.printf(
210                     "%s>>>>>>>>>> Solver number %d (%d answers) <<<<<<<<<<<<<<<<<<%n",
211                     prefix, i, this.solversStats.get(i).getValue());
212             this.solvers.get(i).printStat(out, prefix);
213         }
214     }
215 
216     public void printStat(PrintWriter out, String prefix) {
217         for (int i = 0; i < this.numberOfSolvers; i++) {
218             out.printf(
219                     "%s>>>>>>>>>> Solver number %d (%d answers) <<<<<<<<<<<<<<<<<<%n",
220                     prefix, i, this.solversStats.get(i).getValue());
221             this.solvers.get(i).printStat(out, prefix);
222         }
223     }
224 
225     public boolean removeConstr(IConstr c) {
226         if (c instanceof ConstrGroup) {
227             ConstrGroup group = (ConstrGroup) c;
228             boolean removed = true;
229             IConstr toRemove;
230             for (int i = 0; i < this.numberOfSolvers; i++) {
231                 toRemove = group.getConstr(i);
232                 if (toRemove != null) {
233                     removed = removed
234                             & this.solvers.get(i).removeConstr(toRemove);
235                 }
236             }
237             return removed;
238         }
239         throw new IllegalArgumentException(
240                 "Can only remove a group of constraints!");
241     }
242 
243     public void reset() {
244         for (int i = 0; i < this.numberOfSolvers; i++) {
245             this.solvers.get(i).reset();
246         }
247     }
248 
249     public void setExpectedNumberOfClauses(int nb) {
250         for (int i = 0; i < this.numberOfSolvers; i++) {
251             this.solvers.get(i).setExpectedNumberOfClauses(nb);
252         }
253     }
254 
255     public void setTimeout(int t) {
256         for (int i = 0; i < this.numberOfSolvers; i++) {
257             this.solvers.get(i).setTimeout(t);
258         }
259     }
260 
261     public void setTimeoutMs(long t) {
262         for (int i = 0; i < this.numberOfSolvers; i++) {
263             this.solvers.get(i).setTimeoutMs(t);
264         }
265     }
266 
267     public void setTimeoutOnConflicts(int count) {
268         for (int i = 0; i < this.numberOfSolvers; i++) {
269             this.solvers.get(i).setTimeoutOnConflicts(count);
270         }
271     }
272 
273     public String toString(String prefix) {
274         StringBuffer res = new StringBuffer();
275         res.append(prefix);
276         res.append("ManyCore solver with ");
277         res.append(this.numberOfSolvers);
278         res.append(" solvers running in parallel");
279         res.append("\n");
280         for (int i = 0; i < this.numberOfSolvers; i++) {
281             res.append(prefix);
282             res.append(">>>>>>>>>> Solver number ");
283             res.append(i);
284             res.append(" <<<<<<<<<<<<<<<<<<\n");
285             res.append(this.solvers.get(i).toString(prefix));
286             if (i < this.numberOfSolvers - 1) {
287                 res.append("\n");
288             }
289         }
290         return res.toString();
291     }
292 
293     public int[] findModel() throws TimeoutException {
294         if (isSatisfiable()) {
295             return model();
296         }
297         // A zero length array would mean that the formula is a tautology.
298         return null;
299     }
300 
301     public int[] findModel(IVecInt assumps) throws TimeoutException {
302         if (isSatisfiable(assumps)) {
303             return model();
304         }
305         // A zero length array would mean that the formula is a tautology.
306         return null;
307     }
308 
309     public boolean isSatisfiable() throws TimeoutException {
310         return isSatisfiable(VecInt.EMPTY, false);
311     }
312 
313     public synchronized boolean isSatisfiable(IVecInt assumps,
314             boolean globalTimeout) throws TimeoutException {
315         this.remainingSolvers = new AtomicInteger(this.numberOfSolvers);
316         this.solved = false;
317         for (int i = 0; i < this.numberOfSolvers; i++) {
318             new Thread(new RunnableSolver(i, this.solvers.get(i), assumps,
319                     globalTimeout, this)).start();
320         }
321         try {
322             this.sleepTime = NORMAL_SLEEP;
323             do {
324                 wait(this.sleepTime);
325             } while (this.remainingSolvers.get() > 0);
326         } catch (InterruptedException e) {
327             // will happen when one solver found a solution
328         }
329         if (!this.solved) {
330             assert this.remainingSolvers.get() == 0;
331             throw new TimeoutException();
332         }
333         return this.resultFound;
334     }
335 
336     public boolean isSatisfiable(boolean globalTimeout) throws TimeoutException {
337         throw new UnsupportedOperationException();
338     }
339 
340     public boolean isSatisfiable(IVecInt assumps) throws TimeoutException {
341         throw new UnsupportedOperationException();
342     }
343 
344     public int[] model() {
345         return this.solvers.get(this.winnerId).model();
346     }
347 
348     public boolean model(int var) {
349         return this.solvers.get(this.winnerId).model(var);
350     }
351 
352     public int nConstraints() {
353         return this.solvers.get(0).nConstraints();
354     }
355 
356     public int nVars() {
357         return this.solvers.get(0).nVars();
358     }
359 
360     public void printInfos(PrintWriter out, String prefix) {
361         for (int i = 0; i < this.numberOfSolvers; i++) {
362             out.printf("%s>>>>>>>>>> Solver number %d <<<<<<<<<<<<<<<<<<%n",
363                     prefix, i);
364             this.solvers.get(i).printInfos(out, prefix);
365         }
366     }
367 
368     public synchronized void onFinishWithAnswer(boolean finished,
369             boolean result, int index) {
370         if (finished && !this.solved) {
371             this.winnerId = index;
372             this.solversStats.get(index).inc();
373             this.solved = true;
374             this.resultFound = result;
375             for (int i = 0; i < this.numberOfSolvers; i++) {
376                 if (i != this.winnerId) {
377                     this.solvers.get(i).expireTimeout();
378                 }
379             }
380             this.sleepTime = FAST_SLEEP;
381             System.out.println(getLogPrefix() + "And the winner is "
382                     + this.availableSolvers[this.winnerId]);
383         }
384         this.remainingSolvers.getAndDecrement();
385     }
386 
387     public boolean isDBSimplificationAllowed() {
388         return this.solvers.get(0).isDBSimplificationAllowed();
389     }
390 
391     public void setDBSimplificationAllowed(boolean status) {
392         for (int i = 0; i < this.numberOfSolvers; i++) {
393             this.solvers.get(0).setDBSimplificationAllowed(status);
394         }
395     }
396 
397     public <I extends ISolverService> void setSearchListener(
398             SearchListener<I> sl) {
399         for (int i = 0; i < this.numberOfSolvers; i++) {
400             this.solvers.get(i).setSearchListener(sl);
401         }
402     }
403 
404     /**
405      * @since 2.2
406      */
407     public <I extends ISolverService> SearchListener<I> getSearchListener() {
408         return this.solvers.get(0).getSearchListener();
409     }
410 
411     public int nextFreeVarId(boolean reserve) {
412         int res = -1;
413         for (int i = 0; i < this.numberOfSolvers; ++i) {
414             res = this.solvers.get(i).nextFreeVarId(reserve);
415         }
416         return res;
417     }
418 
419     public IConstr addBlockingClause(IVecInt literals)
420             throws ContradictionException {
421         ConstrGroup group = new ConstrGroup(false);
422         for (int i = 0; i < this.numberOfSolvers; i++) {
423             group.add(this.solvers.get(i).addBlockingClause(literals));
424         }
425         return group;
426     }
427 
428     public boolean removeSubsumedConstr(IConstr c) {
429         if (c instanceof ConstrGroup) {
430             ConstrGroup group = (ConstrGroup) c;
431             boolean removed = true;
432             IConstr toRemove;
433             for (int i = 0; i < this.numberOfSolvers; i++) {
434                 toRemove = group.getConstr(i);
435                 if (toRemove != null) {
436                     removed = removed
437                             & this.solvers.get(i)
438                                     .removeSubsumedConstr(toRemove);
439                 }
440             }
441             return removed;
442         }
443         throw new IllegalArgumentException(
444                 "Can only remove a group of constraints!");
445     }
446 
447     public boolean isVerbose() {
448         return this.solvers.get(0).isVerbose();
449     }
450 
451     public void setVerbose(boolean value) {
452         for (int i = 0; i < this.numberOfSolvers; i++) {
453             this.solvers.get(i).setVerbose(value);
454         }
455     }
456 
457     public void setLogPrefix(String prefix) {
458         for (int i = 0; i < this.numberOfSolvers; i++) {
459             this.solvers.get(i).setLogPrefix(prefix);
460         }
461 
462     }
463 
464     public String getLogPrefix() {
465         return this.solvers.get(0).getLogPrefix();
466     }
467 
468     public IVecInt unsatExplanation() {
469         return this.solvers.get(this.winnerId).unsatExplanation();
470     }
471 
472     public int[] primeImplicant() {
473         return this.solvers.get(this.winnerId).primeImplicant();
474     }
475 
476     /**
477      * @since 2.3.2
478      */
479     public boolean primeImplicant(int p) {
480         return this.solvers.get(this.winnerId).primeImplicant(p);
481     }
482 
483     public List<S> getSolvers() {
484         return new ArrayList<S>(this.solvers);
485     }
486 
487     public int[] modelWithInternalVariables() {
488         return this.solvers.get(this.winnerId).modelWithInternalVariables();
489     }
490 
491     public int realNumberOfVariables() {
492         return this.solvers.get(0).realNumberOfVariables();
493     }
494 
495     public void registerLiteral(int p) {
496         for (int i = 0; i < this.numberOfSolvers; i++) {
497             this.solvers.get(i).registerLiteral(p);
498         }
499 
500     }
501 
502     public boolean isSolverKeptHot() {
503         return this.solvers.get(0).isSolverKeptHot();
504     }
505 
506     public void setKeepSolverHot(boolean value) {
507         for (int i = 0; i < this.numberOfSolvers; i++) {
508             this.solvers.get(i).setKeepSolverHot(value);
509         }
510 
511     }
512 
513     public ISolver getSolvingEngine() {
514         throw new UnsupportedOperationException("Not supported yet in ManyCore");
515     }
516 
517     /**
518      * @since 2.3.3
519      */
520     public void printStat(PrintWriter out) {
521         printStat(out, getLogPrefix());
522     }
523 
524     /**
525      * @since 2.3.3
526      */
527     public void printInfos(PrintWriter out) {
528         for (int i = 0; i < this.numberOfSolvers; i++) {
529             out.printf("%s>>>>>>>>>> Solver number %d <<<<<<<<<<<<<<<<<<%n",
530                     getLogPrefix(), i);
531             this.solvers.get(i).printInfos(out);
532         }
533 
534     }
535 }
536 
537 class RunnableSolver implements Runnable {
538 
539     private final int index;
540     private final ISolver solver;
541     private final OutcomeListener ol;
542     private final IVecInt assumps;
543     private final boolean globalTimeout;
544 
545     public RunnableSolver(int i, ISolver solver, IVecInt assumps,
546             boolean globalTimeout, OutcomeListener ol) {
547         this.index = i;
548         this.solver = solver;
549         this.ol = ol;
550         this.assumps = assumps;
551         this.globalTimeout = globalTimeout;
552     }
553 
554     public void run() {
555         try {
556             boolean result = this.solver.isSatisfiable(this.assumps,
557                     this.globalTimeout);
558             this.ol.onFinishWithAnswer(true, result, this.index);
559         } catch (Exception e) {
560             this.ol.onFinishWithAnswer(false, false, this.index);
561         }
562     }
563 
564 }