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