544 lines
18 KiB
C++
544 lines
18 KiB
C++
/***************************************************************************************[MultiSolvers.cc]
|
||
Glucose -- Copyright (c) 2009-2014, Gilles Audemard, Laurent Simon
|
||
CRIL - Univ. Artois, France
|
||
LRI - Univ. Paris Sud, France (2009-2013)
|
||
Labri - Univ. Bordeaux, France
|
||
|
||
Syrup (Glucose Parallel) -- Copyright (c) 2013-2014, Gilles Audemard, Laurent Simon
|
||
CRIL - Univ. Artois, France
|
||
Labri - Univ. Bordeaux, France
|
||
|
||
Glucose sources are based on MiniSat (see below MiniSat copyrights). Permissions and copyrights of
|
||
Glucose (sources until 2013, Glucose 3.0, single core) are exactly the same as Minisat on which it
|
||
is based on. (see below).
|
||
|
||
Glucose-Syrup sources are based on another copyright. Permissions and copyrights for the parallel
|
||
version of Glucose-Syrup (the "Software") are granted, free of charge, to deal with the Software
|
||
without restriction, including the rights to use, copy, modify, merge, publish, distribute,
|
||
sublicence, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||
furnished to do so, subject to the following conditions:
|
||
|
||
- The above and below copyrights notices and this permission notice shall be included in all
|
||
copies or substantial portions of the Software;
|
||
- The parallel version of Glucose (all files modified since Glucose 3.0 releases, 2013) cannot
|
||
be used in any competitive event (sat competitions/evaluations) without the express permission of
|
||
the authors (Gilles Audemard / Laurent Simon). This is also the case for any competitive event
|
||
using Glucose Parallel as an embedded SAT engine (single core or not).
|
||
|
||
|
||
--------------- Original Minisat Copyrights
|
||
|
||
Copyright (c) 2003-2006, Niklas Een, Niklas Sorensson
|
||
Copyright (c) 2007-2010, Niklas Sorensson
|
||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||
furnished to do so, subject to the following conditions:
|
||
|
||
The above copyright notice and this permission notice shall be included in all copies or
|
||
substantial portions of the Software.
|
||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
|
||
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
**************************************************************************************************/
|
||
|
||
#include <pthread.h>
|
||
#include "parallel/MultiSolvers.h"
|
||
#include "mtl/Sort.h"
|
||
#include "utils/System.h"
|
||
#include "simp/SimpSolver.h"
|
||
#include <errno.h>
|
||
#include <string.h>
|
||
#include "parallel/SolverConfiguration.h"
|
||
|
||
using namespace Glucose;
|
||
|
||
extern const char* _parallel ;
|
||
extern const char* _cunstable;
|
||
// Options at the parallel solver level
|
||
static IntOption opt_nbsolversmultithreads (_parallel, "nthreads", "Number of core threads for syrup (0 for automatic)", 0);
|
||
static IntOption opt_maxnbsolvers (_parallel, "maxnbthreads", "Maximum number of core threads to ask for (when nbthreads=0)", 4);
|
||
static IntOption opt_maxmemory (_parallel, "maxmemory", "Maximum memory to use (in Mb, 0 for no software limit)", 3000);
|
||
static IntOption opt_statsInterval (_parallel, "statsinterval", "Seconds (real time) between two stats reports", 5);
|
||
//
|
||
// Shared with ClausesBuffer.cc
|
||
BoolOption opt_whenFullRemoveOlder (_parallel, "removeolder", "When the FIFO for exchanging clauses between threads is full, remove older clauses", false);
|
||
IntOption opt_fifoSizeByCore(_parallel, "fifosize", "Size of the FIFO structure for exchanging clauses between threads, by threads", 100000);
|
||
//
|
||
// Shared options with Solver.cc
|
||
BoolOption opt_dontExportDirectReusedClauses (_cunstable, "reusedClauses", "Don't export directly reused clauses", false);
|
||
BoolOption opt_plingeling (_cunstable, "plingeling", "plingeling strategy for sharing clauses (exploratory feature)", false);
|
||
|
||
#include <sys/time.h>
|
||
#include <sys/resource.h>
|
||
#include <unistd.h>
|
||
|
||
static inline double cpuTime(void) {
|
||
struct rusage ru;
|
||
getrusage(RUSAGE_SELF, &ru);
|
||
return (double)ru.ru_utime.tv_sec + (double)ru.ru_utime.tv_usec / 1000000; }
|
||
|
||
|
||
void MultiSolvers::informEnd(lbool res) {
|
||
result = res;
|
||
pthread_cond_broadcast(&cfinished);
|
||
}
|
||
|
||
MultiSolvers::MultiSolvers(ParallelSolver *s):
|
||
ok (true)
|
||
, maxnbthreads(4), nbthreads(opt_nbsolversmultithreads), nbsolvers(opt_nbsolversmultithreads)
|
||
, nbcompanions(4), nbcompbysolver(2)
|
||
, allClonesAreBuilt(0)
|
||
, showModel(false)
|
||
, winner(-1)
|
||
, var_decay(1 / 0.95), clause_decay(1 / 0.999),cla_inc(1), var_inc(1)
|
||
, random_var_freq(0.02)
|
||
, restart_first(100), restart_inc(1.5)
|
||
, learntsize_factor((double)1/(double)3), learntsize_inc(1.1)
|
||
, expensive_ccmin(true)
|
||
, polarity_mode (polarity_false)
|
||
, maxmemory(opt_maxmemory), maxnbsolvers(opt_maxnbsolvers)
|
||
, verb(0) , verbEveryConflicts(10000)
|
||
, numvar(0), numclauses(0)
|
||
|
||
{
|
||
result = l_Undef;
|
||
SharedCompanion *sc = new SharedCompanion();
|
||
this->sharedcomp = sc;
|
||
|
||
// Generate only solver 0.
|
||
// It loads the formula
|
||
// All others solvers are clone of this one
|
||
solvers.push(s);
|
||
s->verbosity = 0; // No reportf in solvers... All is done in MultiSolver
|
||
s->setThreadNumber(0);
|
||
//s->belongsto = this;
|
||
s->sharedcomp = sc;
|
||
sc->addSolver(s);
|
||
assert(solvers[0]->threadNumber() == 0);
|
||
|
||
pthread_mutex_init(&m,NULL); //PTHREAD_MUTEX_INITIALIZER;
|
||
pthread_mutex_init(&mfinished,NULL); //PTHREAD_MUTEX_INITIALIZER;
|
||
pthread_cond_init(&cfinished,NULL);
|
||
|
||
if (nbsolvers > 0 )
|
||
fprintf(stdout,"c %d solvers engines and 1 companion as a blackboard created.\n", nbsolvers);
|
||
}
|
||
|
||
MultiSolvers::MultiSolvers() : MultiSolvers(new ParallelSolver(-1)) {
|
||
|
||
}
|
||
|
||
MultiSolvers::~MultiSolvers()
|
||
{}
|
||
|
||
/**
|
||
* Generate All solvers
|
||
*/
|
||
|
||
void MultiSolvers::generateAllSolvers() {
|
||
assert(solvers[0] != NULL);
|
||
assert(allClonesAreBuilt==0);
|
||
|
||
for(int i=1;i<nbsolvers;i++) {
|
||
ParallelSolver *s = (ParallelSolver*)solvers[0]->clone();
|
||
solvers.push(s);
|
||
s->verbosity = 0; // No reportf in solvers... All is done in MultiSolver
|
||
s->setThreadNumber(i);
|
||
s->sharedcomp = this->sharedcomp;
|
||
this->sharedcomp->addSolver(s);
|
||
assert(solvers[i]->threadNumber() == i);
|
||
}
|
||
|
||
adjustParameters();
|
||
|
||
allClonesAreBuilt = 1;
|
||
}
|
||
|
||
/**
|
||
* Choose solver for threads i (if no given in command line see above)
|
||
*/
|
||
|
||
|
||
ParallelSolver* MultiSolvers::retrieveSolver(int i) {
|
||
return new ParallelSolver(i);
|
||
}
|
||
|
||
Var MultiSolvers::newVar(bool sign, bool dvar)
|
||
{
|
||
assert(solvers[0] != NULL);
|
||
numvar++;
|
||
int v;
|
||
sharedcomp->newVar(sign);
|
||
if(!allClonesAreBuilt) { // At the beginning we want to generate only solvers 0
|
||
v = solvers[0]->newVar(sign,dvar);
|
||
assert(numvar == v+1); // Just a useless check
|
||
} else {
|
||
for(int i=0;i<nbsolvers;i++) {
|
||
v = solvers[i]->newVar(sign,dvar);
|
||
}
|
||
}
|
||
return numvar;
|
||
}
|
||
|
||
bool MultiSolvers::addClause_(vec<Lit>&ps) {
|
||
assert(solvers[0] != NULL); // There is at least one solver.
|
||
// Check if clause is satisfied and remove false/duplicate literals:
|
||
if (!okay()) return false;
|
||
|
||
sort(ps);
|
||
Lit p; int i, j;
|
||
for (i = j = 0, p = lit_Undef; i < ps.size(); i++)
|
||
if (solvers[0]->value(ps[i]) == l_True || ps[i] == ~p)
|
||
return true;
|
||
else if (solvers[0]->value(ps[i]) != l_False && ps[i] != p)
|
||
ps[j++] = p = ps[i];
|
||
ps.shrink(i - j);
|
||
|
||
|
||
if (ps.size() == 0) {
|
||
return ok = false;
|
||
}
|
||
else if (ps.size() == 1){
|
||
assert(solvers[0]->value(ps[0]) == l_Undef); // TODO : Passes values to all threads
|
||
solvers[0]->uncheckedEnqueue(ps[0]);
|
||
if(!allClonesAreBuilt) {
|
||
return ok = ( (solvers[0]->propagate()) == CRef_Undef); // checks only main solver here for propagation constradiction
|
||
}
|
||
|
||
// Here, all clones are built.
|
||
// Gives the unit clause to everybody
|
||
for(int i=0;i<nbsolvers;i++)
|
||
solvers[i]->uncheckedEnqueue(ps[0]);
|
||
return ok = ( (solvers[0]->propagate()) == CRef_Undef); // checks only main solver here for propagation constradiction
|
||
}else{
|
||
// printf("Adding clause %0xd for solver %d.\n",(void*)c, thn);
|
||
// At the beginning only solver 0 load the formula
|
||
solvers[0]->addClause(ps);
|
||
|
||
if(!allClonesAreBuilt) {
|
||
numclauses++;
|
||
return true;
|
||
}
|
||
// Clones are built, need to pass the clause to all the threads
|
||
for(int i=1;i<nbsolvers;i++) {
|
||
solvers[i]->addClause(ps);
|
||
}
|
||
numclauses++;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
|
||
bool MultiSolvers::simplify() {
|
||
assert(solvers[0] != NULL); // There is at least one solver.
|
||
|
||
if (!okay()) return false;
|
||
return ok = solvers[0]->simplify();
|
||
}
|
||
|
||
|
||
bool MultiSolvers::eliminate() {
|
||
|
||
// TODO allow variable elimination when all threads are built!
|
||
assert(allClonesAreBuilt==false);
|
||
|
||
SimpSolver * s = (SimpSolver*)getPrimarySolver();
|
||
return s->eliminate(true);
|
||
}
|
||
|
||
|
||
// TODO: Use a template here
|
||
void *localLaunch(void*arg) {
|
||
ParallelSolver* s = (ParallelSolver*)arg;
|
||
|
||
(void)s->solve();
|
||
|
||
pthread_exit(NULL);
|
||
}
|
||
|
||
|
||
#define MAXIMUM_SLEEP_DURATION 5
|
||
void MultiSolvers::printStats() {
|
||
static int nbprinted = 1;
|
||
double cpu_time = cpuTime();
|
||
printf("c\n");
|
||
|
||
printf("c |-------------------------------------------------------------------------------------------------------|\n");
|
||
printf("c | id | starts | decisions | confls | Init T | learnts | exported | imported | promoted | %% | \n");
|
||
printf("c |-------------------------------------------------------------------------------------------------------|\n");
|
||
|
||
//printf("%.0fs | ",cpu_time);
|
||
for(int i=0;i<solvers.size();i++) {
|
||
solvers[i]->reportProgress();
|
||
//printf(" %2d: %12ld confl. |", i, (long int) solvers[i]->conflicts);
|
||
}
|
||
long long int totalconf = 0;
|
||
long long int totalprop = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
totalconf+= (long int) solvers[i]->conflicts;
|
||
totalprop+= solvers[i]->propagations;
|
||
}
|
||
printf("c \n");
|
||
|
||
printf("c synthesis %11lld conflicts %11lld propagations %8.0f conflicts/sec %8.0f propagations/sec\n",
|
||
totalconf, totalprop, (double)totalconf / cpu_time, (double) totalprop / cpu_time);
|
||
|
||
|
||
nbprinted ++;
|
||
}
|
||
|
||
// Still a ugly function... To be rewritten with some statistics class some day
|
||
void MultiSolvers::printFinalStats() {
|
||
sharedcomp->printStats();
|
||
printf("c\nc\n");
|
||
printf("c\n");
|
||
printf("c |---------------------------------------- FINAL STATS --------------------------------------------------|\n");
|
||
printf("c\n");
|
||
|
||
printf("c |---------------");
|
||
for(int i = 0;i<solvers.size();i++)
|
||
printf("|------------");
|
||
printf("|-----------------|\n");
|
||
|
||
printf("c | Threads ");
|
||
for(int i = 0;i < solvers.size();i++) {
|
||
printf("| %10d ",i);
|
||
}
|
||
printf("| Total |\n");
|
||
|
||
printf("c |---------------");
|
||
for(int i = 0;i<solvers.size();i++)
|
||
printf("|------------");
|
||
printf("|-----------------|\n");
|
||
|
||
|
||
printf("c | Conflicts ");
|
||
|
||
long long int totalconf = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->conflicts);
|
||
totalconf += solvers[i]->conflicts;
|
||
}
|
||
printf("| %15lld |\n",totalconf);
|
||
|
||
printf("c | Exported ");
|
||
uint64_t exported = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbexported);
|
||
exported += solvers[i]->nbexported;
|
||
}
|
||
printf("| %15" PRIu64" |\n", exported);
|
||
|
||
printf("c | Imported ");
|
||
uint64_t imported = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbimported);
|
||
imported += solvers[i]->nbimported;
|
||
}
|
||
printf("| %15" PRIu64" |\n", imported);
|
||
|
||
printf("c | Good ");
|
||
uint64_t importedGood = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbImportedGoodClauses);
|
||
importedGood += solvers[i]->nbImportedGoodClauses;
|
||
}
|
||
printf("| %15" PRIu64" |\n", importedGood);
|
||
|
||
printf("c | Purge ");
|
||
uint64_t importedPurg = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbimportedInPurgatory);
|
||
importedPurg += solvers[i]->nbimportedInPurgatory;
|
||
}
|
||
printf("| %15" PRIu64" |\n", importedPurg);
|
||
|
||
printf("c | Promoted ");
|
||
uint64_t promoted = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbPromoted);
|
||
promoted += solvers[i]->nbPromoted;
|
||
}
|
||
printf("| %15" PRIu64" |\n", promoted);
|
||
|
||
printf("c | Remove imp ");
|
||
uint64_t removedimported = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbRemovedUnaryWatchedClauses);
|
||
removedimported += solvers[i]->nbRemovedUnaryWatchedClauses;
|
||
}
|
||
printf("| %15" PRIu64" |\n", removedimported);
|
||
|
||
printf("c | Blocked Reuse ");
|
||
uint64_t blockedreused = 0;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbNotExportedBecauseDirectlyReused);
|
||
blockedreused += solvers[i]->nbNotExportedBecauseDirectlyReused;
|
||
}
|
||
printf("| %15" PRIu64" |\n",blockedreused);
|
||
|
||
|
||
printf("c | Orig seen ");
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->originalClausesSeen);
|
||
}
|
||
printf("| |\n");
|
||
|
||
printf("c | Unaries ");
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbUn);
|
||
}
|
||
printf("| |\n");
|
||
|
||
printf("c | Binaries ");
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbBin);
|
||
}
|
||
printf("| |\n");
|
||
|
||
|
||
printf("c | Glues ");
|
||
for(int i=0;i<solvers.size();i++) {
|
||
printf("| %10" PRIu64" ", solvers[i]->nbDL2);
|
||
}
|
||
printf("| |\n");
|
||
|
||
|
||
int winner = -1;
|
||
for(int i=0;i<solvers.size();i++) {
|
||
if(sharedcomp->winner()==solvers[i])
|
||
winner = i;
|
||
}
|
||
|
||
if(winner!=-1) {
|
||
int sum = 0;
|
||
printf("c | Hamming ");
|
||
for(int i = 0;i<solvers.size();i++) {
|
||
if(i==winner) {
|
||
printf("| X ");
|
||
continue;
|
||
}
|
||
int nb = 0;
|
||
for(int j = 0;j<nVars();j++) {
|
||
if(solvers[i]->valuePhase(j)!= solvers[winner]->valuePhase(j)) nb++;
|
||
}
|
||
printf("| %10d ",nb);
|
||
sum+=nb;
|
||
|
||
}
|
||
printf("| %15d |\n",sum/(solvers.size()>1?solvers.size()-1:1));
|
||
}
|
||
|
||
printf("c |---------------");
|
||
for(int i = 0;i<solvers.size();i++)
|
||
printf("|------------");
|
||
printf("|-----------------|\n");
|
||
|
||
|
||
|
||
}
|
||
|
||
// Well, all those parameteres are just naive guesses... No experimental evidences for this.
|
||
void MultiSolvers::adjustParameters() {
|
||
SolverConfiguration::configure(this,nbsolvers);
|
||
}
|
||
|
||
void MultiSolvers::adjustNumberOfCores() {
|
||
float mem = memUsed();
|
||
if (nbthreads==0) { // Automatic configuration
|
||
if(verb>=1)
|
||
printf("c | Automatic Adjustement of the number of solvers. MaxMemory=%5d, MaxCores=%3d. |\n", maxmemory, maxnbsolvers);
|
||
unsigned int tmpnbsolvers = maxmemory * 4 / 10 / mem;
|
||
if (tmpnbsolvers > maxnbsolvers) tmpnbsolvers = maxnbsolvers;
|
||
if (tmpnbsolvers < 1) tmpnbsolvers = 1;
|
||
if(verb>=1)
|
||
printf("c | One Solver is taking %.2fMb... Let's take %d solvers for this run (max 40%% of the maxmemory). |\n", mem, tmpnbsolvers);
|
||
nbsolvers = tmpnbsolvers;
|
||
nbthreads = nbsolvers;
|
||
} else {
|
||
assert(nbthreads == nbsolvers);
|
||
}
|
||
}
|
||
|
||
lbool MultiSolvers::solve() {
|
||
pthread_attr_t thAttr;
|
||
int i;
|
||
|
||
adjustNumberOfCores();
|
||
sharedcomp->setNbThreads(nbsolvers);
|
||
if(verb>=1)
|
||
printf("c | Generating clones |\n");
|
||
generateAllSolvers();
|
||
if(verb>=1) {
|
||
printf("c | all clones generated. Memory = %6.2fMb. |\n", memUsed());
|
||
printf("c ========================================================================================================|\n");
|
||
}
|
||
|
||
|
||
model.clear();
|
||
|
||
/* Initialize and set thread detached attribute */
|
||
pthread_attr_init(&thAttr);
|
||
pthread_attr_setdetachstate(&thAttr, PTHREAD_CREATE_JOINABLE);
|
||
|
||
|
||
|
||
// Launching all solvers
|
||
for (i = 0; i < nbsolvers; i++) {
|
||
pthread_t * pt = (pthread_t*)malloc(sizeof(pthread_t));
|
||
threads.push(pt);
|
||
solvers[i]->pmfinished = &mfinished;
|
||
solvers[i]->pcfinished = &cfinished;
|
||
pthread_create(threads[i], &thAttr, &localLaunch, (void*)solvers[i]);
|
||
}
|
||
|
||
bool done = false;
|
||
|
||
(void)pthread_mutex_lock(&m);
|
||
while (!done) {
|
||
struct timespec timeout;
|
||
time(&timeout.tv_sec);
|
||
timeout.tv_sec += MAXIMUM_SLEEP_DURATION;
|
||
timeout.tv_nsec = 0;
|
||
if (pthread_cond_timedwait(&cfinished, &mfinished, &timeout) != ETIMEDOUT)
|
||
done = true;
|
||
else
|
||
printStats();
|
||
|
||
float mem = memUsed();
|
||
if(verb>=1) printf("c Total Memory so far : %.2fMb\n", mem);
|
||
if ( (maxmemory > 0) && (mem > maxmemory) && !sharedcomp->panicMode)
|
||
printf("c ** reduceDB switching to Panic Mode due to memory limitations !\n"), sharedcomp->panicMode = true;
|
||
|
||
}
|
||
(void)pthread_mutex_unlock(&m);
|
||
|
||
for (i = 0; i < nbsolvers; i++) { // Wait for all threads to finish
|
||
pthread_join(*threads[i], NULL);
|
||
}
|
||
|
||
assert(sharedcomp != NULL);
|
||
result = sharedcomp->jobStatus;
|
||
if (result == l_True) {
|
||
int n = sharedcomp->jobFinishedBy->nVars();
|
||
model.growTo(n);
|
||
for(int i = 0; i < n; i++)
|
||
model[i] = sharedcomp->jobFinishedBy->model[i];
|
||
}
|
||
|
||
|
||
return result;
|
||
/*
|
||
for(int i=0;i<NBTHREADS;i++)
|
||
pthread_join(*threads[i],&status);
|
||
*/
|
||
|
||
}
|
||
|