This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/packages/bee/glucose-4/parallel/MultiSolvers.cc
Vitor Santos Costa 16015bd8e6 bee
2019-04-22 12:15:21 +01:00

544 lines
18 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/***************************************************************************************[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);
*/
}