/* * ezSAT -- A simple and easy to use CNF generator for SAT solvers * * Copyright (C) 2013 Clifford Wolf * * 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. * */ #include "ezsat.h" #include #include #include #include #include const int ezSAT::CONST_TRUE = 1; const int ezSAT::CONST_FALSE = 2; static std::string my_int_to_string(int i) { #ifdef __MINGW32__ char buffer[64]; snprintf(buffer, 64, "%d", i); return buffer; #else return std::to_string(i); #endif } ezSAT::ezSAT() { statehash = 5381; flag_keep_cnf = false; flag_non_incremental = false; non_incremental_solve_used_up = false; cnfConsumed = false; cnfVariableCount = 0; cnfClausesCount = 0; solverTimeout = 0; solverTimoutStatus = false; literal("CONST_TRUE"); literal("CONST_FALSE"); assert(literal("CONST_TRUE") == CONST_TRUE); assert(literal("CONST_FALSE") == CONST_FALSE); } ezSAT::~ezSAT() { } void ezSAT::addhash(unsigned int h) { statehash = ((statehash << 5) + statehash) ^ h; } int ezSAT::value(bool val) { return val ? CONST_TRUE : CONST_FALSE; } int ezSAT::literal() { literals.push_back(std::string()); return literals.size(); } int ezSAT::literal(const std::string &name) { if (literalsCache.count(name) == 0) { literals.push_back(name); literalsCache[name] = literals.size(); } return literalsCache.at(name); } int ezSAT::frozen_literal() { int id = literal(); freeze(id); return id; } int ezSAT::frozen_literal(const std::string &name) { int id = literal(name); freeze(id); return id; } int ezSAT::expression(OpId op, int a, int b, int c, int d, int e, int f) { std::vector args(6); args[0] = a, args[1] = b, args[2] = c; args[3] = d, args[4] = e, args[5] = f; return expression(op, args); } int ezSAT::expression(OpId op, const std::vector &args) { std::vector myArgs; myArgs.reserve(args.size()); bool xorRemovedOddTrues = false; addhash(__LINE__); addhash(op); for (auto arg : args) { addhash(__LINE__); addhash(arg); if (arg == 0) continue; if (op == OpAnd && arg == CONST_TRUE) continue; if ((op == OpOr || op == OpXor) && arg == CONST_FALSE) continue; if (op == OpXor && arg == CONST_TRUE) { xorRemovedOddTrues = !xorRemovedOddTrues; continue; } myArgs.push_back(arg); } if (myArgs.size() > 0 && (op == OpAnd || op == OpOr || op == OpXor || op == OpIFF)) { std::sort(myArgs.begin(), myArgs.end()); int j = 0; for (int i = 1; i < int(myArgs.size()); i++) if (j < 0 || myArgs[j] != myArgs[i]) myArgs[++j] = myArgs[i]; else if (op == OpXor) j--; myArgs.resize(j+1); } switch (op) { case OpNot: assert(myArgs.size() == 1); if (myArgs[0] == CONST_TRUE) return CONST_FALSE; if (myArgs[0] == CONST_FALSE) return CONST_TRUE; break; case OpAnd: if (myArgs.size() == 0) return CONST_TRUE; if (myArgs.size() == 1) return myArgs[0]; break; case OpOr: if (myArgs.size() == 0) return CONST_FALSE; if (myArgs.size() == 1) return myArgs[0]; break; case OpXor: if (myArgs.size() == 0) return xorRemovedOddTrues ? CONST_TRUE : CONST_FALSE; if (myArgs.size() == 1) return xorRemovedOddTrues ? NOT(myArgs[0]) : myArgs[0]; break; case OpIFF: assert(myArgs.size() >= 1); if (myArgs.size() == 1) return CONST_TRUE; // FIXME: Add proper const folding break; case OpITE: assert(myArgs.size() == 3); if (myArgs[0] == CONST_TRUE) return myArgs[1]; if (myArgs[0] == CONST_FALSE) return myArgs[2]; break; default: abort(); } std::pair> myExpr(op, myArgs); int id = 0; if (expressionsCache.count(myExpr) > 0) { id = expressionsCache.at(myExpr); } else { id = -(int(expressions.size()) + 1); expressionsCache[myExpr] = id; expressions.push_back(myExpr); } if (xorRemovedOddTrues) id = NOT(id); addhash(__LINE__); addhash(id); return id; } void ezSAT::lookup_literal(int id, std::string &name) const { assert(0 < id && id <= int(literals.size())); name = literals[id - 1]; } const std::string &ezSAT::lookup_literal(int id) const { assert(0 < id && id <= int(literals.size())); return literals[id - 1]; } void ezSAT::lookup_expression(int id, OpId &op, std::vector &args) const { assert(0 < -id && -id <= int(expressions.size())); op = expressions[-id - 1].first; args = expressions[-id - 1].second; } const std::vector &ezSAT::lookup_expression(int id, OpId &op) const { assert(0 < -id && -id <= int(expressions.size())); op = expressions[-id - 1].first; return expressions[-id - 1].second; } int ezSAT::parse_string(const std::string &) { abort(); } std::string ezSAT::to_string(int id) const { std::string text; if (id > 0) { lookup_literal(id, text); } else { OpId op; std::vector args; lookup_expression(id, op, args); switch (op) { case OpNot: text = "not("; break; case OpAnd: text = "and("; break; case OpOr: text = "or("; break; case OpXor: text = "xor("; break; case OpIFF: text = "iff("; break; case OpITE: text = "ite("; break; default: abort(); } for (int i = 0; i < int(args.size()); i++) { if (i > 0) text += ", "; text += to_string(args[i]); } text += ")"; } return text; } int ezSAT::eval(int id, const std::vector &values) const { if (id > 0) { if (id <= int(values.size()) && (values[id-1] == CONST_TRUE || values[id-1] == CONST_FALSE || values[id-1] == 0)) return values[id-1]; return 0; } OpId op; const std::vector &args = lookup_expression(id, op); int a, b; switch (op) { case OpNot: assert(args.size() == 1); a = eval(args[0], values); if (a == CONST_TRUE) return CONST_FALSE; if (a == CONST_FALSE) return CONST_TRUE; return 0; case OpAnd: a = CONST_TRUE; for (auto arg : args) { b = eval(arg, values); if (b != CONST_TRUE && b != CONST_FALSE) a = 0; if (b == CONST_FALSE) return CONST_FALSE; } return a; case OpOr: a = CONST_FALSE; for (auto arg : args) { b = eval(arg, values); if (b != CONST_TRUE && b != CONST_FALSE) a = 0; if (b == CONST_TRUE) return CONST_TRUE; } return a; case OpXor: a = CONST_FALSE; for (auto arg : args) { b = eval(arg, values); if (b != CONST_TRUE && b != CONST_FALSE) return 0; if (b == CONST_TRUE) a = a == CONST_TRUE ? CONST_FALSE : CONST_TRUE; } return a; case OpIFF: assert(args.size() > 0); a = eval(args[0], values); for (auto arg : args) { b = eval(arg, values); if (b != CONST_TRUE && b != CONST_FALSE) return 0; if (b != a) return CONST_FALSE; } return CONST_TRUE; case OpITE: assert(args.size() == 3); a = eval(args[0], values); if (a == CONST_TRUE) return eval(args[1], values); if (a == CONST_FALSE) return eval(args[2], values); return 0; default: abort(); } } void ezSAT::clear() { cnfConsumed = false; cnfVariableCount = 0; cnfClausesCount = 0; cnfLiteralVariables.clear(); cnfExpressionVariables.clear(); cnfClauses.clear(); } void ezSAT::freeze(int) { } bool ezSAT::eliminated(int) { return false; } void ezSAT::assume(int id) { addhash(__LINE__); addhash(id); if (id < 0) { assert(0 < -id && -id <= int(expressions.size())); cnfExpressionVariables.resize(expressions.size()); if (cnfExpressionVariables[-id-1] == 0) { OpId op; std::vector args; lookup_expression(id, op, args); if (op == OpNot) { int idx = bind(args[0]); cnfClauses.push_back(std::vector(1, -idx)); cnfClausesCount++; return; } if (op == OpOr) { std::vector clause; for (int arg : args) clause.push_back(bind(arg)); cnfClauses.push_back(clause); cnfClausesCount++; return; } if (op == OpAnd) { for (int arg : args) { cnfClauses.push_back(std::vector(1, bind(arg))); cnfClausesCount++; } return; } } } int idx = bind(id); cnfClauses.push_back(std::vector(1, idx)); cnfClausesCount++; } void ezSAT::add_clause(const std::vector &args) { addhash(__LINE__); for (auto arg : args) addhash(arg); cnfClauses.push_back(args); cnfClausesCount++; } void ezSAT::add_clause(const std::vector &args, bool argsPolarity, int a, int b, int c) { std::vector clause; for (auto arg : args) clause.push_back(argsPolarity ? +arg : -arg); if (a != 0) clause.push_back(a); if (b != 0) clause.push_back(b); if (c != 0) clause.push_back(c); add_clause(clause); } void ezSAT::add_clause(int a, int b, int c) { std::vector clause; if (a != 0) clause.push_back(a); if (b != 0) clause.push_back(b); if (c != 0) clause.push_back(c); add_clause(clause); } int ezSAT::bind_cnf_not(const std::vector &args) { assert(args.size() == 1); return -args[0]; } int ezSAT::bind_cnf_and(const std::vector &args) { assert(args.size() >= 2); int idx = ++cnfVariableCount; add_clause(args, false, idx); for (auto arg : args) add_clause(-idx, arg); return idx; } int ezSAT::bind_cnf_or(const std::vector &args) { assert(args.size() >= 2); int idx = ++cnfVariableCount; add_clause(args, true, -idx); for (auto arg : args) add_clause(idx, -arg); return idx; } int ezSAT::bound(int id) const { if (id > 0 && id <= int(cnfLiteralVariables.size())) return cnfLiteralVariables[id-1]; if (-id > 0 && -id <= int(cnfExpressionVariables.size())) return cnfExpressionVariables[-id-1]; return 0; } std::string ezSAT::cnfLiteralInfo(int idx) const { for (int i = 0; i < int(cnfLiteralVariables.size()); i++) { if (cnfLiteralVariables[i] == idx) return to_string(i+1); if (cnfLiteralVariables[i] == -idx) return "NOT " + to_string(i+1); } for (int i = 0; i < int(cnfExpressionVariables.size()); i++) { if (cnfExpressionVariables[i] == idx) return to_string(-i-1); if (cnfExpressionVariables[i] == -idx) return "NOT " + to_string(-i-1); } return ""; } int ezSAT::bind(int id, bool auto_freeze) { addhash(__LINE__); addhash(id); addhash(auto_freeze); if (id >= 0) { assert(0 < id && id <= int(literals.size())); cnfLiteralVariables.resize(literals.size()); if (eliminated(cnfLiteralVariables[id-1])) { fprintf(stderr, "ezSAT: Missing freeze on literal `%s'.\n", to_string(id).c_str()); abort(); } if (cnfLiteralVariables[id-1] == 0) { cnfLiteralVariables[id-1] = ++cnfVariableCount; if (id == CONST_TRUE) add_clause(+cnfLiteralVariables[id-1]); if (id == CONST_FALSE) add_clause(-cnfLiteralVariables[id-1]); } return cnfLiteralVariables[id-1]; } assert(0 < -id && -id <= int(expressions.size())); cnfExpressionVariables.resize(expressions.size()); if (eliminated(cnfExpressionVariables[-id-1])) { cnfExpressionVariables[-id-1] = 0; // this will recursively call bind(id). within the recursion // the cnf is pre-set to 0. an idx is allocated there, then it // is frozen, then it returns here with the new idx already set. if (auto_freeze) freeze(id); } if (cnfExpressionVariables[-id-1] == 0) { OpId op; std::vector args; lookup_expression(id, op, args); int idx = 0; if (op == OpXor) { while (args.size() > 1) { std::vector newArgs; for (int i = 0; i < int(args.size()); i += 2) if (i+1 == int(args.size())) { newArgs.push_back(args[i]); } else { int sub1 = AND(args[i], NOT(args[i+1])); int sub2 = AND(NOT(args[i]), args[i+1]); newArgs.push_back(OR(sub1, sub2)); } args.swap(newArgs); } idx = bind(args.at(0), false); goto assign_idx; } if (op == OpIFF) { std::vector invArgs; for (auto arg : args) invArgs.push_back(NOT(arg)); int sub1 = expression(OpAnd, args); int sub2 = expression(OpAnd, invArgs); idx = bind(OR(sub1, sub2), false); goto assign_idx; } if (op == OpITE) { int sub1 = AND(args[0], args[1]); int sub2 = AND(NOT(args[0]), args[2]); idx = bind(OR(sub1, sub2), false); goto assign_idx; } for (int i = 0; i < int(args.size()); i++) args[i] = bind(args[i], false); switch (op) { case OpNot: idx = bind_cnf_not(args); break; case OpAnd: idx = bind_cnf_and(args); break; case OpOr: idx = bind_cnf_or(args); break; default: abort(); } assign_idx: assert(idx != 0); cnfExpressionVariables[-id-1] = idx; } return cnfExpressionVariables[-id-1]; } void ezSAT::consumeCnf() { if (mode_keep_cnf()) cnfClausesBackup.insert(cnfClausesBackup.end(), cnfClauses.begin(), cnfClauses.end()); else cnfConsumed = true; cnfClauses.clear(); } void ezSAT::consumeCnf(std::vector> &cnf) { if (mode_keep_cnf()) cnfClausesBackup.insert(cnfClausesBackup.end(), cnfClauses.begin(), cnfClauses.end()); else cnfConsumed = true; cnf.swap(cnfClauses); cnfClauses.clear(); } void ezSAT::getFullCnf(std::vector> &full_cnf) const { assert(full_cnf.empty()); full_cnf.insert(full_cnf.end(), cnfClausesBackup.begin(), cnfClausesBackup.end()); full_cnf.insert(full_cnf.end(), cnfClauses.begin(), cnfClauses.end()); } void ezSAT::preSolverCallback() { assert(!non_incremental_solve_used_up); if (mode_non_incremental()) non_incremental_solve_used_up = true; } bool ezSAT::solver(const std::vector&, std::vector&, const std::vector&) { preSolverCallback(); fprintf(stderr, "************************************************************************\n"); fprintf(stderr, "ERROR: You are trying to use the solve() method of the ezSAT base class!\n"); fprintf(stderr, "Use a dervied class like ezMiniSAT instead.\n"); fprintf(stderr, "************************************************************************\n"); abort(); } std::vector ezSAT::vec_const(const std::vector &bits) { std::vector vec; for (auto bit : bits) vec.push_back(bit ? CONST_TRUE : CONST_FALSE); return vec; } std::vector ezSAT::vec_const_signed(int64_t value, int numBits) { std::vector vec; for (int i = 0; i < numBits; i++) vec.push_back(((value >> i) & 1) != 0 ? CONST_TRUE : CONST_FALSE); return vec; } std::vector ezSAT::vec_const_unsigned(uint64_t value, int numBits) { std::vector vec; for (int i = 0; i < numBits; i++) vec.push_back(((value >> i) & 1) != 0 ? CONST_TRUE : CONST_FALSE); return vec; } std::vector ezSAT::vec_var(int numBits) { std::vector vec; for (int i = 0; i < numBits; i++) vec.push_back(literal()); return vec; } std::vector ezSAT::vec_var(std::string name, int numBits) { std::vector vec; for (int i = 0; i < numBits; i++) { vec.push_back(VAR(name + my_int_to_string(i))); } return vec; } std::vector ezSAT::vec_cast(const std::vector &vec1, int toBits, bool signExtend) { std::vector vec; for (int i = 0; i < toBits; i++) if (i >= int(vec1.size())) vec.push_back(signExtend ? vec1.back() : CONST_FALSE); else vec.push_back(vec1[i]); return vec; } std::vector ezSAT::vec_not(const std::vector &vec1) { std::vector vec; for (auto bit : vec1) vec.push_back(NOT(bit)); return vec; } std::vector ezSAT::vec_and(const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); for (int i = 0; i < int(vec1.size()); i++) vec[i] = AND(vec1[i], vec2[i]); return vec; } std::vector ezSAT::vec_or(const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); for (int i = 0; i < int(vec1.size()); i++) vec[i] = OR(vec1[i], vec2[i]); return vec; } std::vector ezSAT::vec_xor(const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); for (int i = 0; i < int(vec1.size()); i++) vec[i] = XOR(vec1[i], vec2[i]); return vec; } std::vector ezSAT::vec_iff(const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); for (int i = 0; i < int(vec1.size()); i++) vec[i] = IFF(vec1[i], vec2[i]); return vec; } std::vector ezSAT::vec_ite(const std::vector &vec1, const std::vector &vec2, const std::vector &vec3) { assert(vec1.size() == vec2.size() && vec2.size() == vec3.size()); std::vector vec(vec1.size()); for (int i = 0; i < int(vec1.size()); i++) vec[i] = ITE(vec1[i], vec2[i], vec3[i]); return vec; } std::vector ezSAT::vec_ite(int sel, const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); for (int i = 0; i < int(vec1.size()); i++) vec[i] = ITE(sel, vec1[i], vec2[i]); return vec; } // 'y' is the MSB (carry) and x the LSB (sum) output static void fulladder(ezSAT *that, int a, int b, int c, int &y, int &x) { int tmp = that->XOR(a, b); int new_x = that->XOR(tmp, c); int new_y = that->OR(that->AND(a, b), that->AND(c, tmp)); #if 0 printf("FULLADD> a=%s, b=%s, c=%s, carry=%s, sum=%s\n", that->to_string(a).c_str(), that->to_string(b).c_str(), that->to_string(c).c_str(), that->to_string(new_y).c_str(), that->to_string(new_x).c_str()); #endif x = new_x, y = new_y; } // 'y' is the MSB (carry) and x the LSB (sum) output static void halfadder(ezSAT *that, int a, int b, int &y, int &x) { int new_x = that->XOR(a, b); int new_y = that->AND(a, b); #if 0 printf("HALFADD> a=%s, b=%s, carry=%s, sum=%s\n", that->to_string(a).c_str(), that->to_string(b).c_str(), that->to_string(new_y).c_str(), that->to_string(new_x).c_str()); #endif x = new_x, y = new_y; } std::vector ezSAT::vec_count(const std::vector &vec, int numBits, bool clip) { std::vector sum = vec_const_unsigned(0, numBits); std::vector carry_vector; for (auto bit : vec) { int carry = bit; for (int i = 0; i < numBits; i++) halfadder(this, carry, sum[i], carry, sum[i]); carry_vector.push_back(carry); } if (clip) { int overflow = vec_reduce_or(carry_vector); sum = vec_ite(overflow, vec_const_unsigned(~0, numBits), sum); } #if 0 printf("COUNT> vec=["); for (int i = int(vec.size())-1; i >= 0; i--) printf("%s%s", to_string(vec[i]).c_str(), i ? ", " : ""); printf("], result=["); for (int i = int(sum.size())-1; i >= 0; i--) printf("%s%s", to_string(sum[i]).c_str(), i ? ", " : ""); printf("]\n"); #endif return sum; } std::vector ezSAT::vec_add(const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); int carry = CONST_FALSE; for (int i = 0; i < int(vec1.size()); i++) fulladder(this, vec1[i], vec2[i], carry, carry, vec[i]); #if 0 printf("ADD> vec1=["); for (int i = int(vec1.size())-1; i >= 0; i--) printf("%s%s", to_string(vec1[i]).c_str(), i ? ", " : ""); printf("], vec2=["); for (int i = int(vec2.size())-1; i >= 0; i--) printf("%s%s", to_string(vec2[i]).c_str(), i ? ", " : ""); printf("], result=["); for (int i = int(vec.size())-1; i >= 0; i--) printf("%s%s", to_string(vec[i]).c_str(), i ? ", " : ""); printf("]\n"); #endif return vec; } std::vector ezSAT::vec_sub(const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); std::vector vec(vec1.size()); int carry = CONST_TRUE; for (int i = 0; i < int(vec1.size()); i++) fulladder(this, vec1[i], NOT(vec2[i]), carry, carry, vec[i]); #if 0 printf("SUB> vec1=["); for (int i = int(vec1.size())-1; i >= 0; i--) printf("%s%s", to_string(vec1[i]).c_str(), i ? ", " : ""); printf("], vec2=["); for (int i = int(vec2.size())-1; i >= 0; i--) printf("%s%s", to_string(vec2[i]).c_str(), i ? ", " : ""); printf("], result=["); for (int i = int(vec.size())-1; i >= 0; i--) printf("%s%s", to_string(vec[i]).c_str(), i ? ", " : ""); printf("]\n"); #endif return vec; } std::vector ezSAT::vec_neg(const std::vector &vec) { std::vector zero(vec.size(), CONST_FALSE); return vec_sub(zero, vec); } void ezSAT::vec_cmp(const std::vector &vec1, const std::vector &vec2, int &carry, int &overflow, int &sign, int &zero) { assert(vec1.size() == vec2.size()); carry = CONST_TRUE; zero = CONST_FALSE; for (int i = 0; i < int(vec1.size()); i++) { overflow = carry; fulladder(this, vec1[i], NOT(vec2[i]), carry, carry, sign); zero = OR(zero, sign); } overflow = XOR(overflow, carry); carry = NOT(carry); zero = NOT(zero); #if 0 printf("CMP> vec1=["); for (int i = int(vec1.size())-1; i >= 0; i--) printf("%s%s", to_string(vec1[i]).c_str(), i ? ", " : ""); printf("], vec2=["); for (int i = int(vec2.size())-1; i >= 0; i--) printf("%s%s", to_string(vec2[i]).c_str(), i ? ", " : ""); printf("], carry=%s, overflow=%s, sign=%s, zero=%s\n", to_string(carry).c_str(), to_string(overflow).c_str(), to_string(sign).c_str(), to_string(zero).c_str()); #endif } int ezSAT::vec_lt_signed(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return OR(AND(NOT(overflow), sign), AND(overflow, NOT(sign))); } int ezSAT::vec_le_signed(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return OR(AND(NOT(overflow), sign), AND(overflow, NOT(sign)), zero); } int ezSAT::vec_ge_signed(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return OR(AND(NOT(overflow), NOT(sign)), AND(overflow, sign)); } int ezSAT::vec_gt_signed(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return AND(OR(AND(NOT(overflow), NOT(sign)), AND(overflow, sign)), NOT(zero)); } int ezSAT::vec_lt_unsigned(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return carry; } int ezSAT::vec_le_unsigned(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return OR(carry, zero); } int ezSAT::vec_ge_unsigned(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return NOT(carry); } int ezSAT::vec_gt_unsigned(const std::vector &vec1, const std::vector &vec2) { int carry, overflow, sign, zero; vec_cmp(vec1, vec2, carry, overflow, sign, zero); return AND(NOT(carry), NOT(zero)); } int ezSAT::vec_eq(const std::vector &vec1, const std::vector &vec2) { return vec_reduce_and(vec_iff(vec1, vec2)); } int ezSAT::vec_ne(const std::vector &vec1, const std::vector &vec2) { return NOT(vec_reduce_and(vec_iff(vec1, vec2))); } std::vector ezSAT::vec_shl(const std::vector &vec1, int shift, bool signExtend) { std::vector vec; for (int i = 0; i < int(vec1.size()); i++) { int j = i-shift; if (int(vec1.size()) <= j) vec.push_back(signExtend ? vec1.back() : CONST_FALSE); else if (0 <= j) vec.push_back(vec1[j]); else vec.push_back(CONST_FALSE); } return vec; } std::vector ezSAT::vec_srl(const std::vector &vec1, int shift) { std::vector vec; for (int i = 0; i < int(vec1.size()); i++) { int j = i-shift; while (j < 0) j += vec1.size(); while (j >= int(vec1.size())) j -= vec1.size(); vec.push_back(vec1[j]); } return vec; } std::vector ezSAT::vec_shift(const std::vector &vec1, int shift, int extend_left, int extend_right) { std::vector vec; for (int i = 0; i < int(vec1.size()); i++) { int j = i+shift; if (j < 0) vec.push_back(extend_right); else if (j >= int(vec1.size())) vec.push_back(extend_left); else vec.push_back(vec1[j]); } return vec; } static int my_clog2(int x) { int result = 0; for (x--; x > 0; result++) x >>= 1; return result; } std::vector ezSAT::vec_shift_right(const std::vector &vec1, const std::vector &vec2, bool vec2_signed, int extend_left, int extend_right) { int vec2_bits = std::min(my_clog2(vec1.size()) + (vec2_signed ? 1 : 0), int(vec2.size())); std::vector overflow_bits(vec2.begin() + vec2_bits, vec2.end()); int overflow_left = CONST_FALSE, overflow_right = CONST_FALSE; if (vec2_signed) { int overflow = CONST_FALSE; for (auto bit : overflow_bits) overflow = OR(overflow, XOR(bit, vec2[vec2_bits-1])); overflow_left = AND(overflow, NOT(vec2.back())); overflow_right = AND(overflow, vec2.back()); } else overflow_left = vec_reduce_or(overflow_bits); std::vector buffer = vec1; if (vec2_signed) while (buffer.size() < vec1.size() + (1 << vec2_bits)) buffer.push_back(extend_left); std::vector overflow_pattern_left(buffer.size(), extend_left); std::vector overflow_pattern_right(buffer.size(), extend_right); buffer = vec_ite(overflow_left, overflow_pattern_left, buffer); if (vec2_signed) buffer = vec_ite(overflow_right, overflow_pattern_left, buffer); for (int i = vec2_bits-1; i >= 0; i--) { std::vector shifted_buffer; if (vec2_signed && i == vec2_bits-1) shifted_buffer = vec_shift(buffer, -(1 << i), extend_left, extend_right); else shifted_buffer = vec_shift(buffer, 1 << i, extend_left, extend_right); buffer = vec_ite(vec2[i], shifted_buffer, buffer); } buffer.resize(vec1.size()); return buffer; } std::vector ezSAT::vec_shift_left(const std::vector &vec1, const std::vector &vec2, bool vec2_signed, int extend_left, int extend_right) { // vec2_signed is not implemented in vec_shift_left() yet if (vec2_signed) assert(vec2_signed == false); int vec2_bits = std::min(my_clog2(vec1.size()), int(vec2.size())); std::vector overflow_bits(vec2.begin() + vec2_bits, vec2.end()); int overflow = vec_reduce_or(overflow_bits); std::vector buffer = vec1; std::vector overflow_pattern_right(buffer.size(), extend_right); buffer = vec_ite(overflow, overflow_pattern_right, buffer); for (int i = 0; i < vec2_bits; i++) { std::vector shifted_buffer; shifted_buffer = vec_shift(buffer, -(1 << i), extend_left, extend_right); buffer = vec_ite(vec2[i], shifted_buffer, buffer); } buffer.resize(vec1.size()); return buffer; } void ezSAT::vec_append(std::vector &vec, const std::vector &vec1) const { for (auto bit : vec1) vec.push_back(bit); } void ezSAT::vec_append_signed(std::vector &vec, const std::vector &vec1, int64_t value) { assert(int(vec1.size()) <= 64); for (int i = 0; i < int(vec1.size()); i++) { if (((value >> i) & 1) != 0) vec.push_back(vec1[i]); else vec.push_back(NOT(vec1[i])); } } void ezSAT::vec_append_unsigned(std::vector &vec, const std::vector &vec1, uint64_t value) { assert(int(vec1.size()) <= 64); for (int i = 0; i < int(vec1.size()); i++) { if (((value >> i) & 1) != 0) vec.push_back(vec1[i]); else vec.push_back(NOT(vec1[i])); } } int64_t ezSAT::vec_model_get_signed(const std::vector &modelExpressions, const std::vector &modelValues, const std::vector &vec1) const { int64_t value = 0; std::map modelMap; assert(modelExpressions.size() == modelValues.size()); for (int i = 0; i < int(modelExpressions.size()); i++) modelMap[modelExpressions[i]] = modelValues[i]; for (int i = 0; i < 64; i++) { int j = i < int(vec1.size()) ? i : vec1.size()-1; if (modelMap.at(vec1[j])) value |= int64_t(1) << i; } return value; } uint64_t ezSAT::vec_model_get_unsigned(const std::vector &modelExpressions, const std::vector &modelValues, const std::vector &vec1) const { uint64_t value = 0; std::map modelMap; assert(modelExpressions.size() == modelValues.size()); for (int i = 0; i < int(modelExpressions.size()); i++) modelMap[modelExpressions[i]] = modelValues[i]; for (int i = 0; i < int(vec1.size()); i++) if (modelMap.at(vec1[i])) value |= uint64_t(1) << i; return value; } int ezSAT::vec_reduce_and(const std::vector &vec1) { return expression(OpAnd, vec1); } int ezSAT::vec_reduce_or(const std::vector &vec1) { return expression(OpOr, vec1); } void ezSAT::vec_set(const std::vector &vec1, const std::vector &vec2) { assert(vec1.size() == vec2.size()); for (int i = 0; i < int(vec1.size()); i++) SET(vec1[i], vec2[i]); } void ezSAT::vec_set_signed(const std::vector &vec1, int64_t value) { assert(int(vec1.size()) <= 64); for (int i = 0; i < int(vec1.size()); i++) { if (((value >> i) & 1) != 0) assume(vec1[i]); else assume(NOT(vec1[i])); } } void ezSAT::vec_set_unsigned(const std::vector &vec1, uint64_t value) { assert(int(vec1.size()) <= 64); for (int i = 0; i < int(vec1.size()); i++) { if (((value >> i) & 1) != 0) assume(vec1[i]); else assume(NOT(vec1[i])); } } ezSATbit ezSAT::bit(_V a) { return ezSATbit(*this, a); } ezSATvec ezSAT::vec(const std::vector &vec) { return ezSATvec(*this, vec); } void ezSAT::printDIMACS(FILE *f, bool verbose) const { if (cnfConsumed) { fprintf(stderr, "Usage error: printDIMACS() must not be called after cnfConsumed()!"); abort(); } int digits = ceil(log10f(cnfVariableCount)) + 2; fprintf(f, "c generated by ezSAT\n"); if (verbose) { fprintf(f, "c\n"); fprintf(f, "c mapping of variables to literals:\n"); for (int i = 0; i < int(cnfLiteralVariables.size()); i++) if (cnfLiteralVariables[i] != 0) fprintf(f, "c %*d: %s\n", digits, cnfLiteralVariables[i], literals[i].c_str()); fprintf(f, "c\n"); fprintf(f, "c mapping of variables to expressions:\n"); for (int i = 0; i < int(cnfExpressionVariables.size()); i++) if (cnfExpressionVariables[i] != 0) fprintf(f, "c %*d: %d\n", digits, cnfExpressionVariables[i], -i-1); if (mode_keep_cnf()) { fprintf(f, "c\n"); fprintf(f, "c %d clauses from backup, %d from current buffer\n", int(cnfClausesBackup.size()), int(cnfClauses.size())); } fprintf(f, "c\n"); } std::vector> all_clauses; getFullCnf(all_clauses); assert(cnfClausesCount == int(all_clauses.size())); fprintf(f, "p cnf %d %d\n", cnfVariableCount, cnfClausesCount); int maxClauseLen = 0; for (auto &clause : all_clauses) maxClauseLen = std::max(int(clause.size()), maxClauseLen); if (!verbose) maxClauseLen = std::min(maxClauseLen, 3); for (auto &clause : all_clauses) { for (auto idx : clause) fprintf(f, " %*d", digits, idx); if (maxClauseLen >= int(clause.size())) fprintf(f, " %*d\n", (digits + 1)*int(maxClauseLen - clause.size()) + digits, 0); else fprintf(f, " %*d\n", digits, 0); } } static std::string expression2str(const std::pair> &data) { std::string text; switch (data.first) { #define X(op) case ezSAT::op: text += #op; break; X(OpNot) X(OpAnd) X(OpOr) X(OpXor) X(OpIFF) X(OpITE) default: abort(); #undef X } text += ":"; for (auto it : data.second) text += " " + my_int_to_string(it); return text; } void ezSAT::printInternalState(FILE *f) const { fprintf(f, "--8<-- snip --8<--\n"); fprintf(f, "literalsCache:\n"); for (auto &it : literalsCache) fprintf(f, " `%s' -> %d\n", it.first.c_str(), it.second); fprintf(f, "literals:\n"); for (int i = 0; i < int(literals.size()); i++) fprintf(f, " %d: `%s'\n", i+1, literals[i].c_str()); fprintf(f, "expressionsCache:\n"); for (auto &it : expressionsCache) fprintf(f, " `%s' -> %d\n", expression2str(it.first).c_str(), it.second); fprintf(f, "expressions:\n"); for (int i = 0; i < int(expressions.size()); i++) fprintf(f, " %d: `%s'\n", -i-1, expression2str(expressions[i]).c_str()); fprintf(f, "cnfVariables (count=%d):\n", cnfVariableCount); for (int i = 0; i < int(cnfLiteralVariables.size()); i++) if (cnfLiteralVariables[i] != 0) fprintf(f, " literal %d -> %d (%s)\n", i+1, cnfLiteralVariables[i], to_string(i+1).c_str()); for (int i = 0; i < int(cnfExpressionVariables.size()); i++) if (cnfExpressionVariables[i] != 0) fprintf(f, " expression %d -> %d (%s)\n", -i-1, cnfExpressionVariables[i], to_string(-i-1).c_str()); fprintf(f, "cnfClauses:\n"); for (auto &i1 : cnfClauses) { for (auto &i2 : i1) fprintf(f, " %4d", i2); fprintf(f, "\n"); } if (cnfConsumed) fprintf(f, " *** more clauses consumed via cnfConsume() ***\n"); fprintf(f, "--8<-- snap --8<--\n"); } static int clog2(int x) { int y = (x & (x - 1)); y = (y | -y) >> 31; x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); x >>= 1; x -= ((x >> 1) & 0x55555555); x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); x = (((x >> 4) + x) & 0x0f0f0f0f); x += (x >> 8); x += (x >> 16); x = x & 0x0000003f; return x - y; } int ezSAT::onehot(const std::vector &vec, bool max_only) { // Mixed one-hot/binary encoding as described by Claessen in Sec. 4.2 of // "Successful SAT Encoding Techniques. Magnus Bjiirk. 25th July 2009". // http://jsat.ewi.tudelft.nl/addendum/Bjork_encoding.pdf std::vector formula; // add at-leat-one constraint if (max_only == false) formula.push_back(expression(OpOr, vec)); // create binary vector int num_bits = clog2(vec.size()); std::vector bits; for (int k = 0; k < num_bits; k++) bits.push_back(literal()); // add at-most-one clauses using binary encoding for (size_t i = 0; i < vec.size(); i++) for (int k = 0; k < num_bits; k++) { std::vector clause; clause.push_back(NOT(vec[i])); clause.push_back((i & (1 << k)) != 0 ? bits[k] : NOT(bits[k])); formula.push_back(expression(OpOr, clause)); } return expression(OpAnd, formula); } #if 0 int ezSAT::manyhot(const std::vector &vec, int min_hot, int max_hot) { // many-hot encoding using a simple sorting network if (max_hot < 0) max_hot = min_hot; std::vector formula; int M = max_hot+1, N = vec.size(); std::map, int> x; for (int i = -1; i < N; i++) for (int j = -1; j < M; j++) x[std::pair(i,j)] = j < 0 ? CONST_TRUE : i < 0 ? CONST_FALSE : literal(); for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) { formula.push_back(OR(NOT(vec[i]), x[std::pair(i-1,j-1)], NOT(x[std::pair(i,j)]))); formula.push_back(OR(NOT(vec[i]), NOT(x[std::pair(i-1,j-1)]), x[std::pair(i,j)])); formula.push_back(OR(vec[i], x[std::pair(i-1,j)], NOT(x[std::pair(i,j)]))); formula.push_back(OR(vec[i], NOT(x[std::pair(i-1,j)]), x[std::pair(i,j)])); #if 0 // explicit resolution clauses -- in tests it was better to let the sat solver figure those out formula.push_back(OR(NOT(x[std::pair(i-1,j-1)]), NOT(x[std::pair(i-1,j)]), x[std::pair(i,j)])); formula.push_back(OR(x[std::pair(i-1,j-1)], x[std::pair(i-1,j)], NOT(x[std::pair(i,j)]))); #endif } for (int j = 0; j < M; j++) { if (j+1 <= min_hot) formula.push_back(x[std::pair(N-1,j)]); else if (j+1 > max_hot) formula.push_back(NOT(x[std::pair(N-1,j)])); } return expression(OpAnd, formula); } #else static std::vector lfsr_sym(ezSAT *that, const std::vector &vec, int poly) { std::vector out; for (int i = 0; i < int(vec.size()); i++) if ((poly & (1 << (i+1))) != 0) { if (out.empty()) out.push_back(vec.at(i)); else out.at(0) = that->XOR(out.at(0), vec.at(i)); } for (int i = 0; i+1 < int(vec.size()); i++) out.push_back(vec.at(i)); return out; } static int lfsr_num(int vec, int poly, int cnt = 1) { int mask = poly >> 1; mask |= mask >> 1; mask |= mask >> 2; mask |= mask >> 4; mask |= mask >> 8; mask |= mask >> 16; while (cnt-- > 0) { int bits = vec & (poly >> 1); bits = ((bits & 0xAAAAAAAA) >> 1) ^ (bits & 0x55555555); bits = ((bits & 0x44444444) >> 2) ^ (bits & 0x11111111); bits = ((bits & 0x10101010) >> 4) ^ (bits & 0x01010101); bits = ((bits & 0x01000100) >> 8) ^ (bits & 0x00010001); bits = ((bits & 0x00010000) >> 16) ^ (bits & 0x00000001); vec = ((vec << 1) | bits) & mask; } return vec; } int ezSAT::manyhot(const std::vector &vec, int min_hot, int max_hot) { // many-hot encoding using LFSR as counter int poly = 0; int nbits = 0; if (vec.size() < 3) { poly = (1 << 2) | (1 << 1) | 1; nbits = 2; } else if (vec.size() < 7) { poly = (1 << 3) | (1 << 2) | 1; nbits = 3; } else if (vec.size() < 15) { poly = (1 << 4) | (1 << 3) | 1; nbits = 4; } else if (vec.size() < 31) { poly = (1 << 5) | (1 << 3) | 1; nbits = 5; } else if (vec.size() < 63) { poly = (1 << 6) | (1 << 5) | 1; nbits = 6; } else if (vec.size() < 127) { poly = (1 << 7) | (1 << 6) | 1; nbits = 7; } else // if (vec.size() < 255) { // poly = (1 << 8) | (1 << 6) | (1 << 5) | (1 << 4) | 1; // nbits = 8; // } else if (vec.size() < 511) { poly = (1 << 9) | (1 << 5) | 1; nbits = 9; } else { assert(0); } std::vector min_val; std::vector max_val; if (min_hot > 1) min_val = vec_const_unsigned(lfsr_num(1, poly, min_hot), nbits); if (max_hot >= 0) max_val = vec_const_unsigned(lfsr_num(1, poly, max_hot+1), nbits); std::vector state = vec_const_unsigned(1, nbits); std::vector match_min; std::vector match_max; if (min_hot == 1) match_min = vec; for (int i = 0; i < int(vec.size()); i++) { state = vec_ite(vec[i], lfsr_sym(this, state, poly), state); if (!min_val.empty() && i+1 >= min_hot) match_min.push_back(vec_eq(min_val, state)); if (!max_val.empty() && i >= max_hot) match_max.push_back(vec_eq(max_val, state)); } int min_matched = min_hot ? vec_reduce_or(match_min) : CONST_TRUE; int max_matched = vec_reduce_or(match_max); return AND(min_matched, NOT(max_matched)); } #endif int ezSAT::ordered(const std::vector &vec1, const std::vector &vec2, bool allow_equal) { std::vector formula; int last_x = CONST_FALSE; assert(vec1.size() == vec2.size()); for (size_t i = 0; i < vec1.size(); i++) { int a = vec1[i], b = vec2[i]; formula.push_back(OR(NOT(a), b, last_x)); int next_x = i+1 < vec1.size() ? literal() : allow_equal ? CONST_FALSE : CONST_TRUE; formula.push_back(OR(a, b, last_x, NOT(next_x))); formula.push_back(OR(NOT(a), NOT(b), last_x, NOT(next_x))); last_x = next_x; } return expression(OpAnd, formula); }