| /* | |
| * ezSAT -- A simple and easy to use CNF generator for SAT solvers | |
| * | |
| * Copyright (C) 2013 Clifford Wolf <clifford@clifford.at> | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * copyright notice and this permission notice appear in all copies. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| * | |
| */ | |
| // needed for MiniSAT headers (see Minisat Makefile) | |
| #ifndef __STDC_FORMAT_MACROS | |
| #define __STDC_FORMAT_MACROS | |
| #endif | |
| #ifndef __STDC_LIMIT_MACROS | |
| #define __STDC_LIMIT_MACROS | |
| #endif | |
| #include "ezminisat.h" | |
| #include <limits.h> | |
| #include <stdint.h> | |
| #include <csignal> | |
| #include <cinttypes> | |
| #ifndef _WIN32 | |
| # include <unistd.h> | |
| #endif | |
| #include "../minisat/Solver.h" | |
| #include "../minisat/SimpSolver.h" | |
| ezMiniSAT::ezMiniSAT() : minisatSolver(NULL) | |
| { | |
| minisatSolver = NULL; | |
| foundContradiction = false; | |
| freeze(CONST_TRUE); | |
| freeze(CONST_FALSE); | |
| } | |
| ezMiniSAT::~ezMiniSAT() | |
| { | |
| if (minisatSolver != NULL) | |
| delete minisatSolver; | |
| } | |
| void ezMiniSAT::clear() | |
| { | |
| if (minisatSolver != NULL) { | |
| delete minisatSolver; | |
| minisatSolver = NULL; | |
| } | |
| foundContradiction = false; | |
| minisatVars.clear(); | |
| #if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL | |
| cnfFrozenVars.clear(); | |
| #endif | |
| ezSAT::clear(); | |
| } | |
| #if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL | |
| void ezMiniSAT::freeze(int id) | |
| { | |
| if (!mode_non_incremental()) | |
| cnfFrozenVars.insert(bind(id)); | |
| } | |
| bool ezMiniSAT::eliminated(int idx) | |
| { | |
| idx = idx < 0 ? -idx : idx; | |
| if (minisatSolver != NULL && idx > 0 && idx <= int(minisatVars.size())) | |
| return minisatSolver->isEliminated(minisatVars.at(idx-1)); | |
| return false; | |
| } | |
| #endif | |
| #ifndef _WIN32 | |
| ezMiniSAT *ezMiniSAT::alarmHandlerThis = NULL; | |
| clock_t ezMiniSAT::alarmHandlerTimeout = 0; | |
| void ezMiniSAT::alarmHandler(int) | |
| { | |
| if (clock() > alarmHandlerTimeout) { | |
| alarmHandlerThis->minisatSolver->interrupt(); | |
| alarmHandlerTimeout = 0; | |
| } else | |
| alarm(1); | |
| } | |
| #endif | |
| bool ezMiniSAT::solver(const std::vector<int> &modelExpressions, std::vector<bool> &modelValues, const std::vector<int> &assumptions) | |
| { | |
| preSolverCallback(); | |
| solverTimoutStatus = false; | |
| if (0) { | |
| contradiction: | |
| delete minisatSolver; | |
| minisatSolver = NULL; | |
| minisatVars.clear(); | |
| foundContradiction = true; | |
| return false; | |
| } | |
| if (foundContradiction) { | |
| consumeCnf(); | |
| return false; | |
| } | |
| std::vector<int> extraClauses, modelIdx; | |
| for (auto id : assumptions) | |
| extraClauses.push_back(bind(id)); | |
| for (auto id : modelExpressions) | |
| modelIdx.push_back(bind(id)); | |
| if (minisatSolver == NULL) { | |
| minisatSolver = new Solver; | |
| minisatSolver->verbosity = EZMINISAT_VERBOSITY; | |
| } | |
| #if EZMINISAT_INCREMENTAL | |
| std::vector<std::vector<int>> cnf; | |
| consumeCnf(cnf); | |
| #else | |
| const std::vector<std::vector<int>> &cnf = this->cnf(); | |
| #endif | |
| while (int(minisatVars.size()) < numCnfVariables()) | |
| minisatVars.push_back(minisatSolver->newVar()); | |
| #if EZMINISAT_SIMPSOLVER && EZMINISAT_INCREMENTAL | |
| for (auto idx : cnfFrozenVars) | |
| minisatSolver->setFrozen(minisatVars.at(idx > 0 ? idx-1 : -idx-1), true); | |
| cnfFrozenVars.clear(); | |
| #endif | |
| for (auto &clause : cnf) { | |
| Minisat::vec<Minisat::Lit> ps; | |
| for (auto idx : clause) { | |
| if (idx > 0) | |
| ps.push(Minisat::mkLit(minisatVars.at(idx-1))); | |
| else | |
| ps.push(Minisat::mkLit(minisatVars.at(-idx-1), true)); | |
| #if EZMINISAT_SIMPSOLVER | |
| if (minisatSolver->isEliminated(minisatVars.at(idx > 0 ? idx-1 : -idx-1))) { | |
| fprintf(stderr, "Assert in %s:%d failed! Missing call to ezsat->freeze(): %s (lit=%d)\n", | |
| __FILE__, __LINE__, cnfLiteralInfo(idx).c_str(), idx); | |
| abort(); | |
| } | |
| #endif | |
| } | |
| if (!minisatSolver->addClause(ps)) | |
| goto contradiction; | |
| } | |
| if (cnf.size() > 0 && !minisatSolver->simplify()) | |
| goto contradiction; | |
| Minisat::vec<Minisat::Lit> assumps; | |
| for (auto idx : extraClauses) { | |
| if (idx > 0) | |
| assumps.push(Minisat::mkLit(minisatVars.at(idx-1))); | |
| else | |
| assumps.push(Minisat::mkLit(minisatVars.at(-idx-1), true)); | |
| #if EZMINISAT_SIMPSOLVER | |
| if (minisatSolver->isEliminated(minisatVars.at(idx > 0 ? idx-1 : -idx-1))) { | |
| fprintf(stderr, "Assert in %s:%d failed! Missing call to ezsat->freeze(): %s\n", __FILE__, __LINE__, cnfLiteralInfo(idx).c_str()); | |
| abort(); | |
| } | |
| #endif | |
| } | |
| #ifndef _WIN32 | |
| struct sigaction sig_action; | |
| struct sigaction old_sig_action; | |
| int old_alarm_timeout = 0; | |
| if (solverTimeout > 0) { | |
| sig_action.sa_handler = alarmHandler; | |
| sigemptyset(&sig_action.sa_mask); | |
| sig_action.sa_flags = SA_RESTART; | |
| alarmHandlerThis = this; | |
| alarmHandlerTimeout = clock() + solverTimeout*CLOCKS_PER_SEC; | |
| old_alarm_timeout = alarm(0); | |
| sigaction(SIGALRM, &sig_action, &old_sig_action); | |
| alarm(1); | |
| } | |
| #endif | |
| bool foundSolution = minisatSolver->solve(assumps); | |
| #ifndef _WIN32 | |
| if (solverTimeout > 0) { | |
| if (alarmHandlerTimeout == 0) | |
| solverTimoutStatus = true; | |
| alarm(0); | |
| sigaction(SIGALRM, &old_sig_action, NULL); | |
| alarm(old_alarm_timeout); | |
| } | |
| #endif | |
| if (!foundSolution) { | |
| #if !EZMINISAT_INCREMENTAL | |
| delete minisatSolver; | |
| minisatSolver = NULL; | |
| minisatVars.clear(); | |
| #endif | |
| return false; | |
| } | |
| modelValues.clear(); | |
| modelValues.resize(modelIdx.size()); | |
| for (size_t i = 0; i < modelIdx.size(); i++) | |
| { | |
| int idx = modelIdx[i]; | |
| bool refvalue = true; | |
| if (idx < 0) | |
| idx = -idx, refvalue = false; | |
| using namespace Minisat; | |
| lbool value = minisatSolver->modelValue(minisatVars.at(idx-1)); | |
| modelValues[i] = (value == Minisat::lbool(refvalue)); | |
| } | |
| #if !EZMINISAT_INCREMENTAL | |
| delete minisatSolver; | |
| minisatSolver = NULL; | |
| minisatVars.clear(); | |
| #endif | |
| return true; | |
| } | |