#ifndef SATHELPER_SATHELPER_H #define SATHELPER_SATHELPER_H #endif // SATHELPER_SATHELPER_H #include #include #include #include #include #include class SatHelper { private: int nextIntId = 1; std::unordered_map varIntMap; std::unordered_map intVarMap; std::vector> clauses; int varToInt(const std::string &name) { return varIntMap[name]; }; std::string intToVar(const int var) { if (var == 0) return "0"; if (var > 0) { return intVarMap[var]; } return "-" + intVarMap[-var]; } static std::string getVarName(const std::string &lit) { if (isNegated(lit)) { return lit.substr(1); } return lit; } static bool isNegated(const std::string &lit) { return lit[0] == '-'; } int literalToInt(const std::string &lit) { int code = varToInt(getVarName(lit)); if (isNegated(lit)) { return -code; } return code; } std::vector literalsToIntStr(const std::vector &clause) { std::vector res(clause.size() + 1, 0); std::for_each(clause.begin(), clause.end(), [idx = 0, &res, this](const auto &elem) mutable { res[idx] = literalToInt(elem); ++idx; }); return res; } static std::string getNegated(const std::string &lit) { if (isNegated(lit)) { return getVarName(lit); } return "-" + lit; } static std::string clauseToStr(const std::vector &clause) { std::string res; for (const auto &lit : clause) { res += std::to_string(lit); if (lit == 0) { continue; } res += " "; } return res; } std::string formulaToStr() { std::string res = "p cnf " + std::to_string(nextIntId - 1) + " " + std::to_string((int)clauses.size()) + "\n"; std::for_each(clauses.begin(), clauses.end(), [&res](const auto &clause) { res += clauseToStr(clause) + "\n"; }); return res; } const std::string FORMULA_FNAME = "/tmp/formula.cnf"; public: /* * you need to declare vars first before use to create a mapping from name to * int (dimacs doesn't use names) */ void declareVar(const std::string &name) { this->varIntMap[name] = this->nextIntId; this->intVarMap[this->nextIntId] = name; this->nextIntId += 1; } /* * add a cnf clause */ void addClause(const std::vector &clause) { this->clauses.push_back(literalsToIntStr(clause)); } /* * add a clause with at-most-one constraint */ void addAtMostOne(const std::vector &clause) { std::for_each(clause.begin(), clause.end(), [idx = 0, &clause, this](const auto &lit) mutable { for (int j = idx; j < clause.size(); ++j) { std::vector tmp = {getNegated(lit), getNegated(clause[j])}; this->addClause(tmp); } ++idx; }); } void printFormula() { std::cout << formulaToStr() << std::endl; } bool solveSat() { const std::string formula = formulaToStr(); std::ofstream fout(FORMULA_FNAME); fout << formula << std::endl; FILE *fp = popen(("glucose -model " + FORMULA_FNAME).c_str(), "r"); std::ostringstream out; while (!feof(fp) && !ferror(fp)) { char buf[128]; int bytesRead = fread(buf, 1, 128, fp); out.write(buf, bytesRead); } // this can probably be written more elegant std::istringstream stream; stream.str(out.str()); std::string line; while (getline(stream, line)) { if (line == "s SATISFIABLE") { getline(stream, line); std::string var; std::vector actual_vars; std::istringstream var_line(line.substr(2)); while (getline(var_line, var, ' ')) { int varCode = atoi(var.c_str()); actual_vars.push_back(intToVar(varCode)); }; std::cout << "SATISFIABLE WITH VARS "; for (int i = 0; i < actual_vars.size(); ++i) { std::cout << actual_vars[i] << " "; } return true; } if (line == "s UNSATISFIABLE") { return false; } } return false; } void reset() { this->nextIntId = 1; this->clauses = {}; this->intVarMap = {}; this->varIntMap = {}; } };