old version
This commit is contained in:
parent
03691a7e97
commit
356415c113
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -1,6 +1,8 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"*.ejs": "html",
|
||||
"iostream": "cpp"
|
||||
"iostream": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"new": "cpp"
|
||||
}
|
||||
}
|
487
acec.cpp
487
acec.cpp
@ -4,86 +4,362 @@
|
||||
#include "circuit.hpp"
|
||||
#include "sweep.hpp"
|
||||
|
||||
#include "libopenf4.h"
|
||||
//#include "libopenf4.h"
|
||||
#include "src/cadical.hpp"
|
||||
|
||||
#include "polynomial.h"
|
||||
|
||||
extern "C" {
|
||||
#include "aiger.h"
|
||||
}
|
||||
|
||||
#define pvar(i) ((string((i) < 0 ? "N" : "") + "A" + std::to_string(abs(i))).c_str())
|
||||
Sweep *S;
|
||||
|
||||
Sweep* sweep_init(int argv, char* args[]) {
|
||||
Sweep *S = new Sweep();
|
||||
S->aig = aiger_init();
|
||||
S->solver = new CaDiCaL::Solver;
|
||||
S->det_v_sz = 0;
|
||||
S->file = fopen(args[1], "r");
|
||||
// S->rounds = atoi(args[2]);
|
||||
// if (argv > 3) {
|
||||
// S->det_v = new int[argv -3];
|
||||
// for (int i = 3; i < argv; i++)
|
||||
// S->det_v[S->det_v_sz++] = atoi(args[i]);
|
||||
// }
|
||||
return S;
|
||||
}
|
||||
|
||||
Var* pvar(int id) {
|
||||
static std::map<int, Var*> mp;
|
||||
if(mp.count(id)) return mp[id];
|
||||
|
||||
std::string name = "A" + std::to_string(abs(id));
|
||||
if(id < 0) name = "N" + name;
|
||||
|
||||
Var* var = new Var(name.c_str(), id > 0 ? S->topo_index[id] : (100000 + id) );
|
||||
|
||||
return mp[id] = var;
|
||||
}
|
||||
|
||||
bool subsitute(Polynomial* &a, Polynomial* b) {
|
||||
|
||||
|
||||
Polynomial* result = divide_by_term(a, b->get_lt());
|
||||
|
||||
auto ltm = new Monomial(minus_one, b->get_lt());
|
||||
push_mstack(ltm);
|
||||
auto ltp = build_poly();
|
||||
|
||||
Polynomial* to_minus = multiply_poly(result, ltp);
|
||||
|
||||
|
||||
Polynomial* new_result = add_poly(a, to_minus);
|
||||
|
||||
|
||||
for(int i=1; i<b->size(); i++) {
|
||||
push_mstack_end(b->get_mon(i));
|
||||
}
|
||||
Polynomial* tail = build_poly();
|
||||
|
||||
Polynomial* new_tail = multiply_poly(result, tail);
|
||||
|
||||
|
||||
mpz_t c; mpz_init(c);
|
||||
mpz_mul(c, b->get_mon(0)->coeff, minus_one);
|
||||
Polynomial* tail_with_c = multiply_poly_with_constant(new_tail, c);
|
||||
|
||||
|
||||
Polynomial* done = add_poly(tail_with_c, new_result);
|
||||
|
||||
// printf("=======subsitute======\n");
|
||||
// printf("a: "); a->print(stdout);
|
||||
// printf("b: "); b->print(stdout);
|
||||
// printf("result: "); result->print(stdout);
|
||||
// printf("to_minus: "); to_minus->print(stdout);
|
||||
// printf("new_result: "); new_result->print(stdout);
|
||||
// printf("tail: "); tail->print(stdout);
|
||||
// printf("new_tail: "); new_tail->print(stdout);
|
||||
// printf("tail_with_c: "); tail_with_c->print(stdout);
|
||||
// printf("done: "); done->print(stdout);
|
||||
|
||||
a = done;
|
||||
return result->is_constant_zero_poly();
|
||||
}
|
||||
|
||||
void getPolyOfGate(Sweep* s, int gateid, Polynomial* &resultA, Polynomial* &resultB) {
|
||||
circuit *c = &(s->C[abs(gateid)]);
|
||||
gateid = abs(gateid) * (c->neg ? -1 : 1);
|
||||
if(c->type == And) {
|
||||
add_to_vstack(pvar(gateid));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
for(int i=0; i<c->sz; i++) {
|
||||
add_to_vstack(pvar(c->to[i]));
|
||||
}
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
resultA = build_poly();
|
||||
} else if(c->type == Xor) {
|
||||
add_to_vstack(pvar(gateid));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(minus_two, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
resultA = build_poly();
|
||||
} else if(c->type == HA) {
|
||||
add_to_vstack(pvar(gateid));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(minus_two, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
resultA = build_poly();
|
||||
|
||||
add_to_vstack(pvar(c->carrier));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
for(int i=0; i<c->sz; i++) {
|
||||
add_to_vstack(pvar(c->to[i]));
|
||||
}
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
resultB = build_poly();
|
||||
} else if(c->type == FA) {
|
||||
add_to_vstack(pvar(gateid));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(minus_two, build_term_from_stack()));
|
||||
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(minus_two, build_term_from_stack()));
|
||||
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(minus_two, build_term_from_stack()));
|
||||
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(four, build_term_from_stack()));
|
||||
|
||||
resultA = build_poly();
|
||||
|
||||
add_to_vstack(pvar(c->carrier));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(minus_two, build_term_from_stack()));
|
||||
|
||||
resultB = build_poly();
|
||||
} else if(c->type == Majority) {
|
||||
add_to_vstack(pvar(gateid));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(c->to[0]));
|
||||
add_to_vstack(pvar(c->to[1]));
|
||||
add_to_vstack(pvar(c->to[2]));
|
||||
push_mstack(new Monomial(minus_two, build_term_from_stack()));
|
||||
resultA = build_poly();
|
||||
}
|
||||
}
|
||||
|
||||
bool poly_cmp(Polynomial *a, Polynomial *b) {
|
||||
return a->get_lt()->get_var()->get_level() > b->get_lt()->get_var()->get_level();
|
||||
}
|
||||
|
||||
std::map<Polynomial*, Polynomial*> another;
|
||||
int main(int argv, char* args[]) {
|
||||
Sweep *S = sweep_init(argv, args);
|
||||
|
||||
init_mpz(19260817);
|
||||
|
||||
S = sweep_init(argv, args);
|
||||
|
||||
S->solve();
|
||||
|
||||
using namespace std;
|
||||
//using namespace F4;
|
||||
|
||||
// Create polynomial array.
|
||||
vector<string> polynomialArray;
|
||||
|
||||
// Create variable name array.
|
||||
vector<string> variableName;
|
||||
std::map<std::string, Polynomial*> nas;
|
||||
for(int i=1; i<=S->maxvar; i++) {
|
||||
add_to_vstack(pvar(i));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
add_to_vstack(pvar(-i));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
push_mstack(new Monomial(minus_one, build_term_from_stack()));
|
||||
Polynomial *poly = build_poly();
|
||||
nas[pvar(-i)->get_name()] = poly;
|
||||
nas[pvar(i)->get_name()] = poly;
|
||||
}
|
||||
|
||||
set<string> vars;
|
||||
std::vector<Polynomial*> polys;
|
||||
|
||||
int t = S->maxvar;
|
||||
std::set<int> reserved_vars;
|
||||
std::vector<Polynomial*> subsitute_vars;
|
||||
for (int i = 1; i <= S->maxvar; i++) {
|
||||
circuit *c = &(S->C[i]);
|
||||
if (!c->sz || S->del[i] == 2) continue;
|
||||
if(c->type == Xor) {
|
||||
reserved_vars.insert(i);
|
||||
// for(int j=0; j<c->sz; j++)
|
||||
// reserved_vars.insert(abs(c->to[j]));
|
||||
}
|
||||
}
|
||||
for (int i = 1; i <= S->maxvar; i++) {
|
||||
circuit *c = &(S->C[i]);
|
||||
if (!c->sz || S->del[i] == 2) continue;
|
||||
if(!reserved_vars.count(i)) {
|
||||
Polynomial *p, *q;
|
||||
getPolyOfGate(S, i, p, q);
|
||||
while(p->get_lt()->get_var_name()[0] == 'N') {
|
||||
Polynomial *to_div = nas[p->get_lt()->get_var_name()];
|
||||
subsitute(p, to_div);
|
||||
}
|
||||
subsitute_vars.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
printf("reserved: %d ; subsitute: %d\n", reserved_vars.size(), subsitute_vars.size());
|
||||
|
||||
for (int i = 1; i <= S->maxvar; i++) {
|
||||
circuit *c = &(S->C[i]);
|
||||
if (!c->sz || S->del[i] == 2) continue;
|
||||
}
|
||||
|
||||
for (int i = 1; i <= S->maxvar; i++) {
|
||||
circuit *c = &(S->C[i]);
|
||||
if (!c->sz || S->del[i] == 2) continue;
|
||||
|
||||
if(c->type == And) {
|
||||
char term[1024];
|
||||
sprintf(term, "-%s+%s", pvar(i * (S->C[i].neg ? -1 : 1)), pvar(c->to[0]));
|
||||
for(int i=1; i<c->sz; i++) {
|
||||
sprintf(term, "%s*%s", term, pvar(c->to[i]));
|
||||
}
|
||||
polynomialArray.push_back(term);
|
||||
printf("%s\n", term);
|
||||
}
|
||||
if(c->type == Xor) {
|
||||
string last = pvar(c->to[0]);
|
||||
char term[1024];
|
||||
for(int i=1; i<c->sz-1; i++) {
|
||||
string tmp = pvar(++t);
|
||||
sprintf(term, "-%s-2*%s*%s+%s+%s", tmp.c_str(), last.c_str(), pvar(c->to[i]), last.c_str(), pvar(c->to[i]));
|
||||
polynomialArray.push_back(term);
|
||||
printf("%s\n", term);
|
||||
last = tmp;
|
||||
}
|
||||
sprintf(term, "-%s-2*%s*%s+%s+%s", pvar(i * (S->C[i].neg ? -1 : 1)), last.c_str(), pvar(c->to[c->sz-1]), last.c_str(), pvar(c->to[c->sz-1]));
|
||||
polynomialArray.push_back(term);
|
||||
printf("%s\n", term);
|
||||
if(!reserved_vars.count(i)) continue;
|
||||
|
||||
//if(c->sz == 2 && abs(c->to[0]) <= 14 && abs(c->to[1]) <= 14) continue;
|
||||
|
||||
Polynomial *resultA, *resultB = nullptr;
|
||||
getPolyOfGate(S, i, resultA, resultB);
|
||||
|
||||
printf(" original:\t"); resultA->print(stdout);
|
||||
|
||||
while(resultA->get_lt()->get_var_name()[0] == 'N') {
|
||||
Polynomial *to_div = nas[resultA->get_lt()->get_var_name()];
|
||||
subsitute(resultA, to_div);
|
||||
}
|
||||
|
||||
printf(" pos-var:\t"); resultA->print(stdout);
|
||||
|
||||
for(auto subs : subsitute_vars) {
|
||||
//printf("delete: "); subs->print(stdout);
|
||||
subsitute(resultA, subs);
|
||||
}
|
||||
|
||||
printf(" xor-relative:\t"); resultA->print(stdout);
|
||||
|
||||
polys.push_back(resultA);
|
||||
|
||||
if (c->type == HA || c->type == FA) {
|
||||
char term[1024];
|
||||
sprintf(term, "-%s-2*%s+%s", pvar(i * (S->C[i].neg ? -1 : 1)), pvar(c->carrier), pvar(c->to[0]));
|
||||
for(int i=1; i<c->sz; i++) {
|
||||
sprintf(term, "%s+%s", term, pvar(c->to[i]));
|
||||
}
|
||||
polynomialArray.push_back(term);
|
||||
printf("%s\n", term);
|
||||
printf("( %d %d )", i * (S->C[i].neg ? -1 : 1), c->carrier);
|
||||
}
|
||||
else {
|
||||
printf("%d", i * (S->C[i].neg ? -1 : 1));
|
||||
}
|
||||
|
||||
if(c->type == Majority) {
|
||||
char term[1024];
|
||||
sprintf(term, "-%s-2*%s+%s", pvar(++t), pvar(i * (S->C[i].neg ? -1 : 1)), pvar(c->to[0]));
|
||||
for(int i=1; i<c->sz; i++) {
|
||||
sprintf(term, "%s+%s", term, pvar(c->to[i]));
|
||||
}
|
||||
polynomialArray.push_back(term);
|
||||
printf("%s\n", term);
|
||||
printf(" = %s ( ", gate_type[c->type].c_str());
|
||||
|
||||
for (int j = 0; j < c->sz; j++) {
|
||||
printf("%d ", c->to[j]);
|
||||
}
|
||||
puts(")");
|
||||
}
|
||||
|
||||
add_to_vstack(pvar(32));
|
||||
push_mstack(new Monomial(one, build_term_from_stack()));
|
||||
|
||||
Polynomial *result = build_poly();
|
||||
|
||||
std::sort(polys.begin(), polys.end(), poly_cmp);
|
||||
|
||||
for(auto p : polys) {
|
||||
printf("now: "); result->print(stdout);
|
||||
printf("div: "); p->print(stdout);
|
||||
subsitute(result, p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// for (int i = 1; i <= S->maxvar; i++) {
|
||||
// circuit *c = &(S->C[i]);
|
||||
// if (!c->sz || S->del[i] == 2) continue;
|
||||
|
||||
// if(c->type == And) {
|
||||
// char term[1024];
|
||||
// sprintf(term, "-%s+%s", pvar(i * (S->C[i].neg ? -1 : 1)), pvar(c->to[0]));
|
||||
// for(int i=1; i<c->sz; i++) {
|
||||
// sprintf(term, "%s*%s", term, pvar(c->to[i]));
|
||||
// }
|
||||
// polynomialArray.push_back(term);
|
||||
// printf("%s\n", term);
|
||||
// }
|
||||
// if(c->type == Xor) {
|
||||
// string last = pvar(c->to[0]);
|
||||
// char term[1024];
|
||||
// for(int i=1; i<c->sz-1; i++) {
|
||||
// string tmp = pvar(++t);
|
||||
// sprintf(term, "-%s-2*%s*%s+%s+%s", tmp.c_str(), last.c_str(), pvar(c->to[i]), last.c_str(), pvar(c->to[i]));
|
||||
// polynomialArray.push_back(term);
|
||||
// printf("%s\n", term);
|
||||
// last = tmp;
|
||||
// }
|
||||
// sprintf(term, "-%s-2*%s*%s+%s+%s", pvar(i * (S->C[i].neg ? -1 : 1)), last.c_str(), pvar(c->to[c->sz-1]), last.c_str(), pvar(c->to[c->sz-1]));
|
||||
// polynomialArray.push_back(term);
|
||||
// printf("%s\n", term);
|
||||
// }
|
||||
|
||||
// if (c->type == HA || c->type == FA) {
|
||||
// char term[1024];
|
||||
// sprintf(term, "-%s-2*%s+%s", pvar(i * (S->C[i].neg ? -1 : 1)), pvar(c->carrier), pvar(c->to[0]));
|
||||
// for(int i=1; i<c->sz; i++) {
|
||||
// sprintf(term, "%s+%s", term, pvar(c->to[i]));
|
||||
// }
|
||||
// polynomialArray.push_back(term);
|
||||
// printf("%s\n", term);
|
||||
// }
|
||||
|
||||
// if(c->type == Majority) {
|
||||
// char term[1024];
|
||||
// sprintf(term, "-%s-2*%s+%s", pvar(++t), pvar(i * (S->C[i].neg ? -1 : 1)), pvar(c->to[0]));
|
||||
// for(int i=1; i<c->sz; i++) {
|
||||
// sprintf(term, "%s+%s", term, pvar(c->to[i]));
|
||||
// }
|
||||
// polynomialArray.push_back(term);
|
||||
// printf("%s\n", term);
|
||||
// }
|
||||
|
||||
// if (c->type == HA || c->type == FA) {
|
||||
// printf("( %d %d )", i * (S->C[i].neg ? -1 : 1), c->carrier);
|
||||
@ -98,24 +374,24 @@ int main(int argv, char* args[]) {
|
||||
// printf("%d ", c->to[j]);
|
||||
// }
|
||||
// puts(")");
|
||||
}
|
||||
// }
|
||||
|
||||
for(int i=t; i>=1; i--) {
|
||||
variableName.push_back(pvar(i));
|
||||
variableName.push_back(pvar(-i));
|
||||
char term[1024];
|
||||
sprintf(term, "-%s+%s^2", pvar(i), pvar(i));
|
||||
polynomialArray.emplace_back(term);
|
||||
printf("%s\n", term);
|
||||
sprintf(term, "%s+%s", pvar(i), pvar(-i));
|
||||
polynomialArray.emplace_back(term);
|
||||
printf("%s\n", term);
|
||||
}
|
||||
// for(int i=t; i>=1; i--) {
|
||||
// variableName.push_back(pvar(i));
|
||||
// variableName.push_back(pvar(-i));
|
||||
// char term[1024];
|
||||
// sprintf(term, "-%s+%s^2", pvar(i), pvar(i));
|
||||
// polynomialArray.emplace_back(term);
|
||||
// printf("%s\n", term);
|
||||
// sprintf(term, "%s+%s", pvar(i), pvar(-i));
|
||||
// polynomialArray.emplace_back(term);
|
||||
// printf("%s\n", term);
|
||||
// }
|
||||
|
||||
char term[1024];
|
||||
sprintf(term, "-%s+1", pvar(S->maxvar));
|
||||
polynomialArray.emplace_back(term);
|
||||
printf("%s\n", term);
|
||||
// char term[1024];
|
||||
// sprintf(term, "-%s+1", pvar(S->maxvar));
|
||||
// polynomialArray.emplace_back(term);
|
||||
// printf("%s\n", term);
|
||||
|
||||
// for(int i = 0; i < 6; i++)
|
||||
// {
|
||||
@ -131,17 +407,72 @@ int main(int argv, char* args[]) {
|
||||
// polynomialArray.emplace_back("x0*x1*x2*x3*x4*x5-1");
|
||||
|
||||
// Compute a reduce groebner basis.
|
||||
vector<string> basis = groebnerBasisF4(7, variableName.size(), variableName, polynomialArray, 1, 0);
|
||||
//vector<string> basis = groebnerBasisF4(7, variableName.size(), variableName, polynomialArray, 1, 0);
|
||||
|
||||
for(size_t i = 0; i < basis.size(); i++)
|
||||
{
|
||||
cout << basis[i] << endl;
|
||||
}
|
||||
// for(size_t i = 0; i < basis.size(); i++)
|
||||
// {
|
||||
// cout << basis[i] << endl;
|
||||
// }
|
||||
|
||||
for(int i=t; i>=1; i--) {
|
||||
printf("(declare-fun %s () Bool)", pvar(i));
|
||||
printf("(declare-fun %s () Bool)", pvar(-i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// for(int i=1; i<=t; i++) {
|
||||
// printf("(declare-fun %s () Bool)\n", pvar(i).c_str());
|
||||
// }
|
||||
|
||||
// for (int i = 1; i <= S->maxvar; i++) {
|
||||
// circuit *c = &(S->C[i]);
|
||||
// if (!c->sz || S->del[i] == 2) continue;
|
||||
// int var = i * (S->C[i].neg ? -1 : 1);
|
||||
|
||||
// if(c->type == And) {
|
||||
// string clause = "(assert (= " + pvar(var) + " (and";
|
||||
// for(int i=0; i<c->sz; i++) {
|
||||
// clause += " " + pvar(c->to[i]);
|
||||
// }
|
||||
// clause += ")))";
|
||||
// cout << clause << endl;
|
||||
// }
|
||||
// if(c->type == Xor) {
|
||||
// string clause = "(assert (= " + pvar(var) + " (xor";
|
||||
// for(int i=0; i<c->sz; i++) {
|
||||
// clause += " " + pvar(c->to[i]);
|
||||
// }
|
||||
// clause += ")))";
|
||||
// cout << clause << endl;
|
||||
// }
|
||||
|
||||
// if (c->type == HA || c->type == FA) {
|
||||
// string clause = "(assert (= (+ " + pvar(var) + " (* 2 "+pvar(c->carrier)+")) (+ ";
|
||||
// for(int i=0; i<c->sz; i++) {
|
||||
// clause += " " + pvar(c->to[i]);
|
||||
// }
|
||||
// clause += ")))";
|
||||
// cout << clause << endl;
|
||||
// }
|
||||
|
||||
// if(c->type == Majority) {
|
||||
// string ta = pvar(c->to[0]);
|
||||
// string tb = pvar(c->to[1]);
|
||||
// string tc = pvar(c->to[2]);
|
||||
// string clause = "(assert (= "+pvar(var)+" (or (and "+ta+" "+tb+") (and "+ta+" "+tc+") (and "+tb+" "+tc+"))))";
|
||||
// cout << clause << endl;
|
||||
// }
|
||||
|
||||
// // if (c->type == HA || c->type == FA) {
|
||||
// // printf("( %d %d )", i * (S->C[i].neg ? -1 : 1), c->carrier);
|
||||
// // }
|
||||
// // else {
|
||||
// // printf("%d", i * (S->C[i].neg ? -1 : 1));
|
||||
// // }
|
||||
|
||||
// // printf(" = %s ( ", gate_type[c->type].c_str());
|
||||
|
||||
// // for (int j = 0; j < c->sz; j++) {
|
||||
// // printf("%d ", c->to[j]);
|
||||
// // }
|
||||
// // puts(")");
|
||||
// }
|
||||
|
||||
// printf("(assert (= false %s))\n", pvar(S->maxvar).c_str());
|
||||
|
||||
// printf("(check-sat)\n");
|
||||
//printf("(get-model)\n");
|
63
circuit.cpp
63
circuit.cpp
@ -46,8 +46,8 @@ bool Sweep::match_majority(int x) {
|
||||
if (C[-v].outs <= 1) del[-v] = 2;
|
||||
if (C[-w].outs <= 1) del[-w] = 2;
|
||||
|
||||
C[x][0] = a; C[x][1] = b;
|
||||
C[x].push(C[-w][1]);
|
||||
C[x][0] = -a; C[x][1] = -b;
|
||||
C[x].push(-C[-w][1]);
|
||||
C[x].type = Majority;
|
||||
return true;
|
||||
}
|
||||
@ -163,7 +163,6 @@ void Sweep::match_FA() {
|
||||
|
||||
for(int i=1; i<=maxvar; i++) {
|
||||
if (!C[i].sz || del[i] == 2) continue;
|
||||
//printf("X: %d\n", i);
|
||||
|
||||
circuit *c = &C[i];
|
||||
|
||||
@ -347,7 +346,7 @@ void Sweep::to_dot_graph(const char* filename, int end_point) {
|
||||
/**
|
||||
* 删除初始输入
|
||||
*/
|
||||
if(abs(c->to[j]) <= num_inputs) continue;
|
||||
if(abs(c->to[j]) <= 14) continue;
|
||||
|
||||
sprintf(str, "A%d->A%d[arrowhead=%s]\n", abs(c->to[j]), u, !line_positive(c->to[j]) ? "dot" : "none");
|
||||
file << str;
|
||||
@ -365,6 +364,7 @@ void Sweep::to_dot_graph(const char* filename, int end_point) {
|
||||
}
|
||||
|
||||
void Sweep::identify() {
|
||||
|
||||
del = new int[maxvar + 1];
|
||||
for (int i = 1; i <= maxvar; i++) del[i] = 0;
|
||||
recalculate_outs();
|
||||
@ -373,21 +373,19 @@ void Sweep::identify() {
|
||||
if (match_xor(i)) continue;
|
||||
}
|
||||
|
||||
recalculate_outs();
|
||||
// recalculate_outs();
|
||||
|
||||
for (int i = 1; i <= maxvar; i++) {
|
||||
if (!C[i].sz || del[i] == 2) continue;
|
||||
if (match_majority(i)) continue;
|
||||
}
|
||||
// for (int i = 1; i <= maxvar; i++) {
|
||||
// if (!C[i].sz || del[i] == 2) continue;
|
||||
// if (match_majority(i)) continue;
|
||||
// }
|
||||
|
||||
recalculate_outs();
|
||||
|
||||
adjust_not();
|
||||
match_HA();
|
||||
//adjust_not();
|
||||
//match_HA();
|
||||
|
||||
to_multi_input_gates();
|
||||
|
||||
match_FA();
|
||||
//to_multi_input_gates();
|
||||
//match_FA();
|
||||
|
||||
// printf("digraph \"graph\" {\n");
|
||||
// for (int i = 1; i <= maxvar; i++) {
|
||||
@ -411,8 +409,35 @@ void Sweep::identify() {
|
||||
// }
|
||||
// printf("}\n");
|
||||
|
||||
//to_dot_graph("graph1.dot", abs(C[maxvar].to[0]));
|
||||
//to_dot_graph("graph2.dot", abs(C[maxvar].to[1]));
|
||||
recalculate_outs();
|
||||
// for (int i = 1; i <= maxvar; i++) {
|
||||
// circuit *c = &C[i];
|
||||
// if (!c->sz || del[i] == 2) continue;
|
||||
|
||||
// if (c->type == HA || c->type == FA)
|
||||
// printf("( %d %d )", i * (C[i].neg ? -1 : 1), c->carrier);
|
||||
// else
|
||||
// printf("%d", i * (C[i].neg ? -1 : 1));
|
||||
|
||||
// printf(" = %s ( ", gate_type[c->type].c_str());
|
||||
|
||||
// for (int j = 0; j < c->sz; j++) {
|
||||
// printf("%d ", c->to[j]);
|
||||
// }
|
||||
|
||||
// puts(")");
|
||||
// }
|
||||
|
||||
inv_C = new vec<int>[maxvar + 1];
|
||||
for (int i = 1; i <= maxvar; i++) {
|
||||
circuit *c = &C[i];
|
||||
if (!c->sz || del[i] == 2) continue;
|
||||
for (int j = 0; j < c->sz; j++) {
|
||||
inv_C[abs(c->to[j])].push(i);
|
||||
}
|
||||
}
|
||||
// to_dot_graph("graph1.dot", 291);
|
||||
// to_dot_graph("graph2.dot", 175);
|
||||
|
||||
// recalculate_outs();
|
||||
// for (int i = 1; i <= maxvar; i++) {
|
||||
@ -420,7 +445,5 @@ void Sweep::identify() {
|
||||
// if (del[i] == 2) continue;
|
||||
// printf("%d : %d (outs)\n", i, c->outs);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
408
cms.log
Normal file
408
cms.log
Normal file
@ -0,0 +1,408 @@
|
||||
-293 -292 0
|
||||
-293 176 0
|
||||
292 -176 293 0
|
||||
-294 292 0
|
||||
-294 -176 0
|
||||
-292 176 294 0
|
||||
289 231 292 0
|
||||
-173 154 176 0
|
||||
-289 -288 0
|
||||
-289 287 0
|
||||
288 -287 289 0
|
||||
228 197 231 0
|
||||
-173 172 0
|
||||
-173 -157 0
|
||||
-172 157 173 0
|
||||
-151 124 154 0
|
||||
-288 -285 0
|
||||
-288 -242 0
|
||||
285 242 288 0
|
||||
-287 -286 0
|
||||
-287 -249 0
|
||||
286 249 287 0
|
||||
225 208 228 0
|
||||
-197 -196 0
|
||||
-197 195 0
|
||||
196 -195 197 0
|
||||
-172 171 0
|
||||
-172 -160 0
|
||||
-171 160 172 0
|
||||
-150 127 157 0
|
||||
-151 150 0
|
||||
-151 -127 0
|
||||
-150 127 151 0
|
||||
121 88 124 0
|
||||
-285 -284 0
|
||||
-285 283 0
|
||||
284 -283 285 0
|
||||
-242 -241 0
|
||||
-242 240 0
|
||||
241 -240 242 0
|
||||
-286 -285 0
|
||||
-286 -248 0
|
||||
285 248 286 0
|
||||
-249 -248 0
|
||||
-249 -242 0
|
||||
248 242 249 0
|
||||
222 216 225 0
|
||||
205 200 208 0
|
||||
-196 -193 0
|
||||
-196 -180 0
|
||||
193 180 196 0
|
||||
-195 -194 0
|
||||
-195 -187 0
|
||||
194 187 195 0
|
||||
-171 170 0
|
||||
-171 -163 0
|
||||
-170 163 171 0
|
||||
149 130 160 0
|
||||
-150 -149 0
|
||||
-150 -130 0
|
||||
149 130 150 0
|
||||
-119 116 127 0
|
||||
-121 -120 0
|
||||
-121 -115 0
|
||||
120 115 121 0
|
||||
85 78 88 0
|
||||
-284 -281 0
|
||||
-284 -255 0
|
||||
281 255 284 0
|
||||
-283 -282 0
|
||||
-283 -262 0
|
||||
282 262 283 0
|
||||
-241 -238 0
|
||||
-241 97 0
|
||||
238 -97 241 0
|
||||
-240 -239 0
|
||||
-240 -232 0
|
||||
239 232 240 0
|
||||
245 193 248 0
|
||||
-219 42 222 0
|
||||
213 20 216 0
|
||||
-205 -204 0
|
||||
-205 203 0
|
||||
204 -203 205 0
|
||||
-41 27 200 0
|
||||
190 31 193 0
|
||||
-180 179 0
|
||||
-180 -116 0
|
||||
-179 116 180 0
|
||||
-194 -193 0
|
||||
-194 -186 0
|
||||
193 186 194 0
|
||||
-187 -186 0
|
||||
-187 -180 0
|
||||
186 180 187 0
|
||||
-170 169 0
|
||||
-170 -166 0
|
||||
-169 166 170 0
|
||||
-147 144 163 0
|
||||
-149 -148 0
|
||||
-149 -143 0
|
||||
148 143 149 0
|
||||
112 109 130 0
|
||||
114 91 119 0
|
||||
-116 106 0
|
||||
-116 105 0
|
||||
-106 -105 116 0
|
||||
-120 -119 0
|
||||
-120 116 0
|
||||
119 -116 120 0
|
||||
-115 -114 0
|
||||
-115 -91 0
|
||||
114 91 115 0
|
||||
-82 81 85 0
|
||||
75 48 78 0
|
||||
-281 -280 0
|
||||
-281 279 0
|
||||
280 -279 281 0
|
||||
-255 -254 0
|
||||
-255 253 0
|
||||
254 -253 255 0
|
||||
-282 -281 0
|
||||
-282 -261 0
|
||||
281 261 282 0
|
||||
-262 -261 0
|
||||
-262 -255 0
|
||||
261 255 262 0
|
||||
-235 105 238 0
|
||||
-97 96 0
|
||||
-97 95 0
|
||||
-96 -95 97 0
|
||||
-239 -238 0
|
||||
-239 -58 0
|
||||
238 58 239 0
|
||||
-232 97 0
|
||||
-232 -58 0
|
||||
-97 58 232 0
|
||||
186 180 245 0
|
||||
82 21 219 0
|
||||
-42 5 0
|
||||
-42 4 0
|
||||
-5 -4 42 0
|
||||
-213 -212 0
|
||||
-213 211 0
|
||||
212 -211 213 0
|
||||
17 16 20 0
|
||||
-204 63 0
|
||||
-204 -31 0
|
||||
-63 31 204 0
|
||||
-203 -202 0
|
||||
-203 -201 0
|
||||
202 201 203 0
|
||||
38 37 41 0
|
||||
-27 26 0
|
||||
-27 25 0
|
||||
-26 -25 27 0
|
||||
63 54 190 0
|
||||
26 25 31 0
|
||||
-179 -178 0
|
||||
-179 -177 0
|
||||
178 177 179 0
|
||||
-183 62 186 0
|
||||
-169 168 0
|
||||
-169 167 0
|
||||
-168 -167 169 0
|
||||
-140 137 166 0
|
||||
142 133 147 0
|
||||
-144 12 0
|
||||
-144 6 0
|
||||
-12 -6 144 0
|
||||
-148 -147 0
|
||||
-148 144 0
|
||||
147 -144 148 0
|
||||
-143 -142 0
|
||||
-143 -133 0
|
||||
142 133 143 0
|
||||
103 94 112 0
|
||||
106 105 109 0
|
||||
-114 -113 0
|
||||
-114 -104 0
|
||||
113 104 114 0
|
||||
73 70 91 0
|
||||
-106 12 0
|
||||
-106 2 0
|
||||
-12 -2 106 0
|
||||
-105 6 0
|
||||
-105 5 0
|
||||
-6 -5 105 0
|
||||
-82 12 0
|
||||
-82 9 0
|
||||
-12 -9 82 0
|
||||
-81 -80 0
|
||||
-81 -79 0
|
||||
80 79 81 0
|
||||
-75 -74 0
|
||||
-75 -61 0
|
||||
74 61 75 0
|
||||
45 36 48 0
|
||||
-280 -277 0
|
||||
-280 -101 0
|
||||
277 101 280 0
|
||||
-279 -278 0
|
||||
-279 -269 0
|
||||
278 269 279 0
|
||||
-254 251 0
|
||||
-254 144 0
|
||||
-251 -144 254 0
|
||||
-253 -252 0
|
||||
-253 -250 0
|
||||
252 250 253 0
|
||||
258 238 261 0
|
||||
106 55 235 0
|
||||
-96 15 0
|
||||
-96 8 0
|
||||
-15 -8 96 0
|
||||
-95 13 0
|
||||
-95 3 0
|
||||
-13 -3 95 0
|
||||
53 52 58 0
|
||||
-21 11 0
|
||||
-21 7 0
|
||||
-11 -7 21 0
|
||||
-212 67 0
|
||||
-212 62 0
|
||||
-67 -62 212 0
|
||||
-211 -210 0
|
||||
-211 -209 0
|
||||
210 209 211 0
|
||||
-17 10 0
|
||||
-17 8 0
|
||||
-10 -8 17 0
|
||||
-16 14 0
|
||||
-16 13 0
|
||||
-14 -13 16 0
|
||||
-63 5 0
|
||||
-63 2 0
|
||||
-5 -2 63 0
|
||||
-202 54 0
|
||||
-202 -31 0
|
||||
-54 31 202 0
|
||||
-201 63 0
|
||||
-201 54 0
|
||||
-63 -54 201 0
|
||||
-38 15 0
|
||||
-38 2 0
|
||||
-15 -2 38 0
|
||||
-37 6 0
|
||||
-37 3 0
|
||||
-6 -3 37 0
|
||||
-26 8 0
|
||||
-26 7 0
|
||||
-8 -7 26 0
|
||||
-25 13 0
|
||||
-25 10 0
|
||||
-13 -10 25 0
|
||||
-54 53 0
|
||||
-54 52 0
|
||||
-53 -52 54 0
|
||||
-178 105 0
|
||||
-178 55 0
|
||||
-105 -55 178 0
|
||||
-177 106 0
|
||||
-177 55 0
|
||||
-106 -55 177 0
|
||||
67 28 183 0
|
||||
-62 15 0
|
||||
-62 6 0
|
||||
-15 -6 62 0
|
||||
-168 12 0
|
||||
-168 8 0
|
||||
-12 -8 168 0
|
||||
-167 13 0
|
||||
-167 5 0
|
||||
-13 -5 167 0
|
||||
135 134 140 0
|
||||
-137 12 0
|
||||
-137 11 0
|
||||
-12 -11 137 0
|
||||
-142 -141 0
|
||||
-142 -136 0
|
||||
141 136 142 0
|
||||
-101 98 133 0
|
||||
-103 -102 0
|
||||
-103 -97 0
|
||||
102 97 103 0
|
||||
-58 55 94 0
|
||||
-113 -112 0
|
||||
-113 -109 0
|
||||
112 109 113 0
|
||||
-104 -103 0
|
||||
-104 -94 0
|
||||
103 94 104 0
|
||||
60 51 73 0
|
||||
-67 66 70 0
|
||||
-80 67 0
|
||||
-80 -66 0
|
||||
-67 66 80 0
|
||||
-79 63 0
|
||||
-79 62 0
|
||||
-63 -62 79 0
|
||||
-74 -73 0
|
||||
-74 -70 0
|
||||
73 70 74 0
|
||||
-61 -60 0
|
||||
-61 -51 0
|
||||
60 51 61 0
|
||||
-42 41 45 0
|
||||
33 24 36 0
|
||||
-277 -276 0
|
||||
-277 275 0
|
||||
276 -275 277 0
|
||||
96 95 101 0
|
||||
-278 -277 0
|
||||
-278 -268 0
|
||||
277 268 278 0
|
||||
-269 -268 0
|
||||
-269 -101 0
|
||||
268 101 269 0
|
||||
-251 137 0
|
||||
-251 135 0
|
||||
-137 -135 251 0
|
||||
-252 251 0
|
||||
-252 98 0
|
||||
-251 -98 252 0
|
||||
-250 144 0
|
||||
-250 98 0
|
||||
-144 -98 250 0
|
||||
-97 58 258 0
|
||||
-55 15 0
|
||||
-55 11 0
|
||||
-15 -11 55 0
|
||||
-53 8 0
|
||||
-53 3 0
|
||||
-8 -3 53 0
|
||||
-52 13 0
|
||||
-52 7 0
|
||||
-13 -7 52 0
|
||||
-67 12 0
|
||||
-67 4 0
|
||||
-12 -4 67 0
|
||||
-210 62 0
|
||||
-210 28 0
|
||||
-62 -28 210 0
|
||||
-209 67 0
|
||||
-209 28 0
|
||||
-67 -28 209 0
|
||||
-28 11 0
|
||||
-28 3 0
|
||||
-11 -3 28 0
|
||||
-135 8 0
|
||||
-135 5 0
|
||||
-8 -5 135 0
|
||||
-134 15 0
|
||||
-134 13 0
|
||||
-15 -13 134 0
|
||||
-141 -140 0
|
||||
-141 137 0
|
||||
140 -137 141 0
|
||||
-136 135 0
|
||||
-136 134 0
|
||||
-135 -134 136 0
|
||||
-98 11 0
|
||||
-98 5 0
|
||||
-11 -5 98 0
|
||||
-102 -101 0
|
||||
-102 98 0
|
||||
101 -98 102 0
|
||||
-60 -59 0
|
||||
-60 -54 0
|
||||
59 54 60 0
|
||||
-31 28 51 0
|
||||
63 62 66 0
|
||||
-33 -32 0
|
||||
-33 -27 0
|
||||
32 27 33 0
|
||||
-21 20 24 0
|
||||
-276 -273 0
|
||||
-276 134 0
|
||||
273 -134 276 0
|
||||
-275 -274 0
|
||||
-275 -270 0
|
||||
274 270 275 0
|
||||
-265 251 268 0
|
||||
-59 -58 0
|
||||
-59 55 0
|
||||
58 -55 59 0
|
||||
-32 -31 0
|
||||
-32 28 0
|
||||
31 -28 32 0
|
||||
137 135 273 0
|
||||
-274 -273 0
|
||||
-274 169 0
|
||||
273 -169 274 0
|
||||
-270 169 0
|
||||
-270 134 0
|
||||
-169 -134 270 0
|
||||
144 98 265 0
|
||||
-293 -294 0
|
||||
293 294 0
|
||||
-251 252 0
|
||||
251 -252 0
|
||||
-253 255 0
|
||||
253 -255 0
|
||||
-254 266 0
|
||||
254 -266 0
|
||||
-176 292 0
|
||||
176 -292 0
|
||||
c Solver::solve( )
|
1
f4cpp
1
f4cpp
@ -1 +0,0 @@
|
||||
Subproject commit 645458ca02450215bd0e891fc3081d8cbc79c51f
|
291
fraig.cpp
Normal file
291
fraig.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
#include "sweep.hpp"
|
||||
#include "circuit.hpp"
|
||||
#include "src/cadical.hpp"
|
||||
extern "C" {
|
||||
#include "aiger.h"
|
||||
}
|
||||
|
||||
void Sweep::propagate(int* input, int* result) {
|
||||
|
||||
std::queue<int> q;
|
||||
|
||||
for(int i=0; i<aig->num_inputs; i++) {
|
||||
result[aig->inputs[i].lit] = input[i];
|
||||
result[aig->inputs[i].lit+1] = !input[i];
|
||||
q.push(aiger_lit2var(aig->inputs[i].lit));
|
||||
}
|
||||
|
||||
static int* topo_counter = new int[maxvar + 1];
|
||||
memset(topo_counter, 0, (maxvar + 1) * sizeof(int));
|
||||
|
||||
while(!q.empty()) {
|
||||
int cur = q.front();
|
||||
q.pop();
|
||||
for(auto& edge : graph.edge[cur]) {
|
||||
int v = edge.to;
|
||||
topo_counter[v]++;
|
||||
|
||||
if(topo_counter[v] == 2) {
|
||||
q.push(v);
|
||||
|
||||
//cout << "redge: " << v << " " << graph.redge[v][0].to << " " << graph.redge[v][1].to << endl;
|
||||
|
||||
Graph::Edge rhs0_edge = graph.redge[v][0];
|
||||
int rhs0 = aiger_var2lit(rhs0_edge.to);
|
||||
|
||||
Graph::Edge rhs1_edge = graph.redge[v][1];
|
||||
int rhs1 = aiger_var2lit(rhs1_edge.to);
|
||||
|
||||
result[aiger_var2lit(v)] = result[rhs0 + rhs0_edge.neg] & result[rhs1 + rhs1_edge.neg];
|
||||
result[aiger_var2lit(v)+1] = !result[aiger_var2lit(v)];
|
||||
|
||||
//cout << "var: " << aiger_var2lit(v) << " = " << rhs0 + rhs0_edge.neg << " " << rhs1 + rhs1_edge.neg << " : " << result[aiger_var2lit(v)] << endl;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sweep::circuit_propagate(int *input, int* result) {
|
||||
std::queue<int> q;
|
||||
|
||||
for(int i=1; i<=aig->num_inputs; i++) {
|
||||
result[i] = input[i - 1];
|
||||
q.push(i);
|
||||
// printf("%d %d\n", i, result[i]);
|
||||
}
|
||||
for (int i = 1; i <= maxvar; i++) seen[i] = 0;
|
||||
while(!q.empty()) {
|
||||
int x = q.front();
|
||||
q.pop();
|
||||
for (int i = 0; i < inv_C[x].size(); i++) {
|
||||
int v = inv_C[x][i];
|
||||
seen[v]++;
|
||||
if (seen[v] == C[v].sz) {
|
||||
q.push(v);
|
||||
if (C[v].type == And) {
|
||||
result[v] = 1;
|
||||
for (int j = 0; j < C[v].sz; j++) {
|
||||
int x = C[v][j];
|
||||
result[v] &= x > 0 ? result[abs(x)] : !result[abs(x)];
|
||||
}
|
||||
}
|
||||
else if (C[v].type == Xor) {
|
||||
result[v] = 0;
|
||||
for (int j = 0; j < C[v].sz; j++) {
|
||||
int x = C[v][j];
|
||||
result[v] ^= x > 0 ? result[abs(x)] : !result[abs(x)];
|
||||
}
|
||||
}
|
||||
else if (C[v].type == Majority) {
|
||||
int s[2] = {0, 0};
|
||||
for (int j = 0; j < C[v].sz; j++) {
|
||||
int x = C[v][j];
|
||||
s[x > 0 ? result[abs(x)] : !result[abs(x)]]++;
|
||||
}
|
||||
result[v] = s[1] > s[0] ? 1 : 0;
|
||||
}
|
||||
else if (C[v].type == HA) {
|
||||
assert(C[v].sz == 2);
|
||||
int s = 0;
|
||||
for (int j = 0; j < C[v].sz; j++) {
|
||||
int x = C[v][j];
|
||||
s += x > 0 ? result[abs(x)] : !result[abs(x)];
|
||||
}
|
||||
result[v] = s % 2;
|
||||
result[abs(C[v].carrier)] = s >> 1;
|
||||
if (C[v].carrier < 0) result[abs(C[v].carrier)] = !result[abs(C[v].carrier)];
|
||||
q.push(abs(C[v].carrier));
|
||||
}
|
||||
else if (C[v].type == FA) {
|
||||
assert(C[v].sz == 3);
|
||||
int s = 0;
|
||||
for (int j = 0; j < C[v].sz; j++) {
|
||||
int x = C[v][j];
|
||||
s += x > 0 ? result[abs(x)] : !result[abs(x)];
|
||||
}
|
||||
result[v] = s % 2;
|
||||
result[abs(C[v].carrier)] = s >> 1;
|
||||
if (C[v].carrier < 0) result[abs(C[v].carrier)] = !result[abs(C[v].carrier)];
|
||||
q.push(abs(C[v].carrier));
|
||||
}
|
||||
if (C[v].neg) result[v] = !result[v];
|
||||
// printf("%d %d\n", v, result[v]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// int Sweep::seen_cut_points(aiger* aig, int x, int y) {
|
||||
// std::queue<int> q;
|
||||
// int *used =
|
||||
|
||||
// x = x & 1 ? -aiger_lit2var(x) : aiger_lit2var(x);
|
||||
// y = y & 1 ? -aiger_lit2var(y) : aiger_lit2var(y);
|
||||
|
||||
// if (!used[abs(x)])
|
||||
// q.push(abs(x)), used[abs(x)] = true;
|
||||
// if(!used[abs(y)])
|
||||
// q.push(abs(y)), used[abs(y)] = true;
|
||||
|
||||
// }
|
||||
|
||||
bool Sweep::check_var_equal(int x, int y, int assume) {
|
||||
|
||||
std::queue<int> q;
|
||||
|
||||
x = x & 1 ? -aiger_lit2var(x) : aiger_lit2var(x);
|
||||
y = y & 1 ? -aiger_lit2var(y) : aiger_lit2var(y);
|
||||
|
||||
if (!used[abs(x)])
|
||||
q.push(abs(x)), used[abs(x)] = true;
|
||||
if(!used[abs(y)])
|
||||
q.push(abs(y)), used[abs(y)] = true;
|
||||
|
||||
int can_del = 2;
|
||||
|
||||
while(!q.empty()) {
|
||||
int u = q.front();
|
||||
q.pop();
|
||||
|
||||
/*
|
||||
(u => a & b) <=> ~u | (a & b) <=> (~u | a) & (~u | b)
|
||||
(u <= a & b) <=> ~(a & b) | u <=> ~a | ~b | u
|
||||
*/
|
||||
if(graph.redge[u].size() == 0) continue;
|
||||
|
||||
int a = graph.redge[u][0].to;
|
||||
int b = graph.redge[u][1].to;
|
||||
|
||||
//cout << "AND " << aiger_var2lit(u) << " " << (aiger_var2lit(a) + graph.redge[u][0].neg) << " " << (aiger_var2lit(b) + graph.redge[u][1].neg) << endl;
|
||||
|
||||
if(graph.redge[u][0].neg) a = -a;
|
||||
if(graph.redge[u][1].neg) b = -b;
|
||||
|
||||
|
||||
solver->add(-u), solver->add( a), solver->add(0);
|
||||
solver->add(-u), solver->add( b), solver->add(0);
|
||||
solver->add(-a), solver->add(-b), solver->add(u), solver->add(0);
|
||||
|
||||
a = abs(a), b = abs(b);
|
||||
|
||||
if(!used[a])
|
||||
q.push(a), used[a] = true, can_del++;
|
||||
|
||||
if(!used[b])
|
||||
q.push(b), used[b] = true, can_del++;
|
||||
}
|
||||
|
||||
//Assume -NV
|
||||
solver->add(-assume), solver->add(-x), solver->add(-y), solver->add(0);
|
||||
solver->add(-assume), solver->add(x), solver->add(y), solver->add(0);
|
||||
|
||||
solver->assume (assume);
|
||||
|
||||
int result = solver->solve ();
|
||||
|
||||
// if (result == 20) printf("can delete %d vars\n", can_del);
|
||||
return result == 20;
|
||||
}
|
||||
|
||||
bool Sweep::check_constant(int x, int val) {
|
||||
|
||||
std::queue<int> q;
|
||||
x = x & 1 ? -aiger_lit2var(x) : aiger_lit2var(x);
|
||||
|
||||
if (!used[abs(x)]) q.push(abs(x)), used[abs(x)] = true;
|
||||
|
||||
while(!q.empty()) {
|
||||
int u = q.front();
|
||||
q.pop();
|
||||
|
||||
if(graph.redge[u].size() == 0) continue;
|
||||
|
||||
int a = graph.redge[u][0].to;
|
||||
int b = graph.redge[u][1].to;
|
||||
if(graph.redge[u][0].neg) a = -a;
|
||||
if(graph.redge[u][1].neg) b = -b;
|
||||
|
||||
solver->add(-u), solver->add( a), solver->add(0);
|
||||
solver->add(-u), solver->add( b), solver->add(0);
|
||||
solver->add(-a), solver->add(-b), solver->add(u), solver->add(0);
|
||||
|
||||
a = abs(a), b = abs(b);
|
||||
|
||||
if(!used[a]) q.push(a), used[a] = true;
|
||||
if(!used[b]) q.push(b), used[b] = true;
|
||||
}
|
||||
|
||||
solver->assume (x * (val ? -1 : 1));
|
||||
|
||||
int result = solver->solve();
|
||||
return result == 20;
|
||||
}
|
||||
|
||||
void Sweep::random_simulation() {
|
||||
|
||||
int* result = new int[2 * maxvar + 2];
|
||||
int* circuit_res = new int[maxvar + 1];
|
||||
int* input_vector = new int[aig->num_inputs];
|
||||
|
||||
for(int i=0; i<rounds; i++)
|
||||
{
|
||||
if (i % 10000 == 0) printf("================================= %d ===========================\n", i);
|
||||
for(int i=0; i<aig->num_inputs; i++) {
|
||||
input_vector[i] = rand() % 2;
|
||||
}
|
||||
for (int i=0; i<det_v_sz; i++) {
|
||||
if (abs(det_v[i]) <= aig->num_inputs) input_vector[abs(det_v[i]) - 1] = det_v[i] > 0 ? 1 : 0;
|
||||
}
|
||||
memset(result, -1, (2 * maxvar + 2) * sizeof(int));
|
||||
memset(circuit_res, -1, (maxvar + 1) * sizeof(int));
|
||||
|
||||
propagate(input_vector, result);
|
||||
circuit_propagate(input_vector, circuit_res);
|
||||
if (result[output] != !circuit_res[output >> 1]) {
|
||||
printf("wrong %d %d %d\n", output, !result[output], circuit_res[output >> 1]);
|
||||
for (int i = 1; i <= maxvar; i++) {
|
||||
if (circuit_res[i] == -1) continue;
|
||||
if (result[i * 2] != circuit_res[i]) printf("%d %d %d\n", i, result[i * 2], circuit_res[i]);
|
||||
printf("%d %d\n", i, result[i << 1]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
std::vector<std::vector<int>> tmp;
|
||||
for(auto& clas : classes) {
|
||||
std::vector<int> v0;
|
||||
std::vector<int> v1;
|
||||
for(auto &v : clas) {
|
||||
if(result[v]) v1.push_back(v), sum_pos[v] += 1;
|
||||
else v0.push_back(v), sum_neg[v] += 1;
|
||||
}
|
||||
if(v0.size() > 1) tmp.push_back(v0);
|
||||
if(v1.size() > 1) tmp.push_back(v1);
|
||||
}
|
||||
|
||||
classes = tmp;
|
||||
}
|
||||
printf("c Number of equivalent classes: %d\n", classes.size());
|
||||
// printf("classes: \n");
|
||||
// for(auto& clas : classes) {
|
||||
// int lit = clas[0];
|
||||
// printf("[ ");
|
||||
// for(auto &v : clas) {
|
||||
// printf("%d ", v);
|
||||
// }
|
||||
// printf("] %d %d\n", sum_pos[lit], sum_neg[lit]);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
void Sweep::simulation() {
|
||||
//printf("c =================================== detect =================================\n");
|
||||
seen = new int[maxvar + 1];
|
||||
classes.push_back(std::vector<int>());
|
||||
for(int i=2; i<=maxvar*2+1; i++) {
|
||||
classes[0].push_back(i);
|
||||
}
|
||||
|
||||
random_simulation();
|
||||
}
|
104
hCaD_V2/BUILD.md
Normal file
104
hCaD_V2/BUILD.md
Normal file
@ -0,0 +1,104 @@
|
||||
# CaDiCaL Build
|
||||
|
||||
Use `./configure && make` to configure and build `cadical` in the default
|
||||
`build` sub-directory.
|
||||
|
||||
This will also build the library `libcadical.a` as well as the model based
|
||||
tester `mobical`:
|
||||
|
||||
build/cadical
|
||||
build/mobical
|
||||
build/libcadical.a
|
||||
|
||||
The header file of the library is in
|
||||
|
||||
src/cadical.hpp
|
||||
|
||||
The build process requires GNU make. Using the generated `makefile` with
|
||||
GNU make compiles separate object files, which can be cached (for instance
|
||||
with `ccache`). In order to force parallel build you can use the '-j'
|
||||
option either for 'configure' or with 'make'. If the environment variable
|
||||
'MAKEFLAGS' is set, e.g., 'MAKEFLAGS=-j ./configure', the same effect
|
||||
is achieved and the generated makefile will use those flags.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
You might want to check out options of `./configure -h`, such as
|
||||
|
||||
./configure -c # include assertion checking code
|
||||
|
||||
./configure -l # include code to really see what the solver is doing
|
||||
|
||||
./configure -a # both above and in addition `-g` for debugging.
|
||||
|
||||
You can easily use multiple build directories, e.g.,
|
||||
|
||||
mkdir debug; cd debug; ../configure -g; make
|
||||
|
||||
which compiles and builds a debugging version in the sub-directory `debug`,
|
||||
since `-g` was specified as parameter to `configure`. The object files,
|
||||
the library and the binaries are all independent of those in the default
|
||||
build directory `build`.
|
||||
|
||||
All source files reside in the `src` directory. The library `libcadical.a`
|
||||
is compiled from all the `.cpp` files except `cadical.cpp` and
|
||||
`mobical.cpp`, which provide the applications, i.e., the stand alone solver
|
||||
`cadical` and the model based tester `mobical`.
|
||||
|
||||
Manual Build
|
||||
------------
|
||||
|
||||
If you can not or do not want to rely on our `configure` script nor on our
|
||||
build system based on GNU `make`, then this is easily doable as follows.
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
for f in ../src/*.cpp; do g++ -O3 -DNDEBUG -DNBUILD -c $f; done
|
||||
ar rc libcadical.a `ls *.o | grep -v ical.o`
|
||||
g++ -o cadical cadical.o -L. -lcadical
|
||||
g++ -o mobical mobical.o -L. -lcadical
|
||||
|
||||
Note that application object files are excluded from the library.
|
||||
Of course you can use different compilation options as well.
|
||||
|
||||
Since `build.hpp` is not generated in this flow the `-DNBUILD` flag is
|
||||
necessary though, which avoids dependency of `version.cpp` on `build.hpp`.
|
||||
Consequently you will only get very basic version information compiled into
|
||||
the library and binaries (guaranteed is in essence just the version number
|
||||
of the library).
|
||||
|
||||
And if you really do not care about compilation time nor caching and just
|
||||
want to build the solver once manually then the following also works.
|
||||
|
||||
g++ -O3 -DNDEBUG -DNBUILD -o cadical `ls *.cpp | grep -v mobical`
|
||||
|
||||
Further note that the `configure` script provides some feature checks and
|
||||
might generate additional compiler flags necessary for compilation. You
|
||||
might need to set those yourself or just use a modern C++11 compiler.
|
||||
|
||||
This manual build process using object files is fast enough in combination
|
||||
with caching solutions such as `ccache`. But it lacks the ability of our
|
||||
GNU make solution to run compilation in parallel without additional parallel
|
||||
process scheduling solutions.
|
||||
|
||||
Cross-Compilation
|
||||
-----------------
|
||||
|
||||
We have preliminary support for cross-compilation using MinGW32 (only
|
||||
tested for a Linux compilation host and Windows-64 target host at this
|
||||
point).
|
||||
|
||||
There are two steps necessary to make this work. First make
|
||||
sure to be able to execute binaries compiled with the cross-compiler
|
||||
directly. For instance in order to use `wine` to execute the binaries
|
||||
on Linux you might want to look into the `binfmt_misc` module and
|
||||
registering the appropriate interpreter for `DOSWin`. As second step
|
||||
you simply tell the `configure` script to use the cross-compiler.
|
||||
|
||||
CXXFLAGS=-static CXX=i686-w64-mingw32-g++ ./configure
|
||||
|
||||
Note the use of '-static', which was necessary for me since by default
|
||||
`wine` did not find `libstdc++` if dynamically linked.
|
||||
|
||||
|
4
hCaD_V2/CONTRIBUTING
Normal file
4
hCaD_V2/CONTRIBUTING
Normal file
@ -0,0 +1,4 @@
|
||||
At this point we want to keep complete ownership in one hand
|
||||
to particularly avoid any additional co-authorship claims.
|
||||
Thus please refrain from generating pull requests. Use the issue
|
||||
tracker or send email to 'biere@jku.at' instead.
|
22
hCaD_V2/LICENSE
Normal file
22
hCaD_V2/LICENSE
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2021 Armin Biere, Johannes Kepler University Linz, Austria
|
||||
Copyright (c) 2020-2021 Mathias Fleury, Johannes Kepler University Linz, Austria
|
||||
|
||||
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.
|
54
hCaD_V2/README.md
Normal file
54
hCaD_V2/README.md
Normal file
@ -0,0 +1,54 @@
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||
[![Build Status](https://travis-ci.com/arminbiere/cadical.svg?branch=master)](https://travis-ci.com/arminbiere/cadical)
|
||||
|
||||
|
||||
CaDiCaL Simplified Satisfiability Solver
|
||||
===============================================================================
|
||||
|
||||
The goal of the development of CaDiCaL was to obtain a CDCL solver,
|
||||
which is easy to understand and change, while at the same time not being
|
||||
much slower than other state-of-the-art CDCL solvers.
|
||||
|
||||
Originally we wanted to also radically simplify the design and internal data
|
||||
structures, but that goal was only achieved partially, at least for instance
|
||||
compared to Lingeling.
|
||||
|
||||
However, the code is much better documented and CaDiCaL actually became in
|
||||
general faster than Lingeling even though it is missing some preprocessors
|
||||
(mostly parity and cardinality constraint reasoning), which would be crucial
|
||||
to solve certain instances.
|
||||
|
||||
Use `./configure && make` to configure and build `cadical` and the library
|
||||
`libcadical.a` in the default `build` sub-directory. The header file of
|
||||
the library is [`src/cadical.hpp`](src/cadical.hpp) and includes an example
|
||||
for API usage.
|
||||
|
||||
See [`BUILD.md`](BUILD.md) for options and more details related to the build
|
||||
process and [`test/README.md`](test/README.md) for testing the library and
|
||||
the solver.
|
||||
|
||||
The solver has the following usage `cadical [ dimacs [ proof ] ]`.
|
||||
See `cadical -h` for more options.
|
||||
|
||||
If you want to cite CaDiCaL please use the solver description in the
|
||||
latest SAT competition proceedings:
|
||||
|
||||
<pre>
|
||||
@inproceedings{BiereFazekasFleuryHeisinger-SAT-Competition-2020-solvers,
|
||||
author = {Armin Biere and Katalin Fazekas and Mathias Fleury and Maximillian Heisinger},
|
||||
title = {{CaDiCaL}, {Kissat}, {Paracooba}, {Plingeling} and {Treengeling}
|
||||
Entering the {SAT Competition 2020}},
|
||||
pages = {51--53},
|
||||
editor = {Tomas Balyo and Nils Froleyks and Marijn Heule and
|
||||
Markus Iser and Matti J{\"a}rvisalo and Martin Suda},
|
||||
booktitle = {Proc.~of {SAT Competition} 2020 -- Solver and Benchmark Descriptions},
|
||||
volume = {B-2020-1},
|
||||
series = {Department of Computer Science Report Series B},
|
||||
publisher = {University of Helsinki},
|
||||
year = 2020,
|
||||
}
|
||||
</pre>
|
||||
|
||||
You might also find more information on CaDiCaL at <http://fmv.jku.at/cadical>.
|
||||
|
||||
Armin Biere
|
1
hCaD_V2/VERSION
Normal file
1
hCaD_V2/VERSION
Normal file
@ -0,0 +1 @@
|
||||
1.4.1
|
2
hCaD_V2/bin/starexec_run_default
Normal file
2
hCaD_V2/bin/starexec_run_default
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec ./cadical --target=0 --walk=false $1 $2/proof.out
|
494
hCaD_V2/configure
vendored
Executable file
494
hCaD_V2/configure
vendored
Executable file
@ -0,0 +1,494 @@
|
||||
#!/bin/sh
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Run './configure' to produce a 'makefile' in the 'build' sub-directory or
|
||||
# in any immediate sub-directory different from the 'src', 'scripts' and
|
||||
# 'test' directories.
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
rm -f configure.log
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Common default options.
|
||||
|
||||
all=no
|
||||
debug=no
|
||||
logging=no
|
||||
check=no
|
||||
competition=no
|
||||
coverage=no
|
||||
profile=no
|
||||
contracts=yes
|
||||
tracing=yes
|
||||
unlocked=yes
|
||||
pedantic=no
|
||||
options=""
|
||||
quiet=no
|
||||
m32=no
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
if [ -f ./scripts/colors.sh ]
|
||||
then
|
||||
. ./scripts/colors.sh
|
||||
elif [ -f ../scripts/colors.sh ]
|
||||
then
|
||||
. ../scripts/colors.sh
|
||||
else
|
||||
BAD=""
|
||||
HILITE=""
|
||||
BOLD=""
|
||||
NORMAL=""
|
||||
fi
|
||||
|
||||
die () {
|
||||
if [ -f configure.log ]
|
||||
then
|
||||
checklog=" (check also 'configure.log')"
|
||||
else
|
||||
checklog=""
|
||||
fi
|
||||
cecho "${BOLD}configure:${NORMAL} ${BAD}error:${NORMAL} $*${checklog}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
msg () {
|
||||
cecho "${BOLD}configure:${NORMAL} $*"
|
||||
}
|
||||
|
||||
# if we can find the 'color.sh' script source it and overwrite color codes
|
||||
|
||||
for dir in . ..
|
||||
do
|
||||
[ -f $dir/scripts/colors.sh ] || continue
|
||||
. $dir/scripts/colors.sh || exit 1
|
||||
break
|
||||
done
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Parse and handle command line options.
|
||||
|
||||
usage () {
|
||||
cat << EOF
|
||||
usage: configure [ <option> ... ]
|
||||
|
||||
where '<option>' is one of the following
|
||||
|
||||
-h|--help print this command line summary
|
||||
-g|--debug compile with debugging information
|
||||
-c|--check compile with assertion checking (default for '-g')
|
||||
-l|--log[ging] include logging code (but disabled by default)
|
||||
-a|--all short cut for all above, e.g., '-g -l' (thus also '-c')
|
||||
-q|--quiet exclude message and profiling code (logging too)
|
||||
-p|--pedantic add '--pedantic' and '-Werror' compilation flag
|
||||
-s|--symbols add '-ggdb3' (even for optimized compilation)
|
||||
|
||||
--coverage compile with '-ftest-coverage -fprofile-arcs' for 'gcov'
|
||||
--profile compile with '-pg' to profile with 'gprof'
|
||||
|
||||
--no-contracts compile without API contract checking code
|
||||
--no-tracing compile without API call tracing code
|
||||
|
||||
--competition configure for the competition
|
||||
('--quiet', '--no-contracts', '--no-tracing')
|
||||
|
||||
-f... pass '-f<option>[=<val>]' options to the makefile
|
||||
-m32 pass '-m32' to the compiler (compile for 32 bit)
|
||||
-ggdb3 pass '-ggdb3' to makefile (like '-s')
|
||||
-O|-O[123] pass '-O' or '-O[123]' to the makefile
|
||||
-static... pass '-static...' option to makefile
|
||||
|
||||
The environment variable CXX can be used to set a different C++
|
||||
compiler than the default 'g++'. Similarly you can add additional
|
||||
compilation options by setting CXXFLAGS. For example
|
||||
|
||||
CXX=clang++ CXXFLAGS=-fPIC ./configure
|
||||
|
||||
will enforce to use 'clang++' as C++ compiler and also produce
|
||||
position independent code. In order to be shell independent we also
|
||||
allow to have the following form. Thus for instance
|
||||
|
||||
./configure CXX="g++-8" CXXFLAGS="-fPIC -fsanitize=address"
|
||||
|
||||
will have the same effect as
|
||||
|
||||
CXX="g++-8" ./configure -fPIC -fsanitize=address
|
||||
|
||||
The following configuration options might be usefull during porting the
|
||||
code to a new platform and are usually not necessary to change.
|
||||
|
||||
--no-unlocked force compilation without unlocked IO
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
case $1 in
|
||||
|
||||
-h|--help) usage;;
|
||||
|
||||
-a|--all) all=yes;;
|
||||
|
||||
-g|--debug) debug=yes;;
|
||||
-c|--check) check=yes;;
|
||||
-l|--log|--logging) logging=yes;;
|
||||
|
||||
-p|--pedantic) pedantic=yes;;
|
||||
-q|--quiet) quiet=yes;;
|
||||
|
||||
--no-contracts | --no-contract) contracts=no;;
|
||||
--no-tracing | --no-trace) tracing=no;;
|
||||
|
||||
--coverage) coverage=yes;;
|
||||
--profile) profile=yes;;
|
||||
|
||||
--competition) competition=yes;;
|
||||
|
||||
--no-unlocked) unlocked=no;;
|
||||
|
||||
-m32) options="$options $1";m32=yes;;
|
||||
-f*|-ggdb3|-O|-O1|-O2|-O3) options="$options $1";;
|
||||
-s|--symbols) options="$options -ggdb3";;
|
||||
-static*) options="$options $1";;
|
||||
|
||||
CXX=*)
|
||||
CXX="`expr \"$1\" : 'CXX=\(.*\)'`"
|
||||
;;
|
||||
|
||||
CXXFLAGS=*)
|
||||
CXXFLAGS="`expr \"$1\" : 'CXXFLAGS=\(.*\)'`"
|
||||
;;
|
||||
|
||||
*) die "invalid option '$1' (try '-h')";;
|
||||
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
if [ $quiet = yes ]
|
||||
then
|
||||
[ $logging = yes ] && die "can not combine '-q' with '-l'"
|
||||
fi
|
||||
|
||||
if [ $all = yes ]
|
||||
then
|
||||
[ $check = yes ] && die "'-a' subsumes '-c'"
|
||||
[ $debug = yes ] && die "'-a' subsumes '-g'"
|
||||
[ $logging = yes ] && die "'-a' subsumes '-l'"
|
||||
check=yes
|
||||
debug=yes
|
||||
logging=yes
|
||||
elif [ $debug = yes ]
|
||||
then
|
||||
[ $check = yes ] && die "'-g' subsumes '-c'"
|
||||
check=yes
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Generate and enter 'build' directory if not already in sub-directory.
|
||||
|
||||
build_in_default_build_sub_directory () {
|
||||
if [ -d build ]
|
||||
then
|
||||
msg "reusing default 'build' directory"
|
||||
else
|
||||
mkdir build 2>/dev/null || \
|
||||
die "failed to generate 'build' directory"
|
||||
msg "making default 'build' directory"
|
||||
fi
|
||||
cd build
|
||||
msg "building in default ${HILITE}'`pwd`'${NORMAL}"
|
||||
build=build
|
||||
}
|
||||
|
||||
if [ -f configure -a -f makefile.in -a -d src ]
|
||||
then
|
||||
root="`pwd`"
|
||||
build_in_default_build_sub_directory
|
||||
elif [ -f ../configure -a -f ../makefile.in -a -d ../src ]
|
||||
then
|
||||
cwd="`pwd`"
|
||||
build=`basename "$cwd"`
|
||||
root=`dirname "$cwd"`
|
||||
case x"$build" in
|
||||
xsrc|xtest|xscripts)
|
||||
cd ..
|
||||
build_in_default_build_sub_directory
|
||||
;;
|
||||
*)
|
||||
msg "building in ${HILITE}'$build'${NORMAL} sub-directory"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
die "call 'configure' from root of CaDiCaL source or a sub-directory"
|
||||
fi
|
||||
|
||||
msg "root directory '$root'"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
src="$root/src"
|
||||
|
||||
if [ -d "$src" ]
|
||||
then
|
||||
msg "source directory '$src'"
|
||||
else
|
||||
die "could not find source director '$src'"
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Prepare '@CXX@' and '@CXXFLAGS@' parameters for 'makefile.in'
|
||||
|
||||
[ x"$CXX" = x ] && CXX=g++
|
||||
[ x"$CXXFLAGS" = x ] || CXXFLAGS="$CXXFLAGS "
|
||||
|
||||
case x"$CXX" in
|
||||
x*g++*|x*clang++*) CXXFLAGS="${CXXFLAGS}-Wall -Wextra";;
|
||||
*) CXXFLAGS="${CXXFLAGS}-W";;
|
||||
esac
|
||||
|
||||
if [ $debug = yes ]
|
||||
then
|
||||
CXXFLAGS="$CXXFLAGS -g"
|
||||
else
|
||||
case x"$CXX" in
|
||||
x*g++*|x*clang++*) CXXFLAGS="$CXXFLAGS -O3";;
|
||||
*) CXXFLAGS="$CXXFLAGS -O";;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ $m32 = yes ]
|
||||
then
|
||||
case x"$CXX" in
|
||||
x*g++*)
|
||||
options="$options -mpc64"
|
||||
msg "forcing portable 64-bit FPU mode ('-mpc64') for '$CXX'"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ $competition = yes ]
|
||||
then
|
||||
quiet=yes
|
||||
contracts=no
|
||||
tracing=no
|
||||
fi
|
||||
|
||||
[ $check = no ] && CXXFLAGS="$CXXFLAGS -DNDEBUG"
|
||||
[ $logging = yes ] && CXXFLAGS="$CXXFLAGS -DLOGGING"
|
||||
[ $quiet = yes ] && CXXFLAGS="$CXXFLAGS -DQUIET"
|
||||
[ $profile = yes ] && CXXFLAGS="$CXXFLAGS -pg"
|
||||
[ $coverage = yes ] && CXXFLAGS="$CXXFLAGS -ftest-coverage -fprofile-arcs"
|
||||
if [ $pedantic = yes ]
|
||||
then
|
||||
CXXFLAGS="$CXXFLAGS --pedantic -Werror -std=c++11"
|
||||
case x"$CXX" in
|
||||
x*g++*|x*clang++*) CXXFLAGS="${CXXFLAGS} -Wp,-D_GLIBCXX_ASSERTIONS"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
[ $contracts = no ] && CXXFLAGS="$CXXFLAGS -DNCONTRACTS"
|
||||
[ $tracing = no ] && CXXFLAGS="$CXXFLAGS -DNTRACING"
|
||||
|
||||
CXXFLAGS="$CXXFLAGS$options"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
case x"$CXX" in
|
||||
x*g++* | x*clang++*) WERROR="-Werror -pedantic";;
|
||||
*) WERROR="";;
|
||||
esac
|
||||
|
||||
# Check that compilation flags work.
|
||||
|
||||
feature=./configure-hello-world
|
||||
cat <<EOF > $feature.cpp
|
||||
#include <iostream>
|
||||
int main () { std::cout << "hello world" << std::endl; }
|
||||
EOF
|
||||
if $CXX $CXXFLAGS $WERROR -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if [ ! "`$feature.exe 2>>configure.log|tr -d '\r'`" = "hello world" ]
|
||||
then
|
||||
die "execution of '$feature.exe' failed"
|
||||
fi
|
||||
else
|
||||
die "test compilation '$feature.cpp'"
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Since C99/C++11 is becoming the standard newer versions of 'g++' (7.3 for
|
||||
# instance) discourage certain GCC extensions, particularly the GCC version
|
||||
# of variadic macros, if the same concept exists in the standard. This
|
||||
# forced us to replace all GCC style 'ARGS...' macros with '...' and
|
||||
# '__VA_ARGS__'. Otherwise compiling the library with '--pedantic -Werror'
|
||||
# would fail (configuration flag '-p'). However, older versions of 'gcc'
|
||||
# (such as 4.8) would disallow these new forms of macros unless we
|
||||
# explicitly enforce the new standard with '-std=c++11'. Here we try to
|
||||
# figure out whether we need that flag. In earlier versions we used
|
||||
# '-std=c++0x' but due to issues with older MacOS builds we switched to
|
||||
# '-std=c++11' instead.
|
||||
|
||||
feature=./configure-requires-c++11
|
||||
cat <<EOF > $feature.cpp
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
// old variadic macro usage 'ARGS...' / '#ARGS' discouraged in g++-7...
|
||||
// new variadic macro usage '...' / '__VA_ARGS__' available in C99/C++11
|
||||
//
|
||||
#define MACRO(FMT, ...) printf (FMT "\n", __VA_ARGS__)
|
||||
|
||||
// we use ranged for loops which became available in gcc 4.6 and for
|
||||
// the gcc 4.6 as well as 4.8 requires '-std=c++11' too.
|
||||
//
|
||||
unsigned f (const std::vector<unsigned> & a) {
|
||||
unsigned res = 0;
|
||||
for (auto i : a) res += i;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int main () { MACRO ("%d", 42); return 0; }
|
||||
EOF
|
||||
if $CXX $CXXFLAGS $WERROR -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if [ "`$feature.exe 2>>configure.log|tr -d '\r'`" = 42 ]
|
||||
then
|
||||
msg "compiler supports all required C99/C++11 extensions"
|
||||
else
|
||||
die "checking compilation without '-std=c++11' failed"
|
||||
fi
|
||||
else
|
||||
CXXFLAGS="$CXXFLAGS -std=c++11"
|
||||
if $CXX $CXXFLAGS -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if [ "`$feature.exe 2>>configure.log|tr -d '\r'`" = 42 ]
|
||||
then
|
||||
msg "using '-std=c++11' for all required C99/C++11 extensions"
|
||||
else
|
||||
die "checking compilation with '-std=c++11' failed"
|
||||
fi
|
||||
else
|
||||
die "compiler does not support C99/C++11 even with '-std=c++11'"
|
||||
fi
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Unlocked IO is much faster but not necessarily supported.
|
||||
|
||||
if [ $unlocked = yes ]
|
||||
then
|
||||
feature=./configure-have-unlocked-io
|
||||
cat <<EOF > $feature.cpp
|
||||
#include <cstdio>
|
||||
int main () {
|
||||
const char * path = "$feature.log";
|
||||
FILE * file = fopen (path, "w");
|
||||
if (!file) return 1;
|
||||
if (putc_unlocked (42, file) != 42) return 1;
|
||||
if (fclose (file)) return 1;
|
||||
file = fopen (path, "r");
|
||||
if (!file) return 1;
|
||||
if (getc_unlocked (file) != 42) return 1;
|
||||
if (fclose (file)) return 1;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if $CXX $CXXFLAGS -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if $feature.exe
|
||||
then
|
||||
msg "unlocked IO with '{putc,getc}_unlocked' seems to work"
|
||||
else
|
||||
msg "not using unlocked IO (running '$feature.exe' failed)"
|
||||
unlocked=no
|
||||
fi
|
||||
else
|
||||
msg "not using unlocked IO (failed to compile '$feature.cpp')"
|
||||
unlocked=no
|
||||
fi
|
||||
else
|
||||
msg "not using unlocked IO (since '--no-unlocked' specified)"
|
||||
fi
|
||||
|
||||
[ $unlocked = no ] && CXXFLAGS="$CXXFLAGS -DNUNLOCKED"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Instantiate '../makefile.in' template to produce 'makefile' in 'build'.
|
||||
|
||||
msg "compiling with ${HILITE}'$CXX $CXXFLAGS'${NORMAL}"
|
||||
|
||||
rm -f makefile
|
||||
sed \
|
||||
-e "2c\\
|
||||
# This 'makefile' is generated from '../makefile.in'." \
|
||||
-e "s,@CXX@,$CXX," \
|
||||
-e "s#@CXXFLAGS@#$CXXFLAGS#" \
|
||||
../makefile.in > makefile
|
||||
|
||||
msg "generated '$build/makefile' from '../makefile.in'"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
build="`pwd`"
|
||||
makefile="`dirname "$build"`/makefile"
|
||||
cat <<EOF > "$makefile"
|
||||
CADICALBUILD=$build
|
||||
all:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)"
|
||||
clean:
|
||||
@if [ -d "\$(CADICALBUILD)" ]; \\
|
||||
then \\
|
||||
if [ -f "\$(CADICALBUILD)"/makefile ]; \\
|
||||
then \\
|
||||
touch "\$(CADICALBUILD)"/build.hpp; \\
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" clean; \\
|
||||
fi; \\
|
||||
rm -rf "\$(CADICALBUILD)"; \\
|
||||
fi
|
||||
rm -f "$src/makefile"
|
||||
rm -f "$makefile"
|
||||
test:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" test
|
||||
cadical:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" cadical
|
||||
mobical:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" mobical
|
||||
update:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" update
|
||||
.PHONY: all cadical clean mobical test update
|
||||
EOF
|
||||
|
||||
msg "generated '../makefile' as proxy to ..."
|
||||
msg "... '$build/makefile'"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
if [ -f $src/makefile ]
|
||||
then
|
||||
msg "removing '$src/makefile'"
|
||||
rm -f $src/makefile
|
||||
fi
|
||||
|
||||
msg "linking '$root/makefile'"
|
||||
ln -s $root/makefile $src/makefile
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
msg "now run ${HILITE}'make'${NORMAL} to compile CaDiCaL"
|
||||
msg "optionally run 'make test'"
|
24
hCaD_V2/makefile
Normal file
24
hCaD_V2/makefile
Normal file
@ -0,0 +1,24 @@
|
||||
CADICALBUILD=/home/chenzh/experiments/cec/equal/hCaD_V2/build
|
||||
all:
|
||||
$(MAKE) -C "$(CADICALBUILD)"
|
||||
clean:
|
||||
@if [ -d "$(CADICALBUILD)" ]; \
|
||||
then \
|
||||
if [ -f "$(CADICALBUILD)"/makefile ]; \
|
||||
then \
|
||||
touch "$(CADICALBUILD)"/build.hpp; \
|
||||
$(MAKE) -C "$(CADICALBUILD)" clean; \
|
||||
fi; \
|
||||
rm -rf "$(CADICALBUILD)"; \
|
||||
fi
|
||||
rm -f "/home/chenzh/experiments/cec/equal/hCaD_V2/src/makefile"
|
||||
rm -f "/home/chenzh/experiments/cec/equal/hCaD_V2/makefile"
|
||||
test:
|
||||
$(MAKE) -C "$(CADICALBUILD)" test
|
||||
cadical:
|
||||
$(MAKE) -C "$(CADICALBUILD)" cadical
|
||||
mobical:
|
||||
$(MAKE) -C "$(CADICALBUILD)" mobical
|
||||
update:
|
||||
$(MAKE) -C "$(CADICALBUILD)" update
|
||||
.PHONY: all cadical clean mobical test update
|
84
hCaD_V2/makefile.in
Normal file
84
hCaD_V2/makefile.in
Normal file
@ -0,0 +1,84 @@
|
||||
#==========================================================================#
|
||||
# This is a 'makefile.in' template with '@CXX@' and '@CXXFLAGS@' parameters.
|
||||
# This makefile requires GNU make.
|
||||
#==========================================================================#
|
||||
|
||||
# The '../scripts/make-build-header.sh' script searches for the next two
|
||||
# lines to figure out the compiler and compilation flags. This information
|
||||
# is then used to generate corresponding macros in 'build.hpp'.
|
||||
|
||||
CXX=@CXX@
|
||||
CXXFLAGS=@CXXFLAGS@
|
||||
|
||||
############################################################################
|
||||
# It is usually not necessary to change anything below this line! #
|
||||
############################################################################
|
||||
|
||||
APP=cadical.cpp mobical.cpp
|
||||
SRC=$(sort $(wildcard ../src/*.cpp))
|
||||
SUB=$(subst ../src/,,$(SRC))
|
||||
LIB=$(filter-out $(APP),$(SUB))
|
||||
OBJ=$(LIB:.cpp=.o)
|
||||
DIR=../$(shell pwd|sed -e 's,.*/,,')
|
||||
COMPILE=$(CXX) $(CXXFLAGS) -I$(DIR)
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
all: libcadical.a cadical mobical
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
.SUFFIXES: .cpp .o
|
||||
|
||||
%.o: ../src/%.cpp ../src/*.hpp makefile
|
||||
$(COMPILE) -c $<
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Application binaries (the stand alone solver 'cadical' and the model based
|
||||
# tester 'mobical') and the library are the main build targets.
|
||||
|
||||
cadical: cadical.o libcadical.a makefile
|
||||
$(COMPILE) -o $@ $< -L. -lcadical
|
||||
|
||||
mobical: mobical.o libcadical.a makefile
|
||||
$(COMPILE) -o $@ $< -L. -lcadical
|
||||
|
||||
libcadical.a: $(OBJ) makefile
|
||||
ar rc $@ $(OBJ)
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Note that 'build.hpp' is generated and resides in the build directory.
|
||||
|
||||
build.hpp: always
|
||||
../scripts/make-build-header.sh > build.hpp
|
||||
|
||||
version.o: build.hpp
|
||||
|
||||
update:
|
||||
../scripts/update-version.sh
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# These two 'C' interfaces include '.h' headers and thus require explicitly
|
||||
# defined additional dependencies.
|
||||
|
||||
ccadical.o: ../src/ccadical.h
|
||||
ipasir.o: ../src/ipasir.h ../src/ccadical.h
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
analyze: all
|
||||
$(COMPILE) --analyze ../src/*.cpp
|
||||
|
||||
clean:
|
||||
rm -f *.o *.a cadical mobical makefile build.hpp
|
||||
rm -f *.gcda *.gcno *.gcov gmon.out
|
||||
|
||||
test: all
|
||||
CADICALBUILD="$(DIR)" $(MAKE) -j1 -C ../test
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
.PHONY: all always analyze clean test update
|
42
hCaD_V2/scripts/README.md
Normal file
42
hCaD_V2/scripts/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
# CaDiCaL Scripts
|
||||
|
||||
Scripts needed for the build process
|
||||
|
||||
./make-build-header.sh # generates 'build.hpp'
|
||||
./get-git-id.sh # get GIT id (needed by 'make-build-header.sh')
|
||||
./update-version.sh # synchronizes VERSION in '../src/version.cpp'
|
||||
|
||||
and a script which builds and tests all configurations
|
||||
|
||||
./build-and-test-all-configurations.sh
|
||||
CXX=clang++ ./build-and-test-all-configurations.sh
|
||||
CXX=g++-4.8 ./build-and-test-all-configurations.sh
|
||||
|
||||
where as the code shows the compiler (default `g++`) is specified through
|
||||
the environment variable `CXX` (as for `../configure`). Then there are
|
||||
scripts for producing a source release
|
||||
|
||||
./make-src-release.sh # archive as 'cadical-VERSION-GITID.tar.xz'
|
||||
./prepare-sc2021-submission.sh # star-exec format for SAT competition
|
||||
|
||||
and scripts for testing and debugging
|
||||
|
||||
./generate-embedded-options-default-list.sh # in 'c --opt=val' format
|
||||
./generate-options-range-list.sh # 'cnfuzz' option file format
|
||||
./run-cadical-and-check-proof.sh # wrapper to check proofs too
|
||||
./run-simplifier-and-extend-solution.sh # to check simplifier
|
||||
./extend-solution.sh # called by previous script
|
||||
|
||||
a script to check whether all options are actually used
|
||||
|
||||
./check-options-occur.sh
|
||||
|
||||
a script to update the example in the `../src/cadical.hpp` header
|
||||
|
||||
./update-example-in-cadical-header-file.sh
|
||||
|
||||
and finally a script to normalize white space of the source code
|
||||
|
||||
./normalize-white-space.sh
|
||||
|
||||
The `color.sh` script is used by the `run.sh` scripts in `test`.
|
115
hCaD_V2/scripts/build-and-test-all-configurations.sh
Executable file
115
hCaD_V2/scripts/build-and-test-all-configurations.sh
Executable file
@ -0,0 +1,115 @@
|
||||
#!/bin/sh
|
||||
|
||||
. `dirname $0`/colors.sh || exit 1
|
||||
|
||||
############################################################################
|
||||
|
||||
die () {
|
||||
echo "build-and-test-all-configurations.sh: ${BAD}error${NORMAL}: $*" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
############################################################################
|
||||
|
||||
if [ -f configure ]
|
||||
then
|
||||
configure="./configure"
|
||||
makeoptions=""
|
||||
elif [ -f ../configure ]
|
||||
then
|
||||
configure="../configure"
|
||||
makeoptions=" -C .."
|
||||
else
|
||||
die "Can not find 'configure'."
|
||||
fi
|
||||
|
||||
if [ "$CXX" = "" ]
|
||||
then
|
||||
environment=""
|
||||
else
|
||||
environment="CXX=$CXX "
|
||||
fi
|
||||
|
||||
if [ ! "$CXXFLAGS" = "" ]
|
||||
then
|
||||
[ "$environment" = "" ] || environment="$environment "
|
||||
environment="${environment}CXXFLAGS=\"$CXXFLAGS\" "
|
||||
fi
|
||||
|
||||
############################################################################
|
||||
|
||||
ok=0
|
||||
|
||||
run () {
|
||||
if [ "$*" = "" ]
|
||||
then
|
||||
configureoptions=""
|
||||
description="<empty>"
|
||||
else
|
||||
configureoptions=" $*"
|
||||
description="$*"
|
||||
fi
|
||||
echo "$environment$configure$configureoptions && make$makeoptions test"
|
||||
$configure$configureoptions $* >/dev/null 2>&1 && \
|
||||
make$makeoptions test >/dev/null 2>&1
|
||||
test $? = 0 || die "Configuration \`$description' failed."
|
||||
make$makeoptions clean >/dev/null 2>&1
|
||||
test $? = 0 || die "Cleaning up for \`$description' failed."
|
||||
ok=`expr $ok + 1`
|
||||
}
|
||||
|
||||
############################################################################
|
||||
|
||||
# start with these two for fast fail
|
||||
|
||||
run # default configuration (depends on 'MAKEFLAGS'!)
|
||||
run -p # then check default pedantic first
|
||||
|
||||
run -q # library users might want to disable messages
|
||||
run -q -p # also check '--quiet' pedantically
|
||||
|
||||
# now start with the five single options
|
||||
|
||||
run -a # actually enables all the four next options below
|
||||
run -c
|
||||
run -g
|
||||
run -l
|
||||
|
||||
# all five single options pedantically
|
||||
|
||||
run -a -p
|
||||
run -c -p
|
||||
run -g -p
|
||||
run -l -p
|
||||
|
||||
# all legal pairs of single options
|
||||
# ('-a' can not be combined with any of the other options)
|
||||
# ('-g' can not be combined '-c')
|
||||
|
||||
run -c -l
|
||||
run -c -q
|
||||
run -g -l
|
||||
run -g -q
|
||||
|
||||
# the same pairs but now with pedantic compilation
|
||||
|
||||
run -c -l -p
|
||||
run -c -q -p
|
||||
run -g -l -p
|
||||
run -g -q -p
|
||||
|
||||
# finally check that these also work to some extend
|
||||
|
||||
run -m32 -q
|
||||
run -m32 -a -p
|
||||
|
||||
run --no-unlocked -q
|
||||
run --no-unlocked -a -p
|
||||
|
||||
run --no-contracts -q
|
||||
run --no-contracts -a -p
|
||||
|
||||
run --no-tracing -q
|
||||
run --no-tracing -a -p
|
||||
|
||||
echo "successfully compiled and tested ${GOOD}${ok}${NORMAL} configurations"
|
11
hCaD_V2/scripts/check-options-occur.sh
Executable file
11
hCaD_V2/scripts/check-options-occur.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
sed \
|
||||
-e '/^[OLQ[A-Z]*(/!d' \
|
||||
-e 's,^[OLQ[A-Z]*(,,' \
|
||||
-e 's/,.*//' \
|
||||
../src/options.hpp | \
|
||||
while read option
|
||||
do
|
||||
grep -q opts.$option ../src/*.hpp ../src/*.cpp && continue
|
||||
echo "option '$option' not found"
|
||||
done
|
29
hCaD_V2/scripts/colors.sh
Executable file
29
hCaD_V2/scripts/colors.sh
Executable file
@ -0,0 +1,29 @@
|
||||
# Use colors if '<stdin>' is connected to a terminal.
|
||||
|
||||
color_echo_options=""
|
||||
|
||||
if [ -t 1 ]
|
||||
then
|
||||
BAD="\033[1;31m" # bright red
|
||||
HILITE="\033[32m" # normal green
|
||||
GOOD="\033[1;32m" # bright green
|
||||
HIDE="\033[34m" # cyan
|
||||
BOLD="\033[1m" # bold color
|
||||
NORMAL="\033[0m" # normal color
|
||||
|
||||
if [ x"`echo -e 2>/dev/null`" = x ]
|
||||
then
|
||||
color_echo_options=" -e"
|
||||
fi
|
||||
else
|
||||
BAD=""
|
||||
HILITE=""
|
||||
GOOD=""
|
||||
HIDE=""
|
||||
BOLD=""
|
||||
NORMAL=""
|
||||
fi
|
||||
|
||||
cecho () {
|
||||
echo$color_echo_options $*
|
||||
}
|
75
hCaD_V2/scripts/extend-solution.sh
Executable file
75
hCaD_V2/scripts/extend-solution.sh
Executable file
@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
name=extend-solution.sh
|
||||
scriptsdir=`dirname $0`
|
||||
colors=$scriptsdir/colors.sh
|
||||
if [ -f $colors ]
|
||||
then
|
||||
. $colors
|
||||
else
|
||||
BAD=""
|
||||
BOLD=""
|
||||
NORMAL=""
|
||||
fi
|
||||
die () {
|
||||
echo "${BOLD}$name:${NORMAL} ${BAD}error:${NORMAL} $*" 1>&2
|
||||
exit 1
|
||||
}
|
||||
[ $# -eq 2 ] || \
|
||||
die "expected exactly two arguments"
|
||||
[ -f $1 ] || \
|
||||
die "argument '$1' not a file"
|
||||
[ -f $2 ] || \
|
||||
die "argument '$2' not a file"
|
||||
status="`grep '^s' $1|head -1`"
|
||||
case x"$status" in
|
||||
x"s SATISFIABLE") ;;
|
||||
x"s UNSATISFIABLE") echo "s UNSATISFIABLE"; exit 20;;
|
||||
*) die "not valid 's ...' line found in '$1'";;
|
||||
esac
|
||||
(
|
||||
grep '^v' $1;
|
||||
awk '{
|
||||
printf "c"
|
||||
for (i = 1; $i; i++)
|
||||
printf " %d", $i
|
||||
printf "\nw";
|
||||
for (i++; $i; i++)
|
||||
printf " %d", $i
|
||||
printf "\n"
|
||||
}' $2
|
||||
) | \
|
||||
awk '
|
||||
function abs (i) { return i < 0 ? -i : i }
|
||||
/^v/{
|
||||
for (i = 2; i <= NF; i++) {
|
||||
lit = $i;
|
||||
idx = abs(lit);
|
||||
if (idx) a[idx] = lit;
|
||||
}
|
||||
}
|
||||
/^c/{
|
||||
satisfied = 0
|
||||
for (i = 2; i <= NF; i++) {
|
||||
lit = $i;
|
||||
idx = abs(lit);
|
||||
tmp = a[idx];
|
||||
if (lit < 0) tmp = -tmp;
|
||||
if (tmp > 0) satisfied = 1;
|
||||
}
|
||||
}
|
||||
/^w/{
|
||||
if (satisfied) next
|
||||
for (i = 2; i <= NF; i++) {
|
||||
lit = $i;
|
||||
idx = abs(lit);
|
||||
a[idx] = -a[idx];
|
||||
}
|
||||
}
|
||||
END {
|
||||
print "s SATISFIABLE"
|
||||
for (i in a)
|
||||
print "v", a[i]
|
||||
print "v 0"
|
||||
}
|
||||
'
|
||||
exit 10
|
37
hCaD_V2/scripts/generate-cubes.sh
Executable file
37
hCaD_V2/scripts/generate-cubes.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
usage () {
|
||||
cat <<EOF 2>&1
|
||||
usage: generate-cubes.sh [-h] <input> [ <output> [ <march_cu option> ... ] ]
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
[ "$1" = -h ] && usage
|
||||
die () {
|
||||
echo "generate-cubes.sh: error: $*" 1>&2
|
||||
exit 1
|
||||
}
|
||||
[ $# -lt 1 ] && die "expected DIMACS file argument"
|
||||
output=""
|
||||
options=""
|
||||
[ -f "$1" ] || die "first argument is not a DIMACS file"
|
||||
input="$1"
|
||||
shift
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
case "$1" in
|
||||
-*) options="$options $1";;
|
||||
*) exec 1>"$1";;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
prefix=/tmp/generate-cubes-$$
|
||||
cleanup () {
|
||||
rm -f $prefix*
|
||||
}
|
||||
trap "cleanup" 2 11
|
||||
cubes=$prefix.cubes
|
||||
march_cu $input -o $cubes $options 1>&2 || exit 1
|
||||
sed -e 's,^p cnf.*,p inccnf,' $input
|
||||
cat $cubes
|
||||
cleanup
|
||||
exit 0
|
13
hCaD_V2/scripts/generate-embedded-options-default-list.sh
Executable file
13
hCaD_V2/scripts/generate-embedded-options-default-list.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
cd `dirname $0`
|
||||
grep 'OPTION( ' ../src/options.hpp | \
|
||||
sed -e 's,^OPTION( ,,' \
|
||||
-e 's/,/ /' \
|
||||
-e 's/,.*//g' | \
|
||||
awk '{printf "c --%s=%d\n", $1, $2}' | \
|
||||
grep -v 'check' | \
|
||||
grep -v 'clim' | \
|
||||
grep -v 'dlim' | \
|
||||
grep -v 'extend' | \
|
||||
grep -v 'learn' | \
|
||||
grep -v 'witness'
|
18
hCaD_V2/scripts/generate-options-range-list.sh
Executable file
18
hCaD_V2/scripts/generate-options-range-list.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
cd `dirname $0`
|
||||
egrep '^(OPTION|BCEOPT|SHROPT)\( ' ../src/options.hpp | \
|
||||
grep -v '\<double,' | \
|
||||
sed -e 's,^[^(]*(,,' \
|
||||
-e 's/\<int,//' \
|
||||
-e 's/\<bool,//' \
|
||||
-e 's,".*,,' \
|
||||
-e 's/,[^,]*$//' \
|
||||
-e 's,/\*[^\*]*\*/,,' \
|
||||
-e 's/,/ /g' | \
|
||||
awk '{printf "%s %d %d %d\n", $1, $2, $3, $4}' | \
|
||||
grep -v 'check' | \
|
||||
grep -v 'clim' | \
|
||||
grep -v 'dlim' | \
|
||||
grep -v 'extend' | \
|
||||
grep -v 'learn' | \
|
||||
grep -v 'witness'
|
3
hCaD_V2/scripts/get-git-id.sh
Executable file
3
hCaD_V2/scripts/get-git-id.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
# print current SHA1 git id which uniquely identifies the source code
|
||||
git show 2>/dev/null |awk '{print $2;exit}'
|
92
hCaD_V2/scripts/make-build-header.sh
Executable file
92
hCaD_V2/scripts/make-build-header.sh
Executable file
@ -0,0 +1,92 @@
|
||||
#!/bin/sh
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Used to generate 'build.hpp', which in turn is used in 'version.cpp' to
|
||||
# print compile time options, compiler version, and source code version.
|
||||
# If generating 'build.hpp' fails you can still compile 'version.cpp' by
|
||||
# using the '-DNBUILD' compile time option.
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
die () {
|
||||
echo "make-build-header.sh: error: $*" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
warning () {
|
||||
echo "make-build-header.sh: warning: $*" 1>&2
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
[ ! -f VERSION -a ! -f ../VERSION ] && \
|
||||
die "needs to be called from build sub-directory"
|
||||
|
||||
[ -f makefile ] || \
|
||||
warning "could not find 'makefile'"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# The version.
|
||||
#
|
||||
VERSION="`cat ../VERSION`"
|
||||
if [ x"$VERSION" = x ]
|
||||
then
|
||||
warning "could not determine 'VERSION'"
|
||||
else
|
||||
echo "#define VERSION \"$VERSION\""
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# The unique GIT hash.
|
||||
#
|
||||
IDENTIFIER="`../scripts/get-git-id.sh`"
|
||||
if [ x"$IDENTIFIER" = x ]
|
||||
then
|
||||
warning "could not determine 'IDENTIFIER' (git id)"
|
||||
else
|
||||
echo "#define IDENTIFIER \"$IDENTIFIER\""
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# C++ compiler 'CXX' used in 'makefile'.
|
||||
#
|
||||
COMPILER="`sed -e '/^CXX=/!d' -e 's,^CXX=,,' makefile 2>/dev/null`"
|
||||
case x"$COMPILER" in
|
||||
x*g++* | x*clang++*)
|
||||
COMPILER="`$COMPILER --version 2>/dev/null|head -1`";;
|
||||
*) COMPILER="";;
|
||||
esac
|
||||
if [ x"$COMPILER" = x ]
|
||||
then
|
||||
warning "could not determine 'COMPILER' ('CXX')"
|
||||
else
|
||||
echo "#define COMPILER \"$COMPILER\""
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# C++ compiler flags 'CXXFLAGS' used in 'makefile'.
|
||||
#
|
||||
FLAGS="`sed -e '/^CXXFLAGS=/!d' -e 's,^CXXFLAGS=,,' makefile 2>/dev/null`"
|
||||
if [ x"$FLAGS" = x ]
|
||||
then
|
||||
warning "could not determine 'FLAGS' ('CXXFLAGS')"
|
||||
else
|
||||
echo "#define FLAGS \"$FLAGS\""
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
# Use time of executing this script at build time.
|
||||
#
|
||||
LC_TIME="en_US" # Avoid umlaut in 'DATE'.
|
||||
export LC_TIME
|
||||
# The time and date we compiled the CaDiCaL library.
|
||||
DATE="`date 2>/dev/null|sed -e 's, *, ,g'`"
|
||||
OS="`uname -srmn 2>/dev/null`"
|
||||
DATE="`echo $DATE $OS|sed -e 's,^ *,,' -e 's, *$,,'`"
|
||||
if [ x"$DATE" = x" " ]
|
||||
then
|
||||
warning "could not determine 'DATE' (build date and time)"
|
||||
else
|
||||
echo "#define DATE \"$DATE\""
|
||||
fi
|
36
hCaD_V2/scripts/make-src-release.sh
Executable file
36
hCaD_V2/scripts/make-src-release.sh
Executable file
@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
die () {
|
||||
echo "make-src-release.sh: error: $*" 1>&2
|
||||
exit 1
|
||||
}
|
||||
cd `dirname $0`/..
|
||||
[ -d .git ] || \
|
||||
die "does not seem to be under 'git' control ('.git' not found)"
|
||||
version="`awk '/define VERSION/{print $4}' src/version.cpp|sed -e 's,",,g'`"
|
||||
VERSION="`cat VERSION`"
|
||||
[ "$version" = "$VERSION" ] || \
|
||||
die "versions '$version' in 'src/version.cpp' and " \
|
||||
"'$VERSION' in 'VERSION' do not match (run 'scripts/update-version.sh')"
|
||||
fullgitid="`./scripts/get-git-id.sh`"
|
||||
gitid="`echo $fullgitid|sed -e 's,^\(.......\).*,\1,'`"
|
||||
branch=`git branch|grep '^\*'|head -1|awk '{print $2}'`
|
||||
[ "$branch" = "" ] && die "could not get branch from 'git'"
|
||||
name=cadical-${version}-${gitid}
|
||||
[ "$branch" = "master" ] || name="$name-$branch"
|
||||
dir=/tmp/$name
|
||||
tar=/tmp/$name.tar.xz
|
||||
rm -rf $dir
|
||||
mkdir $dir || exit 1
|
||||
git archive $branch | tar -x -C $dir
|
||||
cat >$dir/scripts/get-git-id.sh <<EOF
|
||||
#!/bin/sh
|
||||
echo "$fullgitid"
|
||||
EOF
|
||||
sed -i -e "s,IDENTIFIER 0$,IDENTIFIER \"$fullgitid\"," $dir/src/version.cpp
|
||||
chmod 755 $dir/scripts/*.sh
|
||||
cd /tmp
|
||||
rm -rf /tmp/$name/.git
|
||||
tar cJf $tar $name
|
||||
bytes="`ls --block-size=1 -s $tar 2>/dev/null |awk '{print $1}'`"
|
||||
echo "generated '$tar' of $bytes bytes"
|
||||
rm -rf $dir
|
8
hCaD_V2/scripts/normalize-white-space.sh
Executable file
8
hCaD_V2/scripts/normalize-white-space.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
cd `dirname $0`/../src
|
||||
for i in *.cpp *.hpp
|
||||
do
|
||||
cp $i $i~ || exit 1
|
||||
expand $i~ |sed -e 's, *$,,'|uniq > $i
|
||||
rm -f $i~
|
||||
done
|
52
hCaD_V2/scripts/prepare-sc2021-submission.sh
Executable file
52
hCaD_V2/scripts/prepare-sc2021-submission.sh
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
cd `dirname $0`/..
|
||||
root=`pwd`
|
||||
tmp=/tmp/prepare-cadical-sc2021-submission.log
|
||||
VERSION=`cat VERSION`
|
||||
rm -f $tmp
|
||||
##########################################################################
|
||||
cd $root
|
||||
./scripts/make-src-release.sh >$tmp || exit 1
|
||||
tar=`awk '{print $2}' $tmp |sed -e "s,',,g"`
|
||||
##########################################################################
|
||||
cd $root
|
||||
base=cadical-${VERSION}-starexec
|
||||
dir=/tmp/$base
|
||||
rm -rf $dir
|
||||
mkdir $dir
|
||||
mkdir $dir/bin
|
||||
mkdir $dir/build
|
||||
mkdir $dir/archives
|
||||
cp -a $tar $dir/archives
|
||||
cat <<EOF >$dir/build/build.sh
|
||||
#!/bin/sh
|
||||
tar xf ../archives/cadical*
|
||||
mv cadical* cadical
|
||||
cd cadical
|
||||
./configure --competition
|
||||
make test
|
||||
install -s build/cadical ../../bin/
|
||||
EOF
|
||||
chmod 755 $dir/build/build.sh
|
||||
cat <<EOF >$dir/starexec_build
|
||||
#!/bin/sh
|
||||
cd build
|
||||
exec ./build.sh
|
||||
EOF
|
||||
chmod 755 $dir/starexec_build
|
||||
cat <<EOF >$dir/bin/starexec_run_default
|
||||
#!/bin/sh
|
||||
exec ./cadical \$1 \$2/proof.out
|
||||
EOF
|
||||
chmod 755 $dir/bin/starexec_run_default
|
||||
description=$dir/starexec_description.txt
|
||||
grep '^CaDiCaL' README.md|head -1 >$description
|
||||
cat $description
|
||||
archive=/tmp/$base.zip
|
||||
rm -f $archive
|
||||
cd $dir
|
||||
zip -r $archive .
|
||||
cd /tmp/
|
||||
ls -l $archive
|
||||
rm -f $tmp
|
||||
rm -rf $dir/
|
58
hCaD_V2/scripts/run-cadical-and-check-proof.sh
Executable file
58
hCaD_V2/scripts/run-cadical-and-check-proof.sh
Executable file
@ -0,0 +1,58 @@
|
||||
#!/bin/sh
|
||||
scriptsdir=`dirname $0`
|
||||
bindir=$scriptsdir/../build
|
||||
solver=$bindir/cadical
|
||||
solutionchecker=$bindir/precochk
|
||||
proofchecker=$bindir/drat-trim
|
||||
name=run-cadical-and-check-proof.sh
|
||||
colors=$scriptsdir/colors.sh
|
||||
if [ -f $colors ]
|
||||
then
|
||||
. $colors
|
||||
else
|
||||
BAD=""
|
||||
BOLD=""
|
||||
HIDE=""
|
||||
NORMAL=""
|
||||
fi
|
||||
die () {
|
||||
echo "$name: error: $*" 1>&2
|
||||
exit 1
|
||||
}
|
||||
msg () {
|
||||
echo "${HIDE}c [$name] $*${NORMAL}"
|
||||
}
|
||||
[ $# -eq 1 ] || \
|
||||
die "expected exactly one argument"
|
||||
msg "input '$1'"
|
||||
[ -f $solver ] || \
|
||||
die "can not find '$solver' (build solver first)"
|
||||
msg "found '$solver'"
|
||||
[ -f $solutionchecker ] || \
|
||||
die "can not find '$solutionchecker' (run cnf tests to compile it first)"
|
||||
msg "found '$solutionchecker'"
|
||||
[ -f $proofchecker ] || \
|
||||
die "can not find '$proofchecker' (run cnf tests to compile it first)"
|
||||
msg "found '$proofchecker'"
|
||||
prefix=/tmp/run-cadical-and-check-proof-$$
|
||||
trap "rm -f $prefix*" 2 9 15
|
||||
msg "$solver $1 $prefix.proof > $prefix.log"
|
||||
$solver $1 $prefix.proof > $prefix.log
|
||||
res=$?
|
||||
msg "solver exit code '$res'"
|
||||
msg "cat $prefix.log"
|
||||
cat $prefix.log
|
||||
case $res in
|
||||
10)
|
||||
msg "$solutionchecker $1 $prefix.log"
|
||||
$solutionchecker $1 $prefix.log || exit 1
|
||||
;;
|
||||
20)
|
||||
msg "$proofchecker $1 $prefix.proof"
|
||||
$proofchecker $1 $prefix.proof || exit 1
|
||||
;;
|
||||
esac
|
||||
msg "rm -f $prefix*"
|
||||
rm -f $prefix*
|
||||
msg "exit $res"
|
||||
exit $res
|
84
hCaD_V2/scripts/run-simplifier-and-extend-solution.sh
Executable file
84
hCaD_V2/scripts/run-simplifier-and-extend-solution.sh
Executable file
@ -0,0 +1,84 @@
|
||||
#!/bin/sh
|
||||
scriptsdir=`dirname $0`
|
||||
bindir=$scriptsdir/../build
|
||||
solver=$bindir/cadical
|
||||
colors=$scriptsdir/colors.sh
|
||||
extend=$scriptsdir/extend-solution.sh
|
||||
name=run-simplifier-and-extend-solution.sh
|
||||
if [ -f $colors ]
|
||||
then
|
||||
. $colors
|
||||
else
|
||||
BAD=""
|
||||
BOLD=""
|
||||
HIDE=""
|
||||
NORMAL=""
|
||||
fi
|
||||
die () {
|
||||
echo "${BOLD}$name:${NORMAL} ${BAD}error:${NORMAL} $*" 1>&2
|
||||
exit 1
|
||||
}
|
||||
msg () {
|
||||
echo "${HIDE}c [$name] $*${NORMAL}"
|
||||
}
|
||||
input=""
|
||||
options=""
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
case $1 in
|
||||
-t) options="$1 $2"; shift;;
|
||||
*)
|
||||
if [ -f $1 ]
|
||||
then
|
||||
[ "$input" ] && die "too many inputs"
|
||||
input="$1"
|
||||
else
|
||||
die "invalid option '$1'"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
msg "input '$input'"
|
||||
msg "options '$options'"
|
||||
[ "$input" ] || die "expected at least one input argument"
|
||||
[ -f $solver ] || \
|
||||
die "can not find '$solver' (build solver first)"
|
||||
msg "found '$solver'"
|
||||
[ -f $extend ] || \
|
||||
die "can not find '$extend'"
|
||||
prefix=/tmp/run-simplifier-and-extend-solution-$$
|
||||
trap "rm -f $prefix*" 2 9 15
|
||||
out=$prefix.out
|
||||
ext=$prefix.ext
|
||||
log=$prefix.log
|
||||
msg "$solver $options -n -O1 -c 0 -o $out -e $ext $input > $log"
|
||||
$solver $options -n -O1 -c 0 -o $out -e $ext $input > $log
|
||||
res=$?
|
||||
msg "simplifier exit code '$res'"
|
||||
msg "sed -e 's,^[vs],c,' -e 's,^c,c [simplifier],' $log"
|
||||
sed -e 's,^[vs],c,' -e 's,^c,c [simplifier],' $log
|
||||
msg "$solver $options $out > $log"
|
||||
$solver $options $out > $log
|
||||
res=$?
|
||||
msg "solver exit code '$res'"
|
||||
msg "sed -e 's,^[vs],c,' -e 's,^c,c [solver],' $log"
|
||||
sed -e 's,^[vs],c,' -e 's,^c,c [solver],' $log
|
||||
case $res in
|
||||
0|10|20) ;;
|
||||
*) die "unexpected solver exit code '$res'";;
|
||||
esac
|
||||
if [ $res = 10 ]
|
||||
then
|
||||
msg "$extend $log $ext"
|
||||
$extend $log $ext
|
||||
res=$?
|
||||
msg "extender exit code $res"
|
||||
elif [ $res = 20 ]
|
||||
then
|
||||
echo "s UNSATISFIABLE"
|
||||
fi
|
||||
msg "rm -f $prefix*"
|
||||
rm -f $prefix*
|
||||
msg "exit $res"
|
||||
exit $res
|
28
hCaD_V2/scripts/update-example-in-cadical-header-file.sh
Executable file
28
hCaD_V2/scripts/update-example-in-cadical-header-file.sh
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo `dirname $0`
|
||||
cd `dirname $0`
|
||||
|
||||
pwd
|
||||
|
||||
. ./colors.sh
|
||||
|
||||
new=/tmp/cadical.hpp
|
||||
old=../src/cadical.hpp
|
||||
example=../test/api/example.cpp
|
||||
|
||||
rm -f $new
|
||||
sed -e '/solver = new CaDiCaL::Solver/,$d' $old >> $new
|
||||
sed -e '/solver = new CaDiCaL::Solver/,/delete solver/!d' $example | \
|
||||
expand | sed -e 's,^,// ,' >> $new
|
||||
sed -e '1,/delete solver/d' $old >> $new
|
||||
|
||||
echo "${HILITE}diff $old $new${NORMAL}"
|
||||
if diff $old $new
|
||||
then
|
||||
echo "${GOOD}no need to update '$old'${NORMAL}"
|
||||
else
|
||||
echo "${BAD}cp $new $old${NORMAL}"
|
||||
cp $new $old
|
||||
fi
|
||||
rm -f $new
|
12
hCaD_V2/scripts/update-version.sh
Executable file
12
hCaD_V2/scripts/update-version.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
cd `dirname $0`
|
||||
NEW="`cat ../VERSION`"
|
||||
OLD="`awk '/define VERSION/{print \$4}' ../src/version.cpp|sed -e 's,\",,g'`"
|
||||
if [ x"$OLD" = x"$NEW" ]
|
||||
then
|
||||
echo "same version '$NEW' in 'VERSION' and '../src/version.cpp'"
|
||||
else
|
||||
echo "found new version '$NEW' in '../VERSION'"
|
||||
echo "updating old version '$OLD' in '../src/version.cpp'"
|
||||
sed -i -e '/define VERSION/s,".*",'\""$NEW"\", ../src/version.cpp
|
||||
fi
|
12
hCaD_V2/src/README.md
Normal file
12
hCaD_V2/src/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
This is the source code of the library `libcadical.a` with header
|
||||
`cadical.hpp`, the stand-alone solver `cadical` (in `cadical.cpp`) and the
|
||||
model based tester `mobical` (in `mobical.app`).
|
||||
|
||||
The `configure` script and link to the `makefile` in the root directory
|
||||
can be used from within the `src` sub-directory too and then will just work
|
||||
as if used from the root directory. For instance
|
||||
|
||||
./configure && make test
|
||||
|
||||
will configure and build in `../build` the default (optimizing)
|
||||
configuration and if successful then run the test suite.
|
830
hCaD_V2/src/analyze.cpp
Normal file
830
hCaD_V2/src/analyze.cpp
Normal file
@ -0,0 +1,830 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Code for conflict analysis, i.e., to generate the first UIP clause. The
|
||||
// main function is 'analyze' below. It further uses 'minimize' to minimize
|
||||
// the first UIP clause, which is in 'minimize.cpp'. An important side
|
||||
// effect of conflict analysis is to update the decision queue by bumping
|
||||
// variables. Similarly analyzed clauses are bumped to mark them as active.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::learn_empty_clause () {
|
||||
assert (!unsat);
|
||||
LOG ("learned empty clause");
|
||||
external->check_learned_empty_clause ();
|
||||
if (proof) proof->add_derived_empty_clause ();
|
||||
unsat = true;
|
||||
}
|
||||
|
||||
void Internal::learn_unit_clause (int lit) {
|
||||
LOG ("learned unit clause %d", lit);
|
||||
external->check_learned_unit_clause (lit);
|
||||
if (proof) proof->add_derived_unit_clause (lit);
|
||||
mark_fixed (lit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Move bumped variables to the front of the (VMTF) decision queue. The
|
||||
// 'bumped' time stamp is updated accordingly. It is used to determine
|
||||
// whether the 'queue.assigned' pointer has to be moved in 'unassign'.
|
||||
|
||||
void Internal::bump_queue (int lit) {
|
||||
assert (opts.bump);
|
||||
const int idx = vidx (lit);
|
||||
if (!links[idx].next) return;
|
||||
queue.dequeue (links, idx);
|
||||
queue.enqueue (links, idx);
|
||||
assert (stats.bumped != INT64_MAX);
|
||||
btab[idx] = ++stats.bumped;
|
||||
LOG ("moved to front variable %d and bumped to %" PRId64 "", idx, btab[idx]);
|
||||
if (!vals[idx]) update_queue_unassigned (idx);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// It would be better to use 'isinf' but there are some historical issues
|
||||
// with this function. On some platforms it is a macro and even for C++ it
|
||||
// changed the scope (in pre 5.0 gcc) from '::isinf' to 'std::isinf'. I do
|
||||
// not want to worry about these strange incompatibilities and thus use the
|
||||
// same trick as in older solvers (since the MiniSAT team invented EVSIDS)
|
||||
// and simply put a hard limit here. It is less elegant but easy to port.
|
||||
|
||||
static inline bool evsids_limit_hit (double score) {
|
||||
assert (sizeof (score) == 8); // assume IEEE 754 64-bit double
|
||||
return score > 1e150; // MAX_DOUBLE is around 1.8e308
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Classical exponential VSIDS as pioneered by MiniSAT.
|
||||
|
||||
void Internal::rescale_variable_scores () {
|
||||
stats.rescored++;
|
||||
double divider = score_inc;
|
||||
for (auto idx : vars) {
|
||||
const double tmp = stab[idx];
|
||||
if (tmp > divider) divider = tmp;
|
||||
}
|
||||
PHASE ("rescore", stats.rescored,
|
||||
"rescoring %d variable scores by 1/%g", max_var, divider);
|
||||
assert (divider > 0);
|
||||
double factor = 1.0 / divider;
|
||||
for (auto idx : vars)
|
||||
stab[idx] *= factor;
|
||||
score_inc *= factor;
|
||||
PHASE ("rescore", stats.rescored,
|
||||
"new score increment %g after %" PRId64 " conflicts",
|
||||
score_inc, stats.conflicts);
|
||||
}
|
||||
|
||||
void Internal::bump_variable_score (int lit) {
|
||||
assert (opts.bump);
|
||||
int idx = vidx (lit);
|
||||
double old_score = score (idx);
|
||||
assert (!evsids_limit_hit (old_score));
|
||||
double new_score = old_score + score_inc;
|
||||
if (evsids_limit_hit (new_score)) {
|
||||
LOG ("bumping %g score of %d hits EVSIDS score limit", old_score, idx);
|
||||
rescale_variable_scores ();
|
||||
old_score = score (idx);
|
||||
assert (!evsids_limit_hit (old_score));
|
||||
new_score = old_score + score_inc;
|
||||
}
|
||||
assert (!evsids_limit_hit (new_score));
|
||||
LOG ("new %g score of %d", new_score, idx);
|
||||
score (idx) = new_score;
|
||||
if (scores.contains (idx)) scores.update (idx);
|
||||
}
|
||||
|
||||
// Important variables recently used in conflict analysis are 'bumped',
|
||||
|
||||
void Internal::bump_variable (int lit) {
|
||||
if (use_scores ()) bump_variable_score (lit);
|
||||
else bump_queue (lit);
|
||||
}
|
||||
|
||||
// After every conflict the variable score increment is increased by a
|
||||
// factor (if we are currently using scores).
|
||||
|
||||
void Internal::bump_variable_score_inc () {
|
||||
assert (use_scores ());
|
||||
assert (!evsids_limit_hit (score_inc));
|
||||
double f = 1e3/opts.scorefactor;
|
||||
double new_score_inc = score_inc * f;
|
||||
if (evsids_limit_hit (new_score_inc)) {
|
||||
LOG ("bumping %g increment by %g hits EVSIDS score limit", score_inc, f);
|
||||
rescale_variable_scores ();
|
||||
new_score_inc = score_inc * f;
|
||||
}
|
||||
assert (!evsids_limit_hit (new_score_inc));
|
||||
LOG ("bumped score increment from %g to %g with factor %g",
|
||||
score_inc, new_score_inc, f);
|
||||
score_inc = new_score_inc;
|
||||
}
|
||||
|
||||
|
||||
void Internal::pol_dec_act() {
|
||||
|
||||
double f = 1e3/opts.scorefactor,
|
||||
nps_i = pol_sc_inc * f;
|
||||
if (nps_i > 1e150) {
|
||||
rescale_pol_scs ();
|
||||
nps_i = pol_sc_inc * f;
|
||||
}
|
||||
pol_sc_inc = nps_i;
|
||||
}
|
||||
|
||||
void Internal::rescale_pol_scs () {
|
||||
double f, m = pol_sc_inc;
|
||||
for (auto l : lits) {
|
||||
const double tmp = phases.act[vlit(l)];
|
||||
if (tmp > m) m = tmp;
|
||||
}
|
||||
|
||||
f = 1.0 / m;
|
||||
for (auto l : lits)
|
||||
phases.act[vlit(l)] *= f;
|
||||
pol_sc_inc *= f;
|
||||
}
|
||||
|
||||
void Internal::bump_pol_sc (int lt) {
|
||||
int l = vlit (lt);
|
||||
double os = phases.act[l],
|
||||
ns = os + pol_sc_inc;
|
||||
if (ns > 1e150) {
|
||||
rescale_pol_scs ();
|
||||
os = phases.act[l];
|
||||
ns = os + pol_sc_inc;
|
||||
}
|
||||
phases.act[l] = ns;
|
||||
}
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
struct analyze_bumped_rank {
|
||||
Internal * internal;
|
||||
analyze_bumped_rank (Internal * i) : internal (i) { }
|
||||
typedef uint64_t Type;
|
||||
Type operator () (const int & a) const {
|
||||
return internal->bumped (a);
|
||||
}
|
||||
};
|
||||
|
||||
struct analyze_bumped_smaller {
|
||||
Internal * internal;
|
||||
analyze_bumped_smaller (Internal * i) : internal (i) { }
|
||||
bool operator () (const int & a, const int & b) const {
|
||||
const auto s = analyze_bumped_rank (internal) (a);
|
||||
const auto t = analyze_bumped_rank (internal) (b);
|
||||
return s < t;
|
||||
}
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::bump_variables () {
|
||||
|
||||
assert (opts.bump);
|
||||
|
||||
START (bump);
|
||||
|
||||
if (opts.bumpreason) bump_also_all_reason_literals ();
|
||||
|
||||
if (!use_scores ()) {
|
||||
|
||||
// Variables are bumped in the order they are in the current decision
|
||||
// queue. This maintains relative order between bumped variables in
|
||||
// the queue and seems to work best. We also experimented with
|
||||
// focusing on variables of the last decision level, but results were
|
||||
// mixed.
|
||||
|
||||
MSORT (opts.radixsortlim,
|
||||
analyzed.begin (), analyzed.end (),
|
||||
analyze_bumped_rank (this), analyze_bumped_smaller (this));
|
||||
}
|
||||
|
||||
for (const auto & lit : analyzed)
|
||||
bump_variable (lit);
|
||||
|
||||
if (use_scores ()) bump_variable_score_inc ();
|
||||
|
||||
STOP (bump);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// We use the glue time stamp table 'gtab' for fast glue computation.
|
||||
|
||||
int Internal::recompute_glue (Clause * c) {
|
||||
int res = 0;
|
||||
const int64_t stamp = ++stats.recomputed;
|
||||
for (const auto & lit : *c) {
|
||||
int level = var (lit).level;
|
||||
assert (gtab[level] <= stamp);
|
||||
if (gtab[level] == stamp) continue;
|
||||
gtab[level] = stamp;
|
||||
res++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Clauses resolved since the last reduction are marked as 'used', their
|
||||
// glue is recomputed and they are promoted if the glue shrinks. Note that
|
||||
// promotion from 'tier3' to 'tier2' will set 'used' to '2'.
|
||||
|
||||
inline void Internal::bump_clause (Clause * c) {
|
||||
LOG (c, "bumping");
|
||||
unsigned used = c->used;
|
||||
c->used = 1;
|
||||
if (c->keep) return;
|
||||
if (c->hyper) return;
|
||||
if (!c->redundant) return;
|
||||
int new_glue = recompute_glue (c);
|
||||
if (new_glue < c->glue) promote_clause (c, new_glue);
|
||||
else if (used && c->glue <= opts.reducetier2glue) c->used = 2;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// During conflict analysis literals not seen yet either become part of the
|
||||
// first unique implication point (UIP) clause (if on lower decision level),
|
||||
// are dropped (if fixed), or are resolved away (if on the current decision
|
||||
// level and different from the first UIP). At the same time we update the
|
||||
// number of seen literals on a decision level. This helps conflict clause
|
||||
// minimization. The number of seen levels is the glucose level (also
|
||||
// called 'glue', or 'LBD').
|
||||
|
||||
inline void
|
||||
Internal::analyze_literal (int lit, int & open) {
|
||||
assert (lit);
|
||||
Flags & f = flags (lit);
|
||||
if (f.seen) return;
|
||||
Var & v = var (lit);
|
||||
if (!v.level) return;
|
||||
assert (val (lit) < 0);
|
||||
assert (v.level <= level);
|
||||
if (v.level < level) clause.push_back (lit);
|
||||
Level & l = control[v.level];
|
||||
if (!l.seen.count++) {
|
||||
LOG ("found new level %d contributing to conflict", v.level);
|
||||
levels.push_back (v.level);
|
||||
}
|
||||
if (v.trail < l.seen.trail) l.seen.trail = v.trail;
|
||||
f.seen = true;
|
||||
analyzed.push_back (lit);
|
||||
LOG ("analyzed literal %d assigned at level %d", lit, v.level);
|
||||
if (v.level == level) open++;
|
||||
}
|
||||
|
||||
inline void
|
||||
Internal::analyze_reason (int lit, Clause * reason, int & open) {
|
||||
assert (reason);
|
||||
bump_clause (reason);
|
||||
for (const auto & other : *reason)
|
||||
if (other != lit)
|
||||
analyze_literal (other, open);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is an idea which was implicit in MapleCOMSPS 2016 for 'limit = 1'.
|
||||
// See also the paragraph on 'bumping reason side literals' in their SAT'16
|
||||
// paper [LiangGaneshPoupartCzarnecki-SAT'16]. Reason side bumping was
|
||||
// performed exactly when 'LRB' based decision heuristics was used, which in
|
||||
// the original version was enabled after 10000 conflicts until a time limit
|
||||
// of 2500 seconds was reached (half of the competition time limit). The
|
||||
// Maple / Glucose / MiniSAT evolution winning the SAT race in 2019 made
|
||||
// the schedule of reason side bumping deterministic, i.e., avoiding a time
|
||||
// limit, by switching between 'LRB' and 'VSIDS' in an interval of initially
|
||||
// 30 million propagations, which then is increased geometrically by 10%.
|
||||
|
||||
inline bool Internal::bump_also_reason_literal (int lit) {
|
||||
assert (lit);
|
||||
assert (val (lit) < 0);
|
||||
Flags & f = flags (lit);
|
||||
if (f.seen) return false;
|
||||
const Var & v = var (lit);
|
||||
if (!v.level) return false;
|
||||
f.seen = true;
|
||||
analyzed.push_back (lit);
|
||||
LOG ("bumping also reason literal %d assigned at level %d", lit, v.level);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We experimented with deeper reason bumping without much success though.
|
||||
|
||||
inline void Internal::bump_also_reason_literals (int lit, int limit) {
|
||||
assert (lit);
|
||||
assert (limit > 0);
|
||||
const Var & v = var (lit);
|
||||
assert (val (lit));
|
||||
if (!v.level) return;
|
||||
Clause * reason = v.reason;
|
||||
if (!reason) return;
|
||||
for (const auto & other : *reason) {
|
||||
if (other == lit) continue;
|
||||
if (!bump_also_reason_literal (other)) continue;
|
||||
if (limit < 2) continue;
|
||||
bump_also_reason_literals (-other, limit-1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Internal::bump_also_all_reason_literals () {
|
||||
assert (opts.bumpreason);
|
||||
assert (opts.bumpreasondepth > 0);
|
||||
LOG ("bumping reasons up to depth %d", opts.bumpreasondepth);
|
||||
for (const auto & lit : clause)
|
||||
bump_also_reason_literals (-lit, opts.bumpreasondepth + stable);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::clear_analyzed_literals () {
|
||||
LOG ("clearing %zd analyzed literals", analyzed.size ());
|
||||
for (const auto & lit : analyzed) {
|
||||
Flags & f = flags (lit);
|
||||
assert (f.seen);
|
||||
f.seen = false;
|
||||
assert (!f.keep);
|
||||
assert (!f.poison);
|
||||
assert (!f.removable);
|
||||
}
|
||||
analyzed.clear ();
|
||||
}
|
||||
|
||||
void Internal::clear_analyzed_levels () {
|
||||
LOG ("clearing %zd analyzed levels", levels.size ());
|
||||
for (const auto & l : levels)
|
||||
if (l < (int) control.size ())
|
||||
control[l].reset ();
|
||||
levels.clear ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Smaller level and trail. Comparing literals on their level is necessary
|
||||
// for chronological backtracking, since trail order might in this case not
|
||||
// respect level order.
|
||||
|
||||
struct analyze_trail_negative_rank {
|
||||
Internal * internal;
|
||||
analyze_trail_negative_rank (Internal * s) : internal (s) { }
|
||||
typedef uint64_t Type;
|
||||
Type operator () (int a) {
|
||||
Var & v = internal->var (a);
|
||||
uint64_t res = v.level;
|
||||
res <<= 32;
|
||||
res |= v.trail;
|
||||
return ~res;
|
||||
}
|
||||
};
|
||||
|
||||
struct analyze_trail_larger {
|
||||
Internal * internal;
|
||||
analyze_trail_larger (Internal * s) : internal (s) { }
|
||||
bool operator () (const int & a, const int & b) const {
|
||||
return
|
||||
analyze_trail_negative_rank (internal) (a) <
|
||||
analyze_trail_negative_rank (internal) (b);
|
||||
}
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Generate new driving clause and compute jump level.
|
||||
|
||||
Clause * Internal::new_driving_clause (const int glue, int & jump) {
|
||||
|
||||
const size_t size = clause.size ();
|
||||
Clause * res;
|
||||
|
||||
if (!size) {
|
||||
|
||||
jump = 0;
|
||||
res = 0;
|
||||
|
||||
} else if (size == 1) {
|
||||
|
||||
iterating = true;
|
||||
jump = 0;
|
||||
res = 0;
|
||||
|
||||
} else {
|
||||
|
||||
assert (clause.size () > 1);
|
||||
|
||||
// We have to get the last assigned literals into the watch position.
|
||||
// Sorting all literals with respect to reverse assignment order is
|
||||
// overkill but seems to get slightly faster run-time. For 'minimize'
|
||||
// we sort the literals too heuristically along the trail order (so in
|
||||
// the opposite order) with the hope to hit the recursion limit less
|
||||
// frequently. Thus sorting effort is doubled here.
|
||||
//
|
||||
MSORT (opts.radixsortlim,
|
||||
clause.begin (), clause.end (),
|
||||
analyze_trail_negative_rank (this), analyze_trail_larger (this));
|
||||
|
||||
jump = var (clause[1]).level;
|
||||
res = new_learned_redundant_clause (glue);
|
||||
res->used = 1 + (glue <= opts.reducetier2glue);
|
||||
}
|
||||
|
||||
LOG ("jump level %d", jump);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// If chronological backtracking is enabled we need to find the actual
|
||||
// conflict level and then potentially can also reuse the conflict clause
|
||||
// as driving clause instead of deriving a redundant new driving clause
|
||||
// (forcing 'forced') if the number 'count' of literals in conflict assigned
|
||||
// at the conflict level is exactly one.
|
||||
|
||||
inline int Internal::find_conflict_level (int & forced) {
|
||||
|
||||
assert (conflict);
|
||||
assert (opts.chrono);
|
||||
|
||||
int res = 0, count = 0;
|
||||
|
||||
forced = 0;
|
||||
|
||||
for (const auto & lit : *conflict) {
|
||||
const int tmp = var (lit).level;
|
||||
if (tmp > res) {
|
||||
res = tmp;
|
||||
forced = lit;
|
||||
count = 1;
|
||||
} else if (tmp == res) {
|
||||
count++;
|
||||
if (res == level && count > 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG ("%d literals on actual conflict level %d", count, res);
|
||||
|
||||
const int size = conflict->size;
|
||||
int * lits = conflict->literals;
|
||||
|
||||
// Move the two highest level literals to the front.
|
||||
//
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
const int lit = lits[i];
|
||||
|
||||
int highest_position = i;
|
||||
int highest_literal = lit;
|
||||
int highest_level = var (highest_literal).level;
|
||||
|
||||
for (int j = i + 1; j < size; j++) {
|
||||
const int other = lits[j];
|
||||
const int tmp = var (other).level;
|
||||
if (highest_level >= tmp) continue;
|
||||
highest_literal = other;
|
||||
highest_position = j;
|
||||
highest_level = tmp;
|
||||
if (highest_level == res) break;
|
||||
}
|
||||
|
||||
// No unwatched higher assignment level literal.
|
||||
//
|
||||
if (highest_position == i) continue;
|
||||
|
||||
if (highest_position > 1)
|
||||
{
|
||||
LOG (conflict, "unwatch %d in", lit);
|
||||
remove_watch (watches (lit), conflict);
|
||||
}
|
||||
|
||||
lits[highest_position] = lit;
|
||||
lits[i] = highest_literal;
|
||||
|
||||
if (highest_position > 1)
|
||||
watch_literal (highest_literal, lits[!i], conflict);
|
||||
}
|
||||
|
||||
// Only if the number of highest level literals in the conflict is one
|
||||
// then we can reuse the conflict clause as driving clause for 'forced'.
|
||||
//
|
||||
if (count != 1) forced = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
inline int Internal::determine_actual_backtrack_level (int jump) {
|
||||
|
||||
int res;
|
||||
|
||||
assert (level > jump);
|
||||
|
||||
if (!opts.chrono) {
|
||||
res = jump;
|
||||
LOG ("chronological backtracking disabled using jump level %d", res);
|
||||
} else if (opts.chronoalways) {
|
||||
stats.chrono++;
|
||||
res = level - 1;
|
||||
LOG ("forced chronological backtracking to level %d", res);
|
||||
} else if (jump >= level - 1) {
|
||||
res = jump;
|
||||
LOG ("jump level identical to chronological backtrack level %d", res);
|
||||
} else if ((size_t) jump < assumptions.size ()) {
|
||||
res = jump;
|
||||
LOG ("using jump level %d since it is lower than assumption level %zd",
|
||||
res, assumptions.size ());
|
||||
} else if (level - jump > opts.chronolevelim) {
|
||||
stats.chrono++;
|
||||
res = level - 1;
|
||||
LOG ("back-jumping over %d > %d levels prohibited"
|
||||
"thus backtracking chronologically to level %d",
|
||||
level - jump, opts.chronolevelim, res);
|
||||
} else if (opts.chronoreusetrail) {
|
||||
|
||||
int best_idx = 0, best_pos = 0;
|
||||
|
||||
if (use_scores ()) {
|
||||
for (size_t i = control[jump + 1].trail; i < trail.size (); i++) {
|
||||
const int idx = abs (trail[i]);
|
||||
if (best_idx && !score_smaller (this) (best_idx, idx)) continue;
|
||||
best_idx = idx;
|
||||
best_pos = i;
|
||||
}
|
||||
LOG ("best variable score %g", score (best_idx));
|
||||
} else {
|
||||
for (size_t i = control[jump + 1].trail; i < trail.size (); i++) {
|
||||
const int idx = abs (trail[i]);
|
||||
if (best_idx && bumped (best_idx) >= bumped (idx)) continue;
|
||||
best_idx = idx;
|
||||
best_pos = i;
|
||||
}
|
||||
LOG ("best variable bumped %" PRId64 "", bumped (best_idx));
|
||||
}
|
||||
assert (best_idx);
|
||||
LOG ("best variable %d at trail position %d", best_idx, best_pos);
|
||||
|
||||
// Now find the frame and decision level in the control stack of that
|
||||
// best variable index. Note that, as in 'reuse_trail', the frame
|
||||
// 'control[i]' for decision level 'i' contains the trail before that
|
||||
// decision level, i.e., the decision 'control[i].decision' sits at
|
||||
// 'control[i].trail' in the trail and we thus have to check the level
|
||||
// of the control frame one higher than at the result level.
|
||||
//
|
||||
res = jump;
|
||||
while (res < level-1 && control[res+1].trail <= best_pos)
|
||||
res++;
|
||||
|
||||
if (res == jump)
|
||||
LOG ("default non-chronological back-jumping to level %d", res);
|
||||
else {
|
||||
stats.chrono++;
|
||||
LOG ("chronological backtracking to level %d to reuse trail", res);
|
||||
}
|
||||
|
||||
} else {
|
||||
res = jump;
|
||||
LOG ("non-chronological back-jumping to level %d", res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::eagerly_subsume_recently_learned_clauses (Clause * c) {
|
||||
assert (opts.eagersubsume);
|
||||
LOG (c, "trying eager subsumption with");
|
||||
mark (c);
|
||||
int64_t lim = stats.eagertried + opts.eagersubsumelim;
|
||||
const auto begin = clauses.begin ();
|
||||
auto it = clauses.end ();
|
||||
#ifdef LOGGING
|
||||
int64_t before = stats.eagersub;
|
||||
#endif
|
||||
while (it != begin && stats.eagertried++ <= lim) {
|
||||
Clause * d = *--it;
|
||||
if (c == d) continue;
|
||||
if (d->garbage) continue;
|
||||
if (!d->redundant) continue;
|
||||
int needed = c->size;
|
||||
for (auto & lit : *d) {
|
||||
if (marked (lit) <= 0) continue;
|
||||
if (!--needed) break;
|
||||
}
|
||||
if (needed) continue;
|
||||
LOG (d, "eager subsumed");
|
||||
stats.eagersub++;
|
||||
stats.subsumed++;
|
||||
mark_garbage (d);
|
||||
}
|
||||
unmark (c);
|
||||
#ifdef LOGGING
|
||||
uint64_t subsumed = stats.eagersub - before;
|
||||
if (subsumed) LOG ("eagerly subsumed %" PRIu64 " clauses", subsumed);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is the main conflict analysis routine. It assumes that a conflict
|
||||
// was found. Then we derive the 1st UIP clause, optionally minimize it,
|
||||
// add it as learned clause, and then uses the clause for conflict directed
|
||||
// back-jumping and flipping the 1st UIP literal. In combination with
|
||||
// chronological backtracking (see discussion above) the algorithm becomes
|
||||
// slightly more involved.
|
||||
|
||||
void Internal::analyze () {
|
||||
|
||||
START (analyze);
|
||||
|
||||
assert (conflict);
|
||||
|
||||
// First update moving averages of trail height at conflict.
|
||||
//
|
||||
UPDATE_AVERAGE (averages.current.trail.fast, trail.size ());
|
||||
UPDATE_AVERAGE (averages.current.trail.slow, trail.size ());
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (opts.chrono) {
|
||||
|
||||
int forced;
|
||||
|
||||
const int conflict_level = find_conflict_level (forced);
|
||||
|
||||
// In principle we can perform conflict analysis as in non-chronological
|
||||
// backtracking except if there is only one literal with the maximum
|
||||
// assignment level in the clause. Then standard conflict analysis is
|
||||
// unnecessary and we can use the conflict as a driving clause. In the
|
||||
// pseudo code of the SAT'18 paper on chronological backtracking this
|
||||
// corresponds to the situation handled in line 4-6 in Alg. 1, except
|
||||
// that the pseudo code in the paper only backtracks while we eagerly
|
||||
// assign the single literal on the highest decision level.
|
||||
|
||||
if (forced) {
|
||||
|
||||
assert (forced);
|
||||
assert (conflict_level > 0);
|
||||
LOG ("single highest level literal %d", forced);
|
||||
|
||||
// The pseudo code in the SAT'18 paper actually backtracks to the
|
||||
// 'second highest decision' level, while their code backtracks
|
||||
// to 'conflict_level-1', which is more in the spirit of chronological
|
||||
// backtracking anyhow and thus we also do the latter.
|
||||
//
|
||||
backtrack (conflict_level - 1);
|
||||
|
||||
LOG ("forcing %d", forced);
|
||||
search_assign_driving (forced, conflict);
|
||||
|
||||
conflict = 0;
|
||||
STOP (analyze);
|
||||
return;
|
||||
}
|
||||
|
||||
// Backtracking to the conflict level is in the pseudo code in the
|
||||
// SAT'18 chronological backtracking paper, but not in their actual
|
||||
// implementation. In principle we do not need to backtrack here.
|
||||
// However, as a side effect of backtracking to the conflict level we
|
||||
// set 'level' to the conflict level which then allows us to reuse the
|
||||
// old 'analyze' code as is. The alternative (which we also tried but
|
||||
// then abandoned) is to use 'conflict_level' instead of 'level' in the
|
||||
// analysis, which however requires to pass it to the 'analyze_reason'
|
||||
// and 'analyze_literal' functions.
|
||||
//
|
||||
backtrack (conflict_level);
|
||||
}
|
||||
|
||||
// Actual conflict on root level, thus formula unsatisfiable.
|
||||
//
|
||||
if (!level) {
|
||||
learn_empty_clause ();
|
||||
if (external->learner) external->export_learned_empty_clause ();
|
||||
STOP (analyze);
|
||||
return;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// First derive the 1st UIP clause by going over literals assigned on the
|
||||
// current decision level. Literals in the conflict are marked as 'seen'
|
||||
// as well as all literals in reason clauses of already 'seen' literals on
|
||||
// the current decision level. Thus the outer loop starts with the
|
||||
// conflict clause as 'reason' and then uses the 'reason' of the next
|
||||
// seen literal on the trail assigned on the current decision level.
|
||||
// During this process maintain the number 'open' of seen literals on the
|
||||
// current decision level with not yet processed 'reason'. As soon 'open'
|
||||
// drops to one, we have found the first unique implication point. This
|
||||
// is sound because the topological order in which literals are processed
|
||||
// follows the assignment order and a more complex algorithm to find
|
||||
// articulation points is not necessary.
|
||||
//
|
||||
Clause * reason = conflict;
|
||||
LOG (reason, "analyzing conflict");
|
||||
|
||||
assert (clause.empty ());
|
||||
|
||||
int i = trail.size (); // Start at end-of-trail.
|
||||
int open = 0; // Seen but not processed on this level.
|
||||
int uip = 0; // The first UIP literal.
|
||||
|
||||
for (;;) {
|
||||
analyze_reason (uip, reason, open);
|
||||
uip = 0;
|
||||
while (!uip) {
|
||||
assert (i > 0);
|
||||
const int lit = trail[--i];
|
||||
if (!flags (lit).seen) continue;
|
||||
if (var (lit).level == level) uip = lit;
|
||||
}
|
||||
if (!--open) break;
|
||||
reason = var (uip).reason;
|
||||
LOG (reason, "analyzing %d reason", uip);
|
||||
}
|
||||
LOG ("first UIP %d", uip);
|
||||
clause.push_back (-uip);
|
||||
|
||||
// Update glue and learned (1st UIP literals) statistics.
|
||||
//
|
||||
int size = (int) clause.size ();
|
||||
const int glue = (int) levels.size () - 1;
|
||||
LOG (clause, "1st UIP size %d and glue %d clause", size, glue);
|
||||
UPDATE_AVERAGE (averages.current.glue.fast, glue);
|
||||
UPDATE_AVERAGE (averages.current.glue.slow, glue);
|
||||
stats.learned.literals += size;
|
||||
stats.learned.clauses++;
|
||||
assert (glue < size);
|
||||
|
||||
|
||||
// Minimize the 1st UIP clause as pioneered by Niklas Soerensson in
|
||||
// MiniSAT and described in our joint SAT'09 paper.
|
||||
//
|
||||
if (size > 1) {
|
||||
if (opts.shrink)
|
||||
shrink_and_minimize_clause();
|
||||
else if (opts.minimize)
|
||||
minimize_clause();
|
||||
|
||||
size = (int) clause.size ();
|
||||
|
||||
// Update decision heuristics.
|
||||
//
|
||||
if (opts.bump)
|
||||
bump_variables();
|
||||
|
||||
if(opts.psids) pol_dec_act();
|
||||
|
||||
if (external->learner) external->export_learned_large_clause (clause);
|
||||
} else if (external->learner)
|
||||
external->export_learned_unit_clause(-uip);
|
||||
|
||||
// Update actual size statistics.
|
||||
//
|
||||
stats.units += (size == 1);
|
||||
stats.binaries += (size == 2);
|
||||
UPDATE_AVERAGE (averages.current.size, size);
|
||||
|
||||
// Determine back-jump level, learn driving clause, backtrack and assign
|
||||
// flipped 1st UIP literal.
|
||||
//
|
||||
int jump;
|
||||
Clause * driving_clause = new_driving_clause (glue, jump);
|
||||
UPDATE_AVERAGE (averages.current.jump, jump);
|
||||
|
||||
int new_level = determine_actual_backtrack_level (jump);;
|
||||
UPDATE_AVERAGE (averages.current.level, new_level);
|
||||
backtrack (new_level);
|
||||
|
||||
if (uip) search_assign_driving (-uip, driving_clause);
|
||||
else learn_empty_clause ();
|
||||
|
||||
if (stable) reluctant.tick (); // Reluctant has its own 'conflict' counter.
|
||||
|
||||
// Clean up.
|
||||
//
|
||||
clear_analyzed_literals ();
|
||||
clear_analyzed_levels ();
|
||||
clause.clear ();
|
||||
conflict = 0;
|
||||
|
||||
STOP (analyze);
|
||||
|
||||
if (driving_clause && opts.eagersubsume)
|
||||
eagerly_subsume_recently_learned_clauses (driving_clause);
|
||||
}
|
||||
|
||||
// We wait reporting a learned unit until propagation of that unit is
|
||||
// completed. Otherwise the 'i' report gives the number of remaining
|
||||
// variables before propagating the unit (and hides the actual remaining
|
||||
// variables after propagating it).
|
||||
|
||||
void Internal::iterate () { iterating = false; report ('i'); }
|
||||
|
||||
}
|
30
hCaD_V2/src/arena.cpp
Normal file
30
hCaD_V2/src/arena.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
Arena::Arena (Internal * i) {
|
||||
memset (this, 0, sizeof *this);
|
||||
internal = i;
|
||||
}
|
||||
|
||||
Arena::~Arena () {
|
||||
delete [] from.start;
|
||||
delete [] to.start;
|
||||
}
|
||||
|
||||
void Arena::prepare (size_t bytes) {
|
||||
LOG ("preparing 'to' space of arena with %zd bytes", bytes);
|
||||
assert (!to.start);
|
||||
to.top = to.start = new char[bytes];
|
||||
to.end = to.start + bytes;
|
||||
}
|
||||
|
||||
void Arena::swap () {
|
||||
delete [] from.start;
|
||||
LOG ("delete 'from' space of arena with %zd bytes",
|
||||
(size_t) (from.end - from.start));
|
||||
from = to;
|
||||
to.start = to.top = to.end = 0;
|
||||
}
|
||||
|
||||
}
|
103
hCaD_V2/src/arena.hpp
Normal file
103
hCaD_V2/src/arena.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
#ifndef _arena_hpp_INCLUDED
|
||||
#define _arena_hpp_INCLUDED
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// This memory allocation arena provides fixed size pre-allocated memory for
|
||||
// the moving garbage collector 'copy_non_garbage_clauses' in 'collect.cpp'
|
||||
// to hold clauses which should survive garbage collection.
|
||||
|
||||
// The advantage of using a pre-allocated arena is that the allocation order
|
||||
// of the clauses can be adapted in such a way that clauses watched by the
|
||||
// same literal are allocated consecutively. This improves locality during
|
||||
// propagation and thus is more cache friendly. A similar technique is
|
||||
// implemented in MiniSAT and Glucose and gives substantial speed-up in
|
||||
// propagations per second even though it might even almost double peek
|
||||
// memory usage. Note that in MiniSAT this arena is actually required for
|
||||
// MiniSAT to be able to use 32 bit clauses references instead of 64 bit
|
||||
// pointers. This would restrict the maximum number of clauses and thus is
|
||||
// a restriction we do not want to use anymore.
|
||||
|
||||
// New learned clauses are allocated in CaDiCaL outside of this arena and
|
||||
// moved to the arena during garbage collection. The additional 'to' space
|
||||
// required for such a moving garbage collector is only allocated for those
|
||||
// clauses surviving garbage collection, which usually needs much less
|
||||
// memory than all clauses. The net effect is that in our implementation
|
||||
// the moving garbage collector using this arena only needs roughly 50% more
|
||||
// memory than allocating the clauses directly. Both implementations can be
|
||||
// compared by varying the 'opts.arenatype' option (which also controls the
|
||||
// allocation order of clauses during moving them).
|
||||
|
||||
// The standard sequence of using the arena is as follows:
|
||||
//
|
||||
// Arena arena;
|
||||
// ...
|
||||
// arena.prepare (bytes);
|
||||
// q1 = arena.copy (p1, bytes1);
|
||||
// ...
|
||||
// qn = arena.copy (pn, bytesn);
|
||||
// assert (bytes1 + ... + bytesn <= bytes);
|
||||
// arena.swap ();
|
||||
// ...
|
||||
// if (!arena.contains (q)) delete q;
|
||||
// ...
|
||||
// arena.prepare (bytes);
|
||||
// q1 = arena.copy (p1, bytes1);
|
||||
// ...
|
||||
// qn = arena.copy (pn, bytesn);
|
||||
// assert (bytes1 + ... + bytesn <= bytes);
|
||||
// arena.swap ();
|
||||
// ...
|
||||
//
|
||||
// One has to be really careful with 'qi' references to arena memory.
|
||||
|
||||
struct Internal;
|
||||
|
||||
class Arena {
|
||||
|
||||
Internal * internal;
|
||||
|
||||
struct { char * start, * top, * end; } from, to;
|
||||
|
||||
public:
|
||||
|
||||
Arena (Internal *);
|
||||
~Arena ();
|
||||
|
||||
// Prepare 'to' space to hold that amount of memory. Precondition is that
|
||||
// the 'to' space is empty. The following sequence of 'copy' operations
|
||||
// can use as much memory in sum as pre-allocated here.
|
||||
//
|
||||
void prepare (size_t bytes);
|
||||
|
||||
// Does the memory pointed to by 'p' belong to this arena? More precisely
|
||||
// to the 'from' space, since that is the only one remaining after 'swap'.
|
||||
//
|
||||
bool contains (void * p) const {
|
||||
char * c = (char *) p;
|
||||
return from.start <= c && c < from.top;
|
||||
}
|
||||
|
||||
// Allocate that amount of memory in 'to' space. This assumes the 'to'
|
||||
// space has been prepared to hold enough memory with 'prepare'. Then
|
||||
// copy the memory pointed to by 'p' of size 'bytes'. Note that it does
|
||||
// not matter whether 'p' is in 'from' or allocated outside of the arena.
|
||||
//
|
||||
char * copy (const char * p, size_t bytes) {
|
||||
char * res = to.top;
|
||||
to.top += bytes;
|
||||
assert (to.top <= to.end);
|
||||
memcpy (res, p, bytes);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Completely delete 'from' space and then replace 'from' by 'to' (by
|
||||
// pointer swapping). Everything previously allocated (in 'from') and not
|
||||
// explicitly copied to 'to' with 'copy' becomes invalid.
|
||||
//
|
||||
void swap ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
178
hCaD_V2/src/assume.cpp
Normal file
178
hCaD_V2/src/assume.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// Failed literal handling as pioneered by MiniSAT. This first function
|
||||
// adds an assumption literal onto the assumption stack.
|
||||
|
||||
void Internal::assume (int lit) {
|
||||
Flags & f = flags (lit);
|
||||
const unsigned char bit = bign (lit);
|
||||
if (f.assumed & bit) {
|
||||
LOG ("ignoring already assumed %d", lit);
|
||||
return;
|
||||
}
|
||||
LOG ("assume %d", lit);
|
||||
f.assumed |= bit;
|
||||
assumptions.push_back (lit);
|
||||
freeze (lit);
|
||||
}
|
||||
|
||||
// Find all failing assumptions starting from the one on the assumption
|
||||
// stack with the lowest decision level. This goes back to MiniSAT and is
|
||||
// called 'analyze_final' there.
|
||||
|
||||
void Internal::failing () {
|
||||
|
||||
START (analyze);
|
||||
|
||||
LOG ("analyzing failing assumptions");
|
||||
|
||||
assert (analyzed.empty ());
|
||||
assert (clause.empty ());
|
||||
|
||||
int first = 0;
|
||||
|
||||
// Try to find two clashing assumptions.
|
||||
//
|
||||
for (auto & lit : assumptions) {
|
||||
if (!assumed (-lit)) continue;
|
||||
first = lit;
|
||||
break;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
|
||||
// TODO: if 'first' is on the root-level, then, as below, we probably
|
||||
// want an empty core.
|
||||
|
||||
clause.push_back (first);
|
||||
clause.push_back (-first);
|
||||
|
||||
Flags & f = flags (first);
|
||||
|
||||
unsigned bit = bign (first);
|
||||
assert (!(f.failed & bit));
|
||||
f.failed |= bit;
|
||||
bit = bign (-first);
|
||||
f.failed |= bit;
|
||||
|
||||
} else {
|
||||
|
||||
// Find an assumption falsified at smallest decision level.
|
||||
//
|
||||
for (auto & lit : assumptions) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp >= 0) continue;
|
||||
if (!first || var (first).level > var (lit).level)
|
||||
first = lit;
|
||||
}
|
||||
|
||||
assert (first);
|
||||
LOG ("starting with assumption %d falsified on decision level %d",
|
||||
first, var (first).level);
|
||||
|
||||
if (!var (first).level) {
|
||||
|
||||
// TODO: I think marking this should yield an empty core
|
||||
// and not mark 'first' as failed (similarly above).
|
||||
|
||||
LOG ("failed assumption %d", first);
|
||||
clause.push_back (-first);
|
||||
|
||||
Flags & f = flags (first);
|
||||
const unsigned bit = bign (first);
|
||||
assert (!(f.failed & bit));
|
||||
f.failed |= bit;
|
||||
|
||||
} else {
|
||||
|
||||
// The 'analyzed' stack serves as working stack for a BFS through the
|
||||
// implication graph until decisions, which are all assumptions, or units
|
||||
// are reached. This is simpler than corresponding code in 'analyze'.
|
||||
|
||||
{
|
||||
LOG ("failed assumption %d", first);
|
||||
Flags & f = flags (first);
|
||||
const unsigned bit = bign (first);
|
||||
assert (!(f.failed & bit));
|
||||
f.failed |= bit;
|
||||
f.seen = true;
|
||||
}
|
||||
|
||||
analyzed.push_back (first);
|
||||
clause.push_back (-first);
|
||||
|
||||
size_t next = 0;
|
||||
|
||||
while (next < analyzed.size ()) {
|
||||
const int lit = analyzed[next++];
|
||||
#ifndef NDEBUG
|
||||
if (first == lit) assert (val (lit) < 0);
|
||||
else assert (val (lit) > 0);
|
||||
#endif
|
||||
Var & v = var (lit);
|
||||
if (!v.level) continue;
|
||||
|
||||
if (v.reason) {
|
||||
assert (v.level);
|
||||
LOG (v.reason, "analyze reason");
|
||||
for (const auto & other : *v.reason) {
|
||||
Flags & f = flags (other);
|
||||
if (f.seen) continue;
|
||||
f.seen = true;
|
||||
assert (val (other) < 0);
|
||||
analyzed.push_back (-other);
|
||||
}
|
||||
} else {
|
||||
assert (assumed (lit));
|
||||
LOG ("failed assumption %d", lit);
|
||||
clause.push_back (-lit);
|
||||
Flags & f = flags (lit);
|
||||
const unsigned bit = bign (lit);
|
||||
assert (!(f.failed & bit));
|
||||
f.failed |= bit;
|
||||
}
|
||||
}
|
||||
clear_analyzed_literals ();
|
||||
|
||||
// TODO, we can not do clause minimization here, right?
|
||||
}
|
||||
}
|
||||
|
||||
VERBOSE (1, "found %zd failed assumptions %.0f%%",
|
||||
clause.size (), percent (clause.size (), assumptions.size ()));
|
||||
|
||||
// We do not actually need to learn this clause, since the conflict is
|
||||
// forced already by some other clauses. There is also no bumping
|
||||
// of variables nor clauses necessary. But we still want to check
|
||||
// correctness of the claim that the determined subset of failing
|
||||
// assumptions are a high-level core or equivalently their negations form
|
||||
// a unit-implied clause.
|
||||
//
|
||||
external->check_learned_clause ();
|
||||
if (proof) {
|
||||
proof->add_derived_clause (clause);
|
||||
proof->delete_clause (clause);
|
||||
}
|
||||
clause.clear ();
|
||||
|
||||
STOP (analyze);
|
||||
}
|
||||
|
||||
// Add the start of each incremental phase (leaving the state
|
||||
// 'UNSATISFIABLE' actually) we reset all assumptions.
|
||||
|
||||
void Internal::reset_assumptions () {
|
||||
for (const auto & lit : assumptions) {
|
||||
Flags & f = flags (lit);
|
||||
const unsigned char bit = bign (lit);
|
||||
f.assumed &= ~bit;
|
||||
f.failed &= ~bit;
|
||||
melt (lit);
|
||||
}
|
||||
LOG ("cleared %zd assumptions", assumptions.size ());
|
||||
assumptions.clear ();
|
||||
}
|
||||
|
||||
}
|
30
hCaD_V2/src/averages.cpp
Normal file
30
hCaD_V2/src/averages.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
void Internal::init_averages () {
|
||||
|
||||
LOG ("initializing averages");
|
||||
|
||||
INIT_EMA (averages.current.jump, opts.emajump);
|
||||
INIT_EMA (averages.current.level, opts.emalevel);
|
||||
INIT_EMA (averages.current.size, opts.emasize);
|
||||
|
||||
INIT_EMA (averages.current.glue.fast, opts.emagluefast);
|
||||
INIT_EMA (averages.current.glue.slow, opts.emaglueslow);
|
||||
|
||||
INIT_EMA (averages.current.trail.fast, opts.ematrailfast);
|
||||
INIT_EMA (averages.current.trail.slow, opts.ematrailslow);
|
||||
|
||||
assert (!averages.swapped);
|
||||
}
|
||||
|
||||
void Internal::swap_averages () {
|
||||
LOG ("saving current averages");
|
||||
swap (averages.current, averages.saved);
|
||||
if (!averages.swapped) init_averages ();
|
||||
else LOG ("swapping in previously saved averages");
|
||||
averages.swapped++;
|
||||
}
|
||||
|
||||
}
|
36
hCaD_V2/src/averages.hpp
Normal file
36
hCaD_V2/src/averages.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef _averages_hpp_INCLUDED
|
||||
#define _averages_hpp_INCLUDED
|
||||
|
||||
#include "ema.hpp" // alphabetically after 'averages.hpp'
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct Averages {
|
||||
|
||||
int64_t swapped;
|
||||
|
||||
struct {
|
||||
|
||||
struct {
|
||||
EMA fast; // average fast (small window) moving glucose level
|
||||
EMA slow; // average slow (large window) moving glucose level
|
||||
} glue;
|
||||
|
||||
struct {
|
||||
EMA fast; // average fast (small window) moving trail level
|
||||
EMA slow; // average slow (large window) moving trail level
|
||||
} trail;
|
||||
|
||||
EMA size; // average learned clause size
|
||||
EMA jump; // average (potential non-chronological) back-jump level
|
||||
EMA level; // average back track level after conflict
|
||||
|
||||
} current, saved;
|
||||
|
||||
Averages () : swapped (0) { }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
125
hCaD_V2/src/backtrack.cpp
Normal file
125
hCaD_V2/src/backtrack.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// The global assignment stack can only be (partially) reset through
|
||||
// 'backtrack' which is the only function using 'unassign' (inlined and thus
|
||||
// local to this file). It turns out that 'unassign' does not need a
|
||||
// specialization for 'probe' nor 'vivify' and thus it is shared.
|
||||
|
||||
inline void Internal::unassign (int lit) {
|
||||
assert (val (lit) > 0);
|
||||
const int idx = vidx (lit);
|
||||
vals[idx] = 0;
|
||||
vals[-idx] = 0;
|
||||
LOG ("unassign %d @ %d", lit, var (idx).level);
|
||||
|
||||
// In the standard EVSIDS variable decision heuristic of MiniSAT, we need
|
||||
// to push variables which become unassigned back to the heap.
|
||||
//
|
||||
if (!scores.contains (idx)) scores.push_back (idx);
|
||||
|
||||
// For VMTF we need to update the 'queue.unassigned' pointer in case this
|
||||
// variable sits after the variable to which 'queue.unassigned' currently
|
||||
// points. See our SAT'15 paper for more details on this aspect.
|
||||
//
|
||||
if (queue.bumped < btab[idx]) update_queue_unassigned (idx);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Update the current target maximum assignment and also the very best
|
||||
// assignment. Whether a trail produces a conflict is determined during
|
||||
// propagation. Thus that all functions in the 'search' loop after
|
||||
// propagation can assume that 'no_conflict_until' is valid. If a conflict
|
||||
// is found then the trail before the last decision is used (see the end of
|
||||
// 'propagate'). During backtracking we can then save this largest
|
||||
// propagation conflict free assignment. It is saved as both 'target'
|
||||
// assignment for picking decisions in 'stable' mode and if it is the
|
||||
// largest ever such assignment also as 'best' assignment. This 'best'
|
||||
// assignment can then be used in future stable decisions after the next
|
||||
// 'rephase_best' overwrites saved phases with it.
|
||||
|
||||
void Internal::update_target_and_best () {
|
||||
|
||||
bool reset = (rephased && stats.conflicts > last.rephase.conflicts);
|
||||
|
||||
if (reset) {
|
||||
target_assigned = 0;
|
||||
if (rephased == 'B') best_assigned = 0; // update it again
|
||||
}
|
||||
|
||||
if (no_conflict_until > target_assigned) {
|
||||
copy_phases (phases.target);
|
||||
target_assigned = no_conflict_until;
|
||||
LOG ("new target trail level %zu", target_assigned);
|
||||
}
|
||||
|
||||
if (no_conflict_until > best_assigned) {
|
||||
copy_phases (phases.best);
|
||||
best_assigned = no_conflict_until;
|
||||
LOG ("new best trail level %zu", best_assigned);
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
report (rephased);
|
||||
rephased = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::backtrack (int new_level) {
|
||||
|
||||
assert (new_level <= level);
|
||||
if (new_level == level) return;
|
||||
|
||||
stats.backtracks++;
|
||||
update_target_and_best ();
|
||||
|
||||
const size_t assigned = control[new_level+1].trail;
|
||||
|
||||
LOG ("backtracking to decision level %d with decision %d and trail %zd",
|
||||
new_level, control[new_level].decision, assigned);
|
||||
|
||||
const size_t end_of_trail = trail.size ();
|
||||
size_t i = assigned, j = i;
|
||||
|
||||
int reassigned = 0, unassigned = 0;
|
||||
|
||||
while (i < end_of_trail) {
|
||||
int lit = trail[i++];
|
||||
Var & v = var (lit);
|
||||
if (v.level > new_level) {
|
||||
unassign (lit);
|
||||
unassigned++;
|
||||
} else {
|
||||
// This is the essence of the SAT'18 paper on chronological
|
||||
// backtracking. It is possible to just keep out-of-order assigned
|
||||
// literals on the trail without breaking the solver (after some
|
||||
// modifications to 'analyze' - see 'opts.chrono' guarded code there).
|
||||
assert (opts.chrono);
|
||||
#ifdef LOGGING
|
||||
if (!v.level) LOG ("reassign %d @ 0 unit clause %d", lit, lit);
|
||||
else LOG (v.reason, "reassign %d @ %d", lit, v.level);
|
||||
#endif
|
||||
trail[j] = lit;
|
||||
v.trail = j++;
|
||||
reassigned++;
|
||||
}
|
||||
}
|
||||
trail.resize (j);
|
||||
LOG ("unassigned %d literals %.0f%%",
|
||||
unassigned, percent (unassigned, unassigned + reassigned));
|
||||
LOG ("reassigned %d literals %.0f%%",
|
||||
reassigned, percent (reassigned, unassigned + reassigned));
|
||||
|
||||
if (propagated > assigned) propagated = assigned;
|
||||
if (propagated2 > assigned) propagated2 = assigned;
|
||||
if (no_conflict_until > assigned) no_conflict_until = assigned;
|
||||
|
||||
control.resize (new_level + 1);
|
||||
level = new_level;
|
||||
}
|
||||
|
||||
}
|
148
hCaD_V2/src/backward.cpp
Normal file
148
hCaD_V2/src/backward.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Provide eager backward subsumption for resolved clauses.
|
||||
|
||||
// The eliminator maintains a queue of clauses that are new and have to be
|
||||
// checked to subsume or strengthen other (longer or same size) clauses.
|
||||
|
||||
void Eliminator::enqueue (Clause * c) {
|
||||
if (!internal->opts.elimbackward) return;
|
||||
if (c->enqueued) return;
|
||||
LOG (c, "backward enqueue");
|
||||
backward.push (c);
|
||||
c->enqueued = true;
|
||||
}
|
||||
|
||||
Clause * Eliminator::dequeue () {
|
||||
if (backward.empty ()) return 0;
|
||||
Clause * res = backward.front ();
|
||||
backward.pop ();
|
||||
assert (res->enqueued);
|
||||
res->enqueued = false;
|
||||
LOG (res, "backward dequeue");
|
||||
return res;
|
||||
}
|
||||
|
||||
Eliminator::~Eliminator () {
|
||||
while (dequeue ())
|
||||
;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::elim_backward_clause (Eliminator & eliminator, Clause *c) {
|
||||
assert (opts.elimbackward);
|
||||
assert (!c->redundant);
|
||||
if (c->garbage) return;
|
||||
LOG (c, "attempting backward subsumption and strengthening with");
|
||||
size_t len = UINT_MAX;
|
||||
unsigned size = 0;
|
||||
int best = 0;
|
||||
bool satisfied = false;
|
||||
for (const auto & lit : *c) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (tmp < 0) continue;
|
||||
size_t l = occs (lit).size ();
|
||||
LOG ("literal %d occurs %zd times", lit, l);
|
||||
if (l < len) best = lit, len = l;
|
||||
mark (lit);
|
||||
size++;
|
||||
}
|
||||
if (satisfied) {
|
||||
LOG ("clause actually already satisfied");
|
||||
elim_update_removed_clause (eliminator, c);
|
||||
mark_garbage (c);
|
||||
} else if (len > (size_t) opts.elimocclim) {
|
||||
LOG ("skipping backward subsumption due to too many occurrences");
|
||||
} else {
|
||||
assert (len);
|
||||
LOG ("literal %d has smallest number of occurrences %zd", best, len);
|
||||
LOG ("marked %d literals in clause of size %d", size, c->size);
|
||||
for (auto & d : occs (best)) {
|
||||
if (d == c) continue;
|
||||
if (d->garbage) continue;
|
||||
if ((unsigned) d->size < size) continue;
|
||||
int negated = 0;
|
||||
unsigned found = 0;
|
||||
for (const auto & lit : *d) {
|
||||
signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (tmp < 0) continue;
|
||||
tmp = marked (lit);
|
||||
if (!tmp) continue;
|
||||
if (tmp < 0) {
|
||||
if (negated) { size = UINT_MAX; break; }
|
||||
else negated = lit;
|
||||
}
|
||||
if (++found == size) break;
|
||||
}
|
||||
if (satisfied) {
|
||||
LOG (d, "found satisfied clause");
|
||||
elim_update_removed_clause (eliminator, d);
|
||||
mark_garbage (d);
|
||||
} else if (found == size) {
|
||||
if (!negated) {
|
||||
LOG (d, "found subsumed clause");
|
||||
elim_update_removed_clause (eliminator, d);
|
||||
mark_garbage (d);
|
||||
stats.subsumed++;
|
||||
stats.elimbwsub++;
|
||||
} else {
|
||||
int unit = 0;
|
||||
for (const auto & lit : * d) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp < 0) continue;
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (lit == negated) continue;
|
||||
if (unit) { unit = INT_MIN; break; }
|
||||
else unit = lit;
|
||||
}
|
||||
assert (unit);
|
||||
if (satisfied) {
|
||||
mark_garbage (d);
|
||||
elim_update_removed_clause (eliminator, d);
|
||||
} else if (unit && unit != INT_MIN) {
|
||||
assert (unit);
|
||||
LOG (d, "unit %d through hyper unary resolution with", unit);
|
||||
assign_unit (unit);
|
||||
elim_propagate (eliminator, unit);
|
||||
break;
|
||||
} else if (occs (negated).size () <= (size_t) opts.elimocclim) {
|
||||
strengthen_clause (d, negated);
|
||||
remove_occs (occs (negated), d);
|
||||
elim_update_removed_lit (eliminator, negated);
|
||||
stats.elimbwstr++;
|
||||
assert (negated != best);
|
||||
eliminator.enqueue (d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unmark (c);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::elim_backward_clauses (Eliminator & eliminator) {
|
||||
if (!opts.elimbackward) {
|
||||
assert (eliminator.backward.empty ());
|
||||
return;
|
||||
}
|
||||
START (backward);
|
||||
LOG ("attempting backward subsumption and strengthening with %zd clauses",
|
||||
eliminator.backward.size ());
|
||||
Clause * c;
|
||||
while (!unsat && (c = eliminator.dequeue ()))
|
||||
elim_backward_clause (eliminator, c);
|
||||
STOP (backward);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
}
|
22
hCaD_V2/src/bins.cpp
Normal file
22
hCaD_V2/src/bins.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Binary implication graph lists.
|
||||
|
||||
void Internal::init_bins () {
|
||||
assert (big.empty ());
|
||||
while (big.size () < 2*vsize)
|
||||
big.push_back (Bins ());
|
||||
LOG ("initialized binary implication graph");
|
||||
}
|
||||
|
||||
void Internal::reset_bins () {
|
||||
assert (!big.empty ());
|
||||
erase_vector (big);
|
||||
LOG ("reset binary implication graph");
|
||||
}
|
||||
|
||||
}
|
17
hCaD_V2/src/bins.hpp
Normal file
17
hCaD_V2/src/bins.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef _bins_hpp_INCLUDED
|
||||
#define _bins_hpp_INCLUDED
|
||||
|
||||
#include "util.hpp" // Alphabetically after 'bins'.
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef vector<int> Bins;
|
||||
|
||||
inline void shrink_bins (Bins & bs) { shrink_vector (bs); }
|
||||
inline void erase_bins (Bins & bs) { erase_vector (bs); }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
751
hCaD_V2/src/block.cpp
Normal file
751
hCaD_V2/src/block.cpp
Normal file
@ -0,0 +1,751 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This implements an inprocessing version of blocked clause elimination and
|
||||
// is assumed to be triggered just before bounded variable elimination. It
|
||||
// has a separate 'block' flag while variable elimination uses 'elim'.
|
||||
// Thus it only tries to block clauses on a literal which was removed in an
|
||||
// irredundant clause in negated form before and has not been tried to use
|
||||
// as blocking literal since then.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
inline bool block_more_occs_size::operator () (unsigned a, unsigned b) {
|
||||
size_t s = internal->noccs (-internal->u2i (a));
|
||||
size_t t = internal->noccs (-internal->u2i (b));
|
||||
if (s > t) return true;
|
||||
if (s < t) return false;
|
||||
s = internal->noccs (internal->u2i (a));
|
||||
t = internal->noccs (internal->u2i (b));
|
||||
if (s > t) return true;
|
||||
if (s < t) return false;
|
||||
return a > b;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Determine whether 'c' is blocked on 'lit', by first marking all its
|
||||
// literals and then checking all resolvents with negative clauses (with
|
||||
// '-lit') are tautological. We use a move-to-front scheme for both the
|
||||
// occurrence list of negative clauses (with '-lit') and then for literals
|
||||
// within each such clause. The clause move-to-front scheme has the goal to
|
||||
// find non-tautological clauses faster in the future, while the literal
|
||||
// move-to-front scheme has the goal to faster find the matching literal,
|
||||
// which makes the resolvent tautological (again in the future).
|
||||
|
||||
bool Internal::is_blocked_clause (Clause * c, int lit) {
|
||||
|
||||
LOG (c, "trying to block on %d", lit);
|
||||
|
||||
assert (c->size >= opts.blockminclslim);
|
||||
assert (c->size <= opts.blockmaxclslim);
|
||||
assert (active (lit));
|
||||
assert (!val (lit));
|
||||
assert (!c->garbage);
|
||||
assert (!c->redundant);
|
||||
assert (!level);
|
||||
|
||||
mark (c); // First mark all literals in 'c'.
|
||||
|
||||
Occs & os = occs (-lit);
|
||||
LOG ("resolving against at most %zd clauses with %d", os.size (), -lit);
|
||||
|
||||
bool res = true; // Result is true if all resolvents tautological.
|
||||
|
||||
// Can not use 'auto' here since we update 'os' during traversal.
|
||||
//
|
||||
const auto end_of_os = os.end ();
|
||||
auto i = os.begin ();
|
||||
|
||||
Clause * prev_d = 0; // Previous non-tautological clause.
|
||||
|
||||
for (; i != end_of_os; i++)
|
||||
{
|
||||
// Move the first clause with non-tautological resolvent to the front of
|
||||
// the occurrence list to improve finding it faster later.
|
||||
//
|
||||
Clause * d = *i;
|
||||
|
||||
assert (!d->garbage);
|
||||
assert (!d->redundant);
|
||||
assert (d->size <= opts.blockmaxclslim);
|
||||
|
||||
*i = prev_d; // Move previous non-tautological clause
|
||||
prev_d = d; // backwards but remember clause at this position.
|
||||
|
||||
LOG (d, "resolving on %d against", lit);
|
||||
stats.blockres++;
|
||||
|
||||
int prev_other = 0; // Previous non-tautological literal.
|
||||
|
||||
// No 'auto' since we update literals of 'd' during traversal.
|
||||
//
|
||||
const const_literal_iterator end_of_d = d->end ();
|
||||
literal_iterator l;
|
||||
|
||||
for (l = d->begin (); l != end_of_d; l++)
|
||||
{
|
||||
// Same move-to-front mechanism for literals within a clause. It
|
||||
// moves the first negatively marked literal to the front to find it
|
||||
// faster in the future.
|
||||
//
|
||||
const int other = *l;
|
||||
*l = prev_other;
|
||||
prev_other = other;
|
||||
if (other == -lit) continue;
|
||||
assert (other != lit);
|
||||
assert (active (other));
|
||||
assert (!val (other));
|
||||
if (marked (other) < 0) {
|
||||
LOG ("found tautological literal %d", other);
|
||||
d->literals[0] = other; // Move to front of 'd'.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (l == end_of_d)
|
||||
{
|
||||
LOG ("no tautological literal found");
|
||||
//
|
||||
// Since we did not find a tautological literal we restore the old
|
||||
// order of literals in the clause.
|
||||
//
|
||||
const const_literal_iterator begin_of_d = d->begin ();
|
||||
while (l-- != begin_of_d) {
|
||||
const int other = *l;
|
||||
*l = prev_other;
|
||||
prev_other = other;
|
||||
}
|
||||
res = false; // Now 'd' is a witness that 'c' is not blocked.
|
||||
os[0] = d; // Move it to the front of the occurrence list.
|
||||
break;
|
||||
}
|
||||
}
|
||||
unmark (c); // ... all literals of the candidate clause.
|
||||
|
||||
// If all resolvents are tautological and thus the clause is blocked we
|
||||
// restore the old order of clauses in the occurrence list of '-lit'.
|
||||
//
|
||||
if (res) {
|
||||
assert (i == end_of_os);
|
||||
const auto boc = os.begin ();
|
||||
while (i != boc) {
|
||||
Clause * d = *--i;
|
||||
*i = prev_d;
|
||||
prev_d = d;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::block_schedule (Blocker & blocker)
|
||||
{
|
||||
// Set skip flags for all literals in too large clauses.
|
||||
//
|
||||
for (const auto & c : clauses) {
|
||||
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
if (c->size <= opts.blockmaxclslim) continue;
|
||||
|
||||
for (const auto & lit : *c)
|
||||
mark_skip (-lit);
|
||||
}
|
||||
|
||||
// Connect all literal occurrences in irredundant clauses.
|
||||
//
|
||||
for (const auto & c : clauses) {
|
||||
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
|
||||
for (const auto & lit : *c) {
|
||||
assert (active (lit));
|
||||
assert (!val (lit));
|
||||
occs (lit).push_back (c);
|
||||
}
|
||||
}
|
||||
|
||||
// We establish the invariant that 'noccs' gives the number of actual
|
||||
// occurrences of 'lit' in non-garbage clauses, while 'occs' might still
|
||||
// refer to garbage clauses, thus 'noccs (lit) <= occs (lit).size ()'. It
|
||||
// is expensive to remove references to garbage clauses from 'occs' during
|
||||
// blocked clause elimination, but decrementing 'noccs' is cheap.
|
||||
|
||||
for (auto lit : lits) {
|
||||
if (!active (lit)) continue;
|
||||
assert (!val (lit));
|
||||
Occs & os = occs (lit);
|
||||
noccs (lit) = os.size ();
|
||||
}
|
||||
|
||||
// Now we fill the schedule (priority queue) of candidate literals to be
|
||||
// tried as blocking literals. It is probably slightly faster to do this
|
||||
// in one go after all occurrences have been determined, instead of
|
||||
// filling the priority queue during pushing occurrences. Filling the
|
||||
// schedule can not be fused with the previous loop (easily) since we
|
||||
// first have to initialize 'noccs' for both 'lit' and '-lit'.
|
||||
|
||||
int skipped = 0;
|
||||
|
||||
for (auto idx : vars) {
|
||||
if (!active (idx)) continue;
|
||||
if (frozen (idx)) { skipped += 2; continue; }
|
||||
assert (!val (idx));
|
||||
for (int sign = -1; sign <= 1; sign += 2) {
|
||||
const int lit = sign * idx;
|
||||
if (marked_skip (lit)) { skipped++; continue; }
|
||||
if (!marked_block (lit)) continue;
|
||||
unmark_block (lit);
|
||||
LOG ("scheduling %d with %" PRId64 " positive and %" PRId64 " negative occurrences",
|
||||
lit, noccs (lit), noccs (-lit));
|
||||
blocker.schedule.push_back (vlit (lit));
|
||||
}
|
||||
}
|
||||
|
||||
PHASE ("block", stats.blockings,
|
||||
"scheduled %zd candidate literals %.2f%% (%d skipped %.2f%%)",
|
||||
blocker.schedule.size (),
|
||||
percent (blocker.schedule.size (), 2.0*active ()),
|
||||
skipped, percent (skipped, 2.0*active ()));
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// A literal is pure if it only occurs positive. Then all clauses in which
|
||||
// it occurs are blocked on it. This special case can be implemented faster
|
||||
// than trying to block literals with at least one negative occurrence and
|
||||
// is thus handled separately. It also allows to avoid pushing blocked
|
||||
// clauses onto the extension stack.
|
||||
|
||||
void Internal::block_pure_literal (Blocker & blocker, int lit)
|
||||
{
|
||||
if (frozen (lit)) return;
|
||||
assert (active (lit));
|
||||
|
||||
Occs & pos = occs (lit);
|
||||
Occs & nos = occs (-lit);
|
||||
|
||||
assert (!noccs (-lit));
|
||||
#ifndef NDEBUG
|
||||
for (const auto & c : nos) assert (c->garbage);
|
||||
#endif
|
||||
stats.blockpurelits++;
|
||||
LOG ("found pure literal %d", lit);
|
||||
|
||||
int64_t pured = 0;
|
||||
|
||||
for (const auto & c : pos) {
|
||||
if (c->garbage) continue;
|
||||
assert (!c->redundant);
|
||||
LOG (c, "pure literal %d in", lit);
|
||||
blocker.reschedule.push_back (c);
|
||||
external->push_clause_on_extension_stack (c, lit);
|
||||
stats.blockpured++;
|
||||
mark_garbage (c);
|
||||
pured++;
|
||||
}
|
||||
|
||||
erase_vector (pos);
|
||||
erase_vector (nos);
|
||||
|
||||
mark_pure (lit);
|
||||
stats.blockpured++;
|
||||
LOG ("blocking %" PRId64 " clauses on pure literal %d", pured, lit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// If there is only one negative clause with '-lit' it is faster to mark it
|
||||
// instead of marking all the positive clauses with 'lit' one after the
|
||||
// other and then resolving against the negative clause.
|
||||
|
||||
void
|
||||
Internal::block_literal_with_one_negative_occ (Blocker & blocker, int lit)
|
||||
{
|
||||
assert (active (lit));
|
||||
assert (!frozen (lit));
|
||||
assert (noccs (lit) > 0);
|
||||
assert (noccs (-lit) == 1);
|
||||
|
||||
Occs & nos = occs (-lit);
|
||||
assert (nos.size () >= 1);
|
||||
|
||||
Clause * d = 0;
|
||||
for (const auto & c : nos) {
|
||||
if (c->garbage) continue;
|
||||
assert (!d);
|
||||
d = c;
|
||||
#ifndef NDEBUG
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
assert (d);
|
||||
nos.resize (1);
|
||||
nos[0] = d;
|
||||
|
||||
if (d && d->size > opts.blockmaxclslim) {
|
||||
LOG (d, "skipped common antecedent");
|
||||
return;
|
||||
}
|
||||
|
||||
assert (!d->garbage);
|
||||
assert (!d->redundant);
|
||||
assert (d->size <= opts.blockmaxclslim);
|
||||
|
||||
LOG (d, "common %d antecedent", lit);
|
||||
mark (d);
|
||||
|
||||
int64_t blocked = 0, skipped = 0;
|
||||
|
||||
Occs & pos = occs (lit);
|
||||
|
||||
// Again no 'auto' since 'pos' is update during traversal.
|
||||
//
|
||||
const auto eop = pos.end ();
|
||||
auto j = pos.begin (), i = j;
|
||||
|
||||
for (; i != eop; i++)
|
||||
{
|
||||
Clause * c = *j++ = *i;
|
||||
|
||||
if (c->garbage) { j--; continue; }
|
||||
if (c->size > opts.blockmaxclslim) { skipped++; continue; }
|
||||
if (c->size < opts.blockminclslim) { skipped++; continue; }
|
||||
|
||||
LOG (c, "trying to block on %d", lit);
|
||||
|
||||
// We use the same literal move-to-front strategy as in
|
||||
// 'is_blocked_clause'. See there for more explanations.
|
||||
|
||||
int prev_other = 0; // Previous non-tautological literal.
|
||||
|
||||
// No 'auto' since literals of 'c' are updated during traversal.
|
||||
//
|
||||
const const_literal_iterator end_of_c = c->end ();
|
||||
literal_iterator l;
|
||||
|
||||
for (l = c->begin (); l != end_of_c; l++)
|
||||
{
|
||||
const int other = *l;
|
||||
*l = prev_other;
|
||||
prev_other = other;
|
||||
if (other == lit) continue;
|
||||
assert (other != -lit);
|
||||
assert (active (other));
|
||||
assert (!val (other));
|
||||
if (marked (other) < 0) {
|
||||
LOG ("found tautological literal %d", other);
|
||||
c->literals[0] = other; // Move to front of 'c'.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (l == end_of_c) {
|
||||
LOG ("no tautological literal found");
|
||||
|
||||
// Restore old literal order in the clause because.
|
||||
|
||||
const const_literal_iterator begin_of_c = c->begin ();
|
||||
while (l-- != begin_of_c) {
|
||||
const int other = *l;
|
||||
*l = prev_other;
|
||||
prev_other = other;
|
||||
}
|
||||
|
||||
continue; // ... with next candidate 'c' in 'pos'.
|
||||
}
|
||||
|
||||
blocked++;
|
||||
LOG (c, "blocked");
|
||||
external->push_clause_on_extension_stack (c, lit);
|
||||
blocker.reschedule.push_back (c);
|
||||
mark_garbage (c);
|
||||
j--;
|
||||
}
|
||||
if (j == pos.begin ()) erase_vector (pos);
|
||||
else pos.resize (j - pos.begin ());
|
||||
|
||||
stats.blocked += blocked;
|
||||
LOG ("blocked %" PRId64 " clauses on %d (skipped %" PRId64 ")", blocked, lit, skipped);
|
||||
|
||||
unmark (d);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Determine the set of candidate clauses with 'lit', which are checked to
|
||||
// be blocked by 'lit'. Filter out too large and small clauses and which do
|
||||
// not have any negated other literal in any of the clauses with '-lit'.
|
||||
|
||||
size_t Internal::block_candidates (Blocker & blocker, int lit) {
|
||||
|
||||
assert (blocker.candidates.empty ());
|
||||
|
||||
Occs & pos = occs (lit); // Positive occurrences of 'lit'.
|
||||
Occs & nos = occs (-lit);
|
||||
|
||||
assert ((size_t) noccs (lit) <= pos.size ());
|
||||
assert ((size_t) noccs (-lit) == nos.size ()); // Already flushed.
|
||||
|
||||
// Mark all literals in clauses with '-lit'. Note that 'mark2' uses
|
||||
// separate bits for 'lit' and '-lit'.
|
||||
//
|
||||
for (const auto & c : nos) mark2 (c);
|
||||
|
||||
const auto eop = pos.end ();
|
||||
auto j = pos.begin (), i = j;
|
||||
|
||||
for (; i != eop; i++)
|
||||
{
|
||||
Clause * c = *j++ = *i;
|
||||
if (c->garbage) { j--; continue; }
|
||||
assert (!c->redundant);
|
||||
if (c->size > opts.blockmaxclslim) continue;
|
||||
if (c->size < opts.blockminclslim) continue;
|
||||
const const_literal_iterator eoc = c->end ();
|
||||
const_literal_iterator l;
|
||||
for (l = c->begin (); l != eoc; l++) {
|
||||
const int other = *l;
|
||||
if (other == lit) continue;
|
||||
assert (other != -lit);
|
||||
assert (active (other));
|
||||
assert (!val (other));
|
||||
if (marked2 (-other)) break;
|
||||
}
|
||||
if (l != eoc) blocker.candidates.push_back (c);
|
||||
}
|
||||
if (j == pos.begin ()) erase_vector (pos);
|
||||
else pos.resize (j - pos.begin ());
|
||||
|
||||
assert (pos.size () == (size_t) noccs (lit)); // Now also flushed.
|
||||
|
||||
for (const auto & c : nos) unmark (c);
|
||||
|
||||
return blocker.candidates.size ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Try to find a clause with '-lit' which does not have any literal in
|
||||
// clauses with 'lit'. If such a clause exists no candidate clause can be
|
||||
// blocked on 'lit' since all candidates would produce a non-tautological
|
||||
// resolvent with that clause.
|
||||
|
||||
Clause * Internal::block_impossible (Blocker & blocker, int lit)
|
||||
{
|
||||
assert (noccs (-lit) > 1);
|
||||
assert (blocker.candidates.size () > 1);
|
||||
|
||||
for (const auto & c : blocker.candidates) mark2 (c);
|
||||
|
||||
Occs & nos = occs (-lit);
|
||||
Clause * res = 0;
|
||||
|
||||
for (const auto & c : nos) {
|
||||
assert (!c->garbage);
|
||||
assert (!c->redundant);
|
||||
assert (c->size <= opts.blockmaxclslim);
|
||||
const const_literal_iterator eoc = c->end ();
|
||||
const_literal_iterator l;
|
||||
for (l = c->begin (); l != eoc; l++) {
|
||||
const int other = *l;
|
||||
if (other == -lit) continue;
|
||||
assert (other != lit);
|
||||
assert (active (other));
|
||||
assert (!val (other));
|
||||
if (marked2 (-other)) break;
|
||||
}
|
||||
if (l == eoc) res = c;
|
||||
}
|
||||
|
||||
for (const auto & c : blocker.candidates) unmark (c);
|
||||
|
||||
if (res) {
|
||||
LOG (res, "common non-tautological resolvent producing");
|
||||
blocker.candidates.clear ();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// In the general case we have at least two negative occurrences.
|
||||
|
||||
void Internal::block_literal_with_at_least_two_negative_occs (
|
||||
Blocker & blocker,
|
||||
int lit)
|
||||
{
|
||||
assert (active (lit));
|
||||
assert (!frozen (lit));
|
||||
assert (noccs (lit) > 0);
|
||||
assert (noccs (-lit) > 1);
|
||||
|
||||
Occs & nos = occs (-lit);
|
||||
assert ((size_t) noccs (-lit) <= nos.size ());
|
||||
|
||||
int max_size = 0;
|
||||
|
||||
// Flush all garbage clauses in occurrence list 'nos' of '-lit' and
|
||||
// determine the maximum size of negative clauses (with '-lit').
|
||||
//
|
||||
const auto eon = nos.end ();
|
||||
auto j = nos.begin (), i = j;
|
||||
for (; i != eon; i++)
|
||||
{
|
||||
Clause * c = *j++ = *i;
|
||||
if (c->garbage) j--;
|
||||
else if (c->size > max_size) max_size = c->size;
|
||||
}
|
||||
if (j == nos.begin ()) erase_vector (nos);
|
||||
else nos.resize (j - nos.begin ());
|
||||
|
||||
assert (nos.size () == (size_t) noccs (-lit));
|
||||
assert (nos.size () > 1);
|
||||
|
||||
// If the maximum size of a negative clause (with '-lit') exceeds the
|
||||
// maximum clause size limit ignore this candidate literal.
|
||||
//
|
||||
if (max_size > opts.blockmaxclslim) {
|
||||
LOG ("maximum size %d of clauses with %d exceeds clause size limit %d",
|
||||
max_size, -lit, opts.blockmaxclslim);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG ("maximum size %d of clauses with %d", max_size, -lit);
|
||||
|
||||
// We filter candidate clauses with positive occurrence of 'lit' in
|
||||
// 'blocker.candidates' and return if no candidate clause remains.
|
||||
// Candidates should be small enough and should have at least one literal
|
||||
// which occurs negated in one of the clauses with '-lit'.
|
||||
//
|
||||
size_t candidates = block_candidates (blocker, lit);
|
||||
if (!candidates) {
|
||||
LOG ("no candidate clauses found");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG ("found %zd candidate clauses", candidates);
|
||||
|
||||
// We further search for a clause with '-lit' that has no literal
|
||||
// negated in any of the candidate clauses (except 'lit'). If such a
|
||||
// clause exists, we know that none of the candidates is blocked.
|
||||
//
|
||||
if (candidates > 1 && block_impossible (blocker, lit)) {
|
||||
LOG ("impossible to block any candidate clause on %d", lit);
|
||||
assert (blocker.candidates.empty ());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG ("trying to block %zd clauses out of %" PRId64 " with literal %d",
|
||||
candidates, noccs (lit), lit);
|
||||
|
||||
int64_t blocked = 0;
|
||||
|
||||
// Go over all remaining candidates and try to block them on 'lit'.
|
||||
//
|
||||
for (const auto & c : blocker.candidates) {
|
||||
assert (!c->garbage);
|
||||
assert (!c->redundant);
|
||||
if (!is_blocked_clause (c, lit)) continue;
|
||||
blocked++;
|
||||
LOG (c, "blocked");
|
||||
external->push_clause_on_extension_stack (c, lit);
|
||||
blocker.reschedule.push_back (c);
|
||||
mark_garbage (c);
|
||||
}
|
||||
|
||||
LOG ("blocked %" PRId64 " clauses on %d out of %zd candidates in %zd occurrences",
|
||||
blocked, lit, blocker.candidates.size (), occs (lit).size ());
|
||||
|
||||
blocker.candidates.clear ();
|
||||
stats.blocked += blocked;
|
||||
if (blocked) flush_occs (lit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Reschedule literals in a clause (except 'lit') which was blocked.
|
||||
|
||||
void
|
||||
Internal::block_reschedule_clause (Blocker & blocker, int lit, Clause * c)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
(void) lit;
|
||||
#endif
|
||||
assert (c->garbage);
|
||||
|
||||
for (const auto & other : *c) {
|
||||
|
||||
int64_t & n = noccs (other);
|
||||
assert (n > 0);
|
||||
n--;
|
||||
|
||||
LOG ("updating %d with %" PRId64 " positive and %" PRId64 " negative occurrences",
|
||||
other, noccs (other), noccs (-other));
|
||||
|
||||
if (blocker.schedule.contains (vlit (-other)))
|
||||
blocker.schedule.update (vlit (-other));
|
||||
else if (active (other) &&
|
||||
!frozen (other) &&
|
||||
!marked_skip (-other)) {
|
||||
LOG ("rescheduling to block clauses on %d", -other);
|
||||
blocker.schedule.push_back (vlit (-other));
|
||||
}
|
||||
|
||||
if (blocker.schedule.contains (vlit (other))) {
|
||||
assert (other != lit);
|
||||
blocker.schedule.update (vlit (other));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reschedule all literals in clauses blocked by 'lit' (except 'lit').
|
||||
|
||||
void Internal::block_reschedule (Blocker & blocker, int lit)
|
||||
{
|
||||
while (!blocker.reschedule.empty ()) {
|
||||
Clause * c = blocker.reschedule.back ();
|
||||
blocker.reschedule.pop_back ();
|
||||
block_reschedule_clause (blocker, lit, c);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::block_literal (Blocker & blocker, int lit)
|
||||
{
|
||||
assert (!marked_skip (lit));
|
||||
|
||||
if (!active (lit)) return; // Pure literal '-lit'.
|
||||
if (frozen (lit)) return;
|
||||
|
||||
assert (!val (lit));
|
||||
|
||||
// If the maximum number of a negative clauses (with '-lit') exceeds the
|
||||
// occurrence limit ignore this candidate literal.
|
||||
//
|
||||
if (noccs (-lit) > opts.blockocclim) return;
|
||||
|
||||
LOG ("blocking literal candidate %d "
|
||||
"with %" PRId64 " positive and %" PRId64 " negative occurrences",
|
||||
lit, noccs (lit), noccs (-lit));
|
||||
|
||||
stats.blockcands++;
|
||||
|
||||
assert (blocker.reschedule.empty ());
|
||||
assert (blocker.candidates.empty ());
|
||||
|
||||
if (!noccs (-lit)) block_pure_literal (blocker, lit);
|
||||
else if (!noccs (lit)) {
|
||||
// Rare situation, where the clause length limit was hit for 'lit' and
|
||||
// '-lit' is skipped and then it becomes pure. Can be ignored. We also
|
||||
// so it once happing for a 'elimboundmin=-1' and zero positive and one
|
||||
// negative occurrence.
|
||||
} else if (noccs (-lit) == 1)
|
||||
block_literal_with_one_negative_occ (blocker, lit);
|
||||
else
|
||||
block_literal_with_at_least_two_negative_occs (blocker, lit);
|
||||
|
||||
// Done with blocked clause elimination on this literal and we do not
|
||||
// have to try blocked clause elimination on it again until irredundant
|
||||
// clauses with its negation are removed.
|
||||
//
|
||||
assert (!frozen (lit)); // just to be sure ...
|
||||
unmark_block (lit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::block () {
|
||||
|
||||
if (!opts.block) return false;
|
||||
if (unsat) return false;
|
||||
if (!stats.current.irredundant) return false;
|
||||
if (terminated_asynchronously ()) return false;
|
||||
|
||||
if (propagated < trail.size ()) {
|
||||
LOG ("need to propagate %zd units first", trail.size () - propagated);
|
||||
init_watches ();
|
||||
connect_watches ();
|
||||
if (!propagate ()) {
|
||||
LOG ("propagating units results in empty clause");
|
||||
learn_empty_clause ();
|
||||
assert (unsat);
|
||||
}
|
||||
clear_watches ();
|
||||
reset_watches ();
|
||||
if (unsat) return false;
|
||||
}
|
||||
|
||||
START_SIMPLIFIER (block, BLOCK);
|
||||
|
||||
stats.blockings++;
|
||||
|
||||
LOG ("block-%" PRId64 "", stats.blockings);
|
||||
|
||||
assert (!level);
|
||||
assert (!watching ());
|
||||
assert (!occurring ());
|
||||
|
||||
mark_satisfied_clauses_as_garbage ();
|
||||
|
||||
init_occs (); // Occurrence lists for all literals.
|
||||
init_noccs (); // Number of occurrences to avoid flushing garbage clauses.
|
||||
|
||||
Blocker blocker (this);
|
||||
block_schedule (blocker);
|
||||
|
||||
int64_t blocked = stats.blocked;
|
||||
int64_t resolutions = stats.blockres;
|
||||
int64_t purelits = stats.blockpurelits;
|
||||
int64_t pured = stats.blockpured;
|
||||
|
||||
while (!terminated_asynchronously () &&
|
||||
!blocker.schedule.empty ()) {
|
||||
int lit = u2i (blocker.schedule.front ());
|
||||
blocker.schedule.pop_front ();
|
||||
block_literal (blocker, lit);
|
||||
block_reschedule (blocker, lit);
|
||||
}
|
||||
|
||||
blocker.erase ();
|
||||
reset_noccs ();
|
||||
reset_occs ();
|
||||
|
||||
resolutions = stats.blockres - resolutions;
|
||||
blocked = stats.blocked - blocked;
|
||||
|
||||
PHASE ("block", stats.blockings,
|
||||
"blocked %" PRId64 " clauses in %" PRId64 " resolutions",
|
||||
blocked, resolutions);
|
||||
|
||||
pured = stats.blockpured - pured;
|
||||
purelits = stats.blockpurelits - purelits;
|
||||
|
||||
if (pured)
|
||||
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
|
||||
|
||||
if (purelits)
|
||||
PHASE ("block", stats.blockings,
|
||||
"found %" PRId64 " pure literals in %" PRId64 " clauses",
|
||||
purelits, pured);
|
||||
else
|
||||
PHASE ("block", stats.blockings,
|
||||
"no pure literals found");
|
||||
|
||||
report ('b', !opts.reportall && !blocked);
|
||||
|
||||
STOP_SIMPLIFIER (block, BLOCK);
|
||||
|
||||
return blocked;
|
||||
}
|
||||
|
||||
}
|
37
hCaD_V2/src/block.hpp
Normal file
37
hCaD_V2/src/block.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef _block_hpp_INCLUDED
|
||||
#define _block_hpp_INCLUDED
|
||||
|
||||
#include "heap.hpp" // Alphabetically after 'block.hpp'.
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct Internal;
|
||||
|
||||
struct block_more_occs_size {
|
||||
Internal * internal;
|
||||
block_more_occs_size (Internal * i) : internal (i) { }
|
||||
bool operator () (unsigned a, unsigned b);
|
||||
};
|
||||
|
||||
typedef heap<block_more_occs_size> BlockSchedule;
|
||||
|
||||
class Blocker {
|
||||
|
||||
friend struct Internal;
|
||||
|
||||
vector<struct Clause*> candidates;
|
||||
vector<struct Clause*> reschedule;
|
||||
BlockSchedule schedule;
|
||||
|
||||
Blocker (Internal * i) : schedule (block_more_occs_size (i)) { }
|
||||
|
||||
void erase () {
|
||||
erase_vector (candidates);
|
||||
erase_vector (reschedule);
|
||||
schedule.erase ();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
933
hCaD_V2/src/cadical.cpp
Normal file
933
hCaD_V2/src/cadical.cpp
Normal file
@ -0,0 +1,933 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Do include 'internal.hpp' but try to minimize internal dependencies.
|
||||
|
||||
#include "internal.hpp"
|
||||
#include "signal.hpp" // Separate, only need for apps.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// A wrapper app which makes up the CaDiCaL stand alone solver. It in
|
||||
// essence only consists of the 'App::main' function. So this class
|
||||
// contains code, which is not required if only the library interface in
|
||||
// the class 'Solver' is used (defined in 'cadical.hpp'). It further uses
|
||||
// static data structures in order to have a signal handler catch signals.
|
||||
//
|
||||
// It is thus neither thread-safe nor reentrant. If you want to use
|
||||
// multiple instances of the solver use the 'Solver' interface directly
|
||||
// which is thread-safe and reentrant among different solver instances.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
class App : public Handler, public Terminator {
|
||||
|
||||
Solver * solver; // Global solver.
|
||||
|
||||
#ifndef __WIN32
|
||||
// Command line options.
|
||||
//
|
||||
int time_limit; // '-t <sec>'
|
||||
#endif
|
||||
|
||||
// Strictness of (DIMACS) parsing:
|
||||
//
|
||||
// 0 = force parsing and completely ignore header
|
||||
// 1 = relaxed header handling (default)
|
||||
// 2 = strict header handling
|
||||
//
|
||||
// To use '0' use '-f' of '--force'.
|
||||
// To use '2' use '--strict'.
|
||||
//
|
||||
int force_strict_parsing;
|
||||
|
||||
bool force_writing;
|
||||
static bool most_likely_existing_cnf_file (const char * path);
|
||||
|
||||
// Internal variables.
|
||||
//
|
||||
int max_var; // Set after parsing.
|
||||
volatile bool timesup; // Asynchronous termination.
|
||||
|
||||
// Printing.
|
||||
//
|
||||
void print_usage (bool all = false);
|
||||
void print_witness (FILE *);
|
||||
|
||||
#ifndef QUIET
|
||||
void signal_message (const char * msg, int sig);
|
||||
#endif
|
||||
|
||||
// Option handling.
|
||||
//
|
||||
bool set (const char*);
|
||||
bool set (const char*, int);
|
||||
int get (const char*);
|
||||
bool verbose () { return get ("verbose") && !get ("quiet"); }
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// The actual initialization.
|
||||
//
|
||||
void init ();
|
||||
|
||||
// Terminator interface.
|
||||
//
|
||||
bool terminate () { return timesup; }
|
||||
|
||||
// Handler interface.
|
||||
//
|
||||
void catch_signal (int sig);
|
||||
void catch_alarm ();
|
||||
|
||||
public:
|
||||
|
||||
App ();
|
||||
~App ();
|
||||
|
||||
// Parse the arguments and run the solver.
|
||||
//
|
||||
int main (int arg, char ** argv);
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void App::print_usage (bool all) {
|
||||
printf (
|
||||
"usage: cadical [ <option> ... ] [ <input> [ <proof> ] ]\n"
|
||||
"\n"
|
||||
"where '<option>' is one of the following common options:\n"
|
||||
"\n");
|
||||
|
||||
if (!all) { // Print only a short list of common options.
|
||||
printf (
|
||||
" -h print this short list of common options\n"
|
||||
" --help print complete list of all options\n"
|
||||
" --version print version\n"
|
||||
"\n"
|
||||
" -n do not print witness\n"
|
||||
#ifndef QUIET
|
||||
" -v increase verbosity\n"
|
||||
" -q be quiet\n"
|
||||
#endif
|
||||
"\n"
|
||||
#ifndef __WIN32
|
||||
" -t <sec> set wall clock time limit\n"
|
||||
#endif
|
||||
);
|
||||
} else { // Print complete list of all options.
|
||||
printf (
|
||||
" -h print alternatively only a list of common options\n"
|
||||
" --help print this complete list of all options\n"
|
||||
" --version print version\n"
|
||||
"\n"
|
||||
" -n do not print witness (same as '--no-witness')\n"
|
||||
#ifndef QUIET
|
||||
" -v increase verbosity (see also '--verbose' below)\n"
|
||||
" -q be quiet (same as '--quiet')\n"
|
||||
#endif
|
||||
#ifndef __WIN32
|
||||
" -t <sec> set wall clock time limit\n"
|
||||
#endif
|
||||
"\n"
|
||||
"Or '<option>' is one of the less common options\n"
|
||||
"\n"
|
||||
" -L<rounds> run local search initially (default '0' rounds)\n"
|
||||
" -O<level> increase limits by '2^<level>' or '10^<level>'\n"
|
||||
" -P<rounds> initial preprocessing (default '0' rounds)\n"
|
||||
"\n"
|
||||
"Note there is no separating space for the options above while the\n"
|
||||
"following options require a space after the option name:\n"
|
||||
"\n"
|
||||
" -c <limit> limit the number of conflicts (default unlimited)\n"
|
||||
" -d <limit> limit the number of decisions (default unlimited)\n"
|
||||
"\n"
|
||||
" -o <output> write simplified CNF in DIMACS format to file\n"
|
||||
" -e <extend> write reconstruction/extension stack to file\n"
|
||||
#ifdef LOGGING
|
||||
" -l enable logging messages (same as '--log')\n"
|
||||
#endif
|
||||
"\n"
|
||||
" --force | -f parsing broken DIMACS header and writing proofs\n"
|
||||
" --strict strict parsing (no white space in header)\n"
|
||||
"\n"
|
||||
" -r <sol> read solution in competition output format\n"
|
||||
" to check consistency of learned clauses\n"
|
||||
" during testing and debugging\n"
|
||||
"\n"
|
||||
" -w <sol> write result including a potential witness\n"
|
||||
" solution in competition format to the given file\n"
|
||||
"\n"
|
||||
" --colors force colored output\n"
|
||||
" --no-colors disable colored output to terminal\n"
|
||||
" --no-witness do not print witness (see also '-n' above)\n"
|
||||
"\n"
|
||||
" --build print build configuration\n"
|
||||
" --copyright print copyright information\n"
|
||||
);
|
||||
|
||||
printf (
|
||||
"\n"
|
||||
"There are pre-defined configurations of advanced internal options:\n"
|
||||
"\n");
|
||||
|
||||
solver->configurations ();
|
||||
|
||||
printf (
|
||||
"\n"
|
||||
"Or '<option>' is one of the following advanced internal options:\n"
|
||||
"\n");
|
||||
solver->usage ();
|
||||
|
||||
fputs (
|
||||
"\n"
|
||||
"The internal options have their default value printed in brackets\n"
|
||||
"after their description. They can also be used in the form\n"
|
||||
"'--<name>' which is equivalent to '--<name>=1' and in the form\n"
|
||||
"'--no-<name>' which is equivalent to '--<name>=0'. One can also\n"
|
||||
"use 'true' instead of '1', 'false' instead of '0', as well as\n"
|
||||
"numbers with positive exponent such as '1e3' instead of '1000'.\n"
|
||||
"\n"
|
||||
"Alternatively option values can also be specified in the header\n"
|
||||
"of the DIMACS file, e.g., 'c --elim=false', or through environment\n"
|
||||
"variables, such as 'CADICAL_ELIM=false'. The embedded options in\n"
|
||||
"the DIMACS file have highest priority, followed by command line\n"
|
||||
"options and then values specified through environment variables.\n",
|
||||
stdout);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Common to both complete and common option usage.
|
||||
|
||||
fputs (
|
||||
"\n"
|
||||
"The input is read from '<input>' assumed to be in DIMACS format.\n"
|
||||
"Incremental 'p inccnf' files are supported too with cubes at the end.\n"
|
||||
"If '<proof>' is given then a DRAT proof is written to that file.\n",
|
||||
stdout);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// More explanations for complete option usage.
|
||||
|
||||
if (all) {
|
||||
fputs (
|
||||
"\n"
|
||||
"If '<input>' is missing then the solver reads from '<stdin>',\n"
|
||||
"also if '-' is used as input path name '<input>'. Similarly,\n"
|
||||
"\n"
|
||||
"For incremental files each cube is solved in turn. The solver\n"
|
||||
"stops at the first satisfied cube if there is one and uses that\n"
|
||||
"one for the witness to print. Conflict and decision limits are\n"
|
||||
"applied to each individual cube solving call while '-P', '-L'"
|
||||
#ifdef __WIN32
|
||||
"\n"
|
||||
#else
|
||||
" and\n"
|
||||
"'-t' "
|
||||
#endif
|
||||
"remain global. Only if all cubes were unsatisfiable the solver\n"
|
||||
"prints the standard unsatisfiable solution line ('s UNSATISFIABLE').\n"
|
||||
"\n"
|
||||
"By default the proof is stored in the binary DRAT format unless\n"
|
||||
"the option '--no-binary' is specified or the proof is written\n"
|
||||
"to '<stdout>' and '<stdout>' is connected to a terminal.\n"
|
||||
"\n"
|
||||
"The input is assumed to be compressed if it is given explicitly\n"
|
||||
"and has a '.gz', '.bz2', '.xz' or '.7z' suffix. The same applies\n"
|
||||
"to the output file. In order to use compression and decompression\n"
|
||||
"the corresponding utilities 'gzip', 'bzip', 'xz', and '7z' (depending\n"
|
||||
"on the format) are required and need to be installed on the system.\n"
|
||||
"The solver checks file type signatures though and falls back to\n"
|
||||
"non-compressed file reading if the signature does not match.\n",
|
||||
stdout);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Pretty print competition format witness with 'v' lines.
|
||||
|
||||
void App::print_witness (FILE * file) {
|
||||
int c = 0, i = 0, tmp;
|
||||
do {
|
||||
if (!c) fputc ('v', file), c = 1;
|
||||
if (i++ == max_var) tmp = 0;
|
||||
else tmp = solver->val (i) < 0 ? -i : i;
|
||||
char str[20];
|
||||
sprintf (str, " %d", tmp);
|
||||
int l = strlen (str);
|
||||
if (c + l > 78) fputs ("\nv", file), c = 1;
|
||||
fputs (str, file);
|
||||
c += l;
|
||||
} while (tmp);
|
||||
if (c) fputc ('\n', file);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Wrapper around option setting.
|
||||
|
||||
int App::get (const char * o) { return solver->get (o); }
|
||||
bool App::set (const char * o, int v) { return solver->set (o, v); }
|
||||
bool App::set (const char * arg) { return solver->set_long_option (arg); }
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool App::most_likely_existing_cnf_file (const char * path)
|
||||
{
|
||||
if (!File::exists (path)) return false;
|
||||
|
||||
if (has_suffix (path, ".dimacs")) return true;
|
||||
if (has_suffix (path, ".dimacs.gz")) return true;
|
||||
if (has_suffix (path, ".dimacs.xz")) return true;
|
||||
if (has_suffix (path, ".dimacs.bz2")) return true;
|
||||
if (has_suffix (path, ".dimacs.7z")) return true;
|
||||
if (has_suffix (path, ".dimacs.lzma")) return true;
|
||||
|
||||
if (has_suffix (path, ".cnf")) return true;
|
||||
if (has_suffix (path, ".cnf.gz")) return true;
|
||||
if (has_suffix (path, ".cnf.xz")) return true;
|
||||
if (has_suffix (path, ".cnf.bz2")) return true;
|
||||
if (has_suffix (path, ".cnf.7z")) return true;
|
||||
if (has_suffix (path, ".cnf.lzma")) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Short-cut for errors to avoid a hard 'exit'.
|
||||
|
||||
#define APPERR(...) \
|
||||
do { solver->error (__VA_ARGS__); } while (0)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int App::main (int argc, char ** argv) {
|
||||
|
||||
// Handle options which lead to immediate exit first.
|
||||
|
||||
if (argc == 2) {
|
||||
const char * arg = argv[1];
|
||||
if (!strcmp (arg, "-h")) {
|
||||
print_usage ();
|
||||
return 0;
|
||||
} else if (!strcmp (arg, "--help")) {
|
||||
print_usage (true);
|
||||
return 0;
|
||||
} else if (!strcmp (arg, "--version")) {
|
||||
printf ("%s\n", CaDiCaL::version ());
|
||||
return 0;
|
||||
} else if (!strcmp (arg, "--build")) {
|
||||
tout.disable ();
|
||||
Solver::build (stdout, "");
|
||||
return 0;
|
||||
} else if (!strcmp (arg, "--copyright")) {
|
||||
printf ("%s\n", copyright ());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Now initialize solver.
|
||||
|
||||
init ();
|
||||
|
||||
// Set all argument option values to not used yet.
|
||||
|
||||
const char * preprocessing_specified = 0, * optimization_specified = 0;
|
||||
const char * read_solution_path = 0, * write_result_path = 0;
|
||||
const char * dimacs_path = 0, * proof_path = 0;
|
||||
bool proof_specified = false, dimacs_specified = false;
|
||||
int optimize = 0, preprocessing = 0, localsearch = 0;
|
||||
const char * output_path = 0, * extension_path = 0;
|
||||
int conflict_limit = -1, decision_limit = -1;
|
||||
const char * conflict_limit_specified = 0;
|
||||
const char * decision_limit_specified = 0;
|
||||
const char * localsearch_specified = 0;
|
||||
const char * time_limit_specified = 0;
|
||||
bool witness = true, less = false;
|
||||
const char * dimacs_name, * err;
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp (argv[i], "-h") ||
|
||||
!strcmp (argv[i], "--help") ||
|
||||
!strcmp (argv[i], "--build") ||
|
||||
!strcmp (argv[i], "--version") ||
|
||||
!strcmp (argv[i], "--copyright")) {
|
||||
APPERR ("can only use '%s' as single first option", argv[i]);
|
||||
} else if (!strcmp (argv[i], "-")) {
|
||||
if (proof_specified) APPERR ("too many arguments");
|
||||
else if (!dimacs_specified) dimacs_specified = true;
|
||||
else proof_specified = true;
|
||||
} else if (!strcmp (argv[i], "-r")) {
|
||||
if (++i == argc) APPERR ("argument to '-r' missing");
|
||||
else if (read_solution_path)
|
||||
APPERR ("multiple read solution file options '-r %s' and '-r %s'",
|
||||
read_solution_path, argv[i]);
|
||||
else read_solution_path = argv[i];
|
||||
} else if (!strcmp (argv[i], "-w")) {
|
||||
if (++i == argc) APPERR ("argument to '-w' missing");
|
||||
else if (write_result_path)
|
||||
APPERR ("multiple solution file options '-w %s' and '-w %s'",
|
||||
write_result_path, argv[i]);
|
||||
else write_result_path = argv[i];
|
||||
} else if (!strcmp (argv[i], "-o")) {
|
||||
if (++i == argc) APPERR ("argument to '-o' missing");
|
||||
else if (output_path)
|
||||
APPERR ("multiple output file options '-o %s' and '-o %s'",
|
||||
output_path, argv[i]);
|
||||
else if (!force_writing &&
|
||||
most_likely_existing_cnf_file (argv[i]))
|
||||
APPERR ("output file '%s' most likely existing CNF (use '-f')",
|
||||
argv[i]);
|
||||
else if (!File::writable (argv[i]))
|
||||
APPERR ("output file '%s' not writable", argv[i]);
|
||||
else output_path = argv[i];
|
||||
} else if (!strcmp (argv[i], "-e")) {
|
||||
if (++i == argc) APPERR ("argument to '-e' missing");
|
||||
else if (extension_path)
|
||||
APPERR ("multiple extension file options '-e %s' and '-e %s'",
|
||||
extension_path, argv[i]);
|
||||
else if (!force_writing &&
|
||||
most_likely_existing_cnf_file (argv[i]))
|
||||
APPERR ("extension file '%s' most likely existing CNF (use '-f')",
|
||||
argv[i]);
|
||||
else if (!File::writable (argv[i]))
|
||||
APPERR ("extension file '%s' not writable", argv[i]);
|
||||
else extension_path = argv[i];
|
||||
} else if (is_color_option (argv[i])) {
|
||||
tout.force_colors ();
|
||||
terr.force_colors ();
|
||||
} else if (is_no_color_option (argv[i])) {
|
||||
tout.force_no_colors ();
|
||||
terr.force_no_colors ();
|
||||
} else if (!strcmp (argv[i], "--witness") ||
|
||||
!strcmp (argv[i], "--witness=true") ||
|
||||
!strcmp (argv[i], "--witness=1"))
|
||||
witness = true;
|
||||
else if (!strcmp (argv[i], "-n") ||
|
||||
!strcmp (argv[i], "--no-witness") ||
|
||||
!strcmp (argv[i], "--witness=false") ||
|
||||
!strcmp (argv[i], "--witness=0"))
|
||||
witness = false;
|
||||
else if (!strcmp (argv[i], "--less")) { // EXPERIMENTAL!
|
||||
if (less) APPERR ("multiple '--less' options");
|
||||
else if (!isatty (1))
|
||||
APPERR ("'--less' without '<stdout>' connected to terminal");
|
||||
else less = true;
|
||||
} else if (!strcmp (argv[i], "-c")) {
|
||||
if (++i == argc) APPERR ("argument to '-c' missing");
|
||||
else if (conflict_limit_specified)
|
||||
APPERR ("multiple conflict limits '-c %s' and '-c %s'",
|
||||
conflict_limit_specified, argv[i]);
|
||||
else if (!parse_int_str (argv[i], conflict_limit))
|
||||
APPERR ("invalid argument in '-c %s'", argv[i]);
|
||||
else if (conflict_limit < 0)
|
||||
APPERR ("invalid conflict limit");
|
||||
else conflict_limit_specified = argv[i];
|
||||
} else if (!strcmp (argv[i], "-d")) {
|
||||
if (++i == argc) APPERR ("argument to '-d' missing");
|
||||
else if (decision_limit_specified)
|
||||
APPERR ("multiple decision limits '-d %s' and '-d %s'",
|
||||
decision_limit_specified, argv[i]);
|
||||
else if (!parse_int_str (argv[i], decision_limit))
|
||||
APPERR ("invalid argument in '-d %s'", argv[i]);
|
||||
else if (decision_limit < 0)
|
||||
APPERR ("invalid decision limit");
|
||||
else decision_limit_specified = argv[i];
|
||||
}
|
||||
#ifndef __WIN32
|
||||
else if (!strcmp (argv[i], "-t")) {
|
||||
if (++i == argc) APPERR ("argument to '-t' missing");
|
||||
else if (time_limit_specified)
|
||||
APPERR ("multiple time limit '-t %s' and '-t %s'",
|
||||
time_limit_specified, argv[i]);
|
||||
else if (!parse_int_str (argv[i], time_limit))
|
||||
APPERR ("invalid argument in '-d %s'", argv[i]);
|
||||
else if (time_limit < 0)
|
||||
APPERR ("invalid time limit");
|
||||
else time_limit_specified = argv[i];
|
||||
}
|
||||
#endif
|
||||
#ifndef QUIET
|
||||
else if (!strcmp (argv[i], "-q")) set ("--quiet");
|
||||
else if (!strcmp (argv[i], "-v"))
|
||||
set ("verbose", get ("verbose") + 1);
|
||||
#endif
|
||||
#ifdef LOGGING
|
||||
else if (!strcmp (argv[i], "-l")) set ("--log");
|
||||
#endif
|
||||
else if (!strcmp (argv[i], "-f") ||
|
||||
!strcmp (argv[i], "--force") ||
|
||||
!strcmp (argv[i], "--force=1") ||
|
||||
!strcmp (argv[i], "--force=true"))
|
||||
force_strict_parsing = 0,
|
||||
force_writing = true;
|
||||
else if (!strcmp (argv[i], "--strict") ||
|
||||
!strcmp (argv[i], "--strict=1") ||
|
||||
!strcmp (argv[i], "--strict=true"))
|
||||
force_strict_parsing = 2;
|
||||
else if (has_prefix (argv[i], "-O")) {
|
||||
if (optimization_specified)
|
||||
APPERR ("multiple optimization options '%s' and '%s'",
|
||||
optimization_specified, argv[i]);
|
||||
optimization_specified = argv[i];
|
||||
if (!parse_int_str (argv[i] + 2, optimize))
|
||||
APPERR ("invalid optimization option '%s'", argv[i]);
|
||||
if (optimize < 0 || optimize > 31)
|
||||
APPERR ("invalid argument in '%s' (expected '0..31')", argv[i]);
|
||||
} else if (has_prefix (argv[i], "-P")) {
|
||||
if (preprocessing_specified)
|
||||
APPERR ("multiple preprocessing options '%s' and '%s'",
|
||||
preprocessing_specified, argv[i]);
|
||||
preprocessing_specified = argv[i];
|
||||
if (!parse_int_str (argv[i] + 2, preprocessing))
|
||||
APPERR ("invalid preprocessing option '%s'", argv[i]);
|
||||
if (preprocessing < 0)
|
||||
APPERR ("invalid argument in '%s' (expected non-negative number)",
|
||||
argv[i]);
|
||||
} else if (has_prefix (argv[i], "-L")) {
|
||||
if (localsearch_specified)
|
||||
APPERR ("multiple local search options '%s' and '%s'",
|
||||
localsearch_specified, argv[i]);
|
||||
localsearch_specified = argv[i];
|
||||
if (!parse_int_str (argv[i] + 2, localsearch))
|
||||
APPERR ("invalid local search option '%s'", argv[i]);
|
||||
if (localsearch < 0)
|
||||
APPERR ("invalid argument in '%s' (expected non-negative number)",
|
||||
argv[i]);
|
||||
} else if (has_prefix (argv[i], "--") &&
|
||||
solver->is_valid_configuration (argv[i] + 2)) {
|
||||
solver->configure (argv[i] + 2);
|
||||
} else if (set (argv[i])) {
|
||||
/* nothing do be done */
|
||||
} else if (argv[i][0] == '-') APPERR ("invalid option '%s'", argv[i]);
|
||||
else if (proof_specified) APPERR ("too many arguments");
|
||||
else if (dimacs_specified) {
|
||||
proof_path = argv[i];
|
||||
proof_specified = true;
|
||||
if (!force_writing &&
|
||||
most_likely_existing_cnf_file (proof_path))
|
||||
APPERR ("DRAT proof file '%s' most likely existing CNF (use '-f')",
|
||||
proof_path);
|
||||
else if (!File::writable (proof_path))
|
||||
APPERR ("DRAT proof file '%s' not writable", proof_path);
|
||||
} else dimacs_specified = true, dimacs_path = argv[i];
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (dimacs_specified && dimacs_path && !File::exists (dimacs_path))
|
||||
APPERR ("DIMACS input file '%s' does not exist", dimacs_path);
|
||||
if (read_solution_path && !File::exists (read_solution_path))
|
||||
APPERR ("solution file '%s' does not exist", read_solution_path);
|
||||
if (dimacs_specified && dimacs_path &&
|
||||
proof_specified && proof_path &&
|
||||
!strcmp (dimacs_path, proof_path) && strcmp (dimacs_path, "-"))
|
||||
APPERR ("DIMACS input file '%s' also specified as DRAT proof file",
|
||||
dimacs_path);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// The '--less' option is not fully functional yet (it is also not
|
||||
// mentioned in the 'usage' message yet). It only works as expected if
|
||||
// you let the solver run until it exits. The goal is to have a similar
|
||||
// experience as with 'git diff' if the terminal is too small for the
|
||||
// printed messages, but this needs substantial hacking.
|
||||
//
|
||||
// TODO: add proper forking, waiting, signal catching & propagating ...
|
||||
//
|
||||
FILE * less_pipe;
|
||||
if (less) {
|
||||
assert (isatty (1));
|
||||
less_pipe = popen ("less -r", "w");
|
||||
if (!less_pipe)
|
||||
APPERR ("could not execute and open pipe to 'less -r' command");
|
||||
dup2 (fileno (less_pipe), 1);
|
||||
} else less_pipe = 0;
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (read_solution_path && !get ("check")) set ("--check");
|
||||
#ifndef QUIET
|
||||
if (!get ("quiet")) {
|
||||
solver->section ("banner");
|
||||
solver->message ("%sCaDiCaL Radically Simplified CDCL SAT Solver%s",
|
||||
tout.bright_magenta_code (), tout.normal_code ());
|
||||
solver->message ("%s%s%s",
|
||||
tout.bright_magenta_code (), copyright (), tout.normal_code ());
|
||||
solver->message ();
|
||||
CaDiCaL::Solver::build (stdout, "c ");
|
||||
}
|
||||
#endif
|
||||
if (preprocessing > 0 || localsearch > 0 ||
|
||||
#ifndef __WIN32
|
||||
time_limit >= 0 ||
|
||||
#endif
|
||||
conflict_limit >= 0 || decision_limit >= 0) {
|
||||
solver->section ("limit");
|
||||
if (preprocessing > 0) {
|
||||
solver->message (
|
||||
"enabling %d initial rounds of preprocessing (due to '%s')",
|
||||
preprocessing, preprocessing_specified);
|
||||
solver->limit ("preprocessing", preprocessing);
|
||||
}
|
||||
if (localsearch > 0) {
|
||||
solver->message (
|
||||
"enabling %d initial rounds of local search (due to '%s')",
|
||||
localsearch, localsearch_specified);
|
||||
solver->limit ("localsearch", localsearch);
|
||||
}
|
||||
#ifndef __WIN32
|
||||
if (time_limit >= 0) {
|
||||
solver->message (
|
||||
"setting time limit to %d seconds real time (due to '-t %s')",
|
||||
time_limit, time_limit_specified);
|
||||
Signal::alarm (time_limit);
|
||||
solver->connect_terminator (this);
|
||||
}
|
||||
#endif
|
||||
if (conflict_limit >= 0) {
|
||||
solver->message (
|
||||
"setting conflict limit to %d conflicts (due to '%s')",
|
||||
conflict_limit, conflict_limit_specified);
|
||||
bool succeeded = solver->limit ("conflicts", conflict_limit);
|
||||
assert (succeeded), (void) succeeded;
|
||||
}
|
||||
if (decision_limit >= 0) {
|
||||
solver->message (
|
||||
"setting decision limit to %d decisions (due to '%s')",
|
||||
decision_limit, decision_limit_specified);
|
||||
bool succeeded = solver->limit ("decisions", decision_limit);
|
||||
assert (succeeded), (void) succeeded;
|
||||
}
|
||||
}
|
||||
if (verbose () || proof_specified) solver->section ("proof tracing");
|
||||
if (proof_specified) {
|
||||
if (!proof_path) {
|
||||
const bool force_binary = (isatty (1) && get ("binary"));
|
||||
if (force_binary) set ("--no-binary");
|
||||
solver->message ("writing %s proof trace to %s'<stdout>'%s",
|
||||
(get ("binary") ? "binary" : "non-binary"),
|
||||
tout.green_code (), tout.normal_code ());
|
||||
if (force_binary)
|
||||
solver->message (
|
||||
"connected to terminal thus non-binary proof forced");
|
||||
solver->trace_proof (stdout, "<stdout>");
|
||||
} else if (!solver->trace_proof (proof_path))
|
||||
APPERR ("can not open and write DRAT proof to '%s'", proof_path);
|
||||
else
|
||||
solver->message (
|
||||
"writing %s proof trace to %s'%s'%s",
|
||||
(get ("binary") ? "binary" : "non-binary"),
|
||||
tout.green_code (), proof_path, tout.normal_code ());
|
||||
} else solver->verbose (1, "will not generate nor write DRAT proof");
|
||||
solver->section ("parsing input");
|
||||
dimacs_name = dimacs_path ? dimacs_path : "<stdin>";
|
||||
string help;
|
||||
if (!dimacs_path) {
|
||||
help += " ";
|
||||
help += tout.magenta_code ();
|
||||
help += "(use '-h' for a list of common options)";
|
||||
help += tout.normal_code ();
|
||||
}
|
||||
solver->message ("reading DIMACS file from %s'%s'%s%s",
|
||||
tout.green_code (), dimacs_name, tout.normal_code (), help.c_str ());
|
||||
bool incremental;
|
||||
vector<int> cube_literals;
|
||||
if (dimacs_path)
|
||||
err = solver->read_dimacs(dimacs_path, max_var, force_strict_parsing,
|
||||
incremental, cube_literals);
|
||||
else
|
||||
err = solver->read_dimacs(stdin, dimacs_name, max_var, force_strict_parsing,
|
||||
incremental, cube_literals);
|
||||
if (err) APPERR ("%s", err);
|
||||
if (read_solution_path) {
|
||||
solver->section ("parsing solution");
|
||||
solver->message ("reading solution file from '%s'", read_solution_path);
|
||||
if ((err = solver->read_solution (read_solution_path)))
|
||||
APPERR ("%s", err);
|
||||
}
|
||||
|
||||
solver->section ("options");
|
||||
if (optimize > 0) {
|
||||
solver->optimize (optimize);
|
||||
solver->message ();
|
||||
}
|
||||
solver->options ();
|
||||
|
||||
int res = 0;
|
||||
|
||||
if (incremental) {
|
||||
bool reporting = get ("report") > 1 || get ("verbose") > 0;
|
||||
if (!reporting) set ("report", 0);
|
||||
if (!reporting) solver->section ("incremental solving");
|
||||
size_t cubes = 0, solved = 0;
|
||||
size_t satisfiable = 0, unsatisfiable = 0, inconclusive = 0;
|
||||
#ifndef QUIET
|
||||
bool quiet = get ("quiet");
|
||||
struct { double start, delta, sum; } time = { 0, 0, 0 };
|
||||
#endif
|
||||
for (auto lit : cube_literals)
|
||||
if (!lit)
|
||||
cubes++;
|
||||
if (!reporting) {
|
||||
if (cubes)
|
||||
solver->message ("starting to solve %zu cubes", cubes),
|
||||
solver->message ();
|
||||
else
|
||||
solver->message ("no cube to solve");
|
||||
}
|
||||
vector<int> cube, failed;
|
||||
for (auto lit : cube_literals) {
|
||||
if (lit) cube.push_back (lit);
|
||||
else {
|
||||
reverse (cube.begin (), cube.end ());
|
||||
for (auto other : cube)
|
||||
solver->assume (other);
|
||||
if (solved++) {
|
||||
if (conflict_limit >= 0)
|
||||
(void) solver->limit ("conflicts", conflict_limit);
|
||||
if (decision_limit >= 0)
|
||||
(void) solver->limit ("decisions", decision_limit);
|
||||
}
|
||||
#ifndef QUIET
|
||||
char buffer[160];
|
||||
if (!quiet) {
|
||||
if (reporting) {
|
||||
sprintf (buffer, "solving cube %zu / %zu %.0f%%",
|
||||
solved, cubes, percent (solved, cubes));
|
||||
solver->section (buffer);
|
||||
}
|
||||
time.start = absolute_process_time ();
|
||||
}
|
||||
#endif
|
||||
res = solver->solve ();
|
||||
#ifndef QUIET
|
||||
if (!quiet) {
|
||||
time.delta = absolute_process_time () - time.start;
|
||||
time.sum += time.delta;
|
||||
sprintf (buffer,
|
||||
"%s"
|
||||
"in %.3f sec "
|
||||
"(%.0f%% after %.2f sec at %.0f ms/cube)"
|
||||
"%s",
|
||||
tout.magenta_code (),
|
||||
time.delta,
|
||||
percent (solved, cubes),
|
||||
time.sum,
|
||||
relative (1e3*time.sum, solved),
|
||||
tout.normal_code ());
|
||||
if (reporting)
|
||||
solver->message ();
|
||||
const char * cube_str, * status_str, * color_code;
|
||||
if (res == 10) {
|
||||
cube_str = "CUBE";
|
||||
color_code = tout.green_code ();
|
||||
status_str = "SATISFIABLE";
|
||||
} else if (res == 20) {
|
||||
cube_str = "CUBE";
|
||||
color_code = tout.cyan_code ();
|
||||
status_str = "UNSATISFIABLE";
|
||||
} else {
|
||||
cube_str = "cube";
|
||||
color_code = tout.magenta_code ();
|
||||
status_str = "inconclusive";
|
||||
}
|
||||
const char * fmt;
|
||||
if (reporting) fmt = "%s%s %zu %s%s %s";
|
||||
else fmt = "%s%s %zu %-13s%s %s";
|
||||
solver->message (fmt,
|
||||
color_code, cube_str, solved, status_str,
|
||||
tout.normal_code (), buffer);
|
||||
}
|
||||
#endif
|
||||
if (res == 10) {
|
||||
satisfiable++;
|
||||
break;
|
||||
} else if (res == 20) {
|
||||
unsatisfiable++;
|
||||
for (auto other : cube)
|
||||
if (solver->failed (other))
|
||||
failed.push_back (other);
|
||||
for (auto other : failed)
|
||||
solver->add (-other);
|
||||
solver->add (0);
|
||||
failed.clear ();
|
||||
} else {
|
||||
assert (!res);
|
||||
inconclusive++;
|
||||
if (timesup)
|
||||
break;
|
||||
}
|
||||
cube.clear ();
|
||||
}
|
||||
}
|
||||
solver->section ("incremental summary");
|
||||
solver->message ("%zu cubes solved %.0f%%",
|
||||
solved, percent (solved, cubes));
|
||||
solver->message ("%zu cubes inconclusive %.0f%%",
|
||||
inconclusive, percent (inconclusive, solved));
|
||||
solver->message ("%zu cubes unsatisfiable %.0f%%",
|
||||
unsatisfiable, percent (unsatisfiable, solved));
|
||||
solver->message ("%zu cubes satisfiable %.0f%%",
|
||||
satisfiable, percent (satisfiable, solved));
|
||||
|
||||
if (inconclusive && res == 20)
|
||||
res = 0;
|
||||
} else {
|
||||
solver->section ("solving");
|
||||
res = solver->solve ();
|
||||
}
|
||||
|
||||
if (proof_specified) {
|
||||
solver->section ("closing proof");
|
||||
solver->flush_proof_trace ();
|
||||
solver->close_proof_trace ();
|
||||
}
|
||||
|
||||
if (output_path) {
|
||||
solver->section ("writing output");
|
||||
solver->message ("writing simplified CNF to DIMACS file %s'%s'%s",
|
||||
tout.green_code (), output_path, tout.normal_code ());
|
||||
err = solver->write_dimacs (output_path, max_var);
|
||||
if (err) APPERR ("%s", err);
|
||||
}
|
||||
|
||||
if (extension_path) {
|
||||
solver->section ("writing extension");
|
||||
solver->message ("writing extension stack to %s'%s'%s",
|
||||
tout.green_code (), extension_path, tout.normal_code ());
|
||||
err = solver->write_extension (extension_path);
|
||||
if (err) APPERR ("%s", err);
|
||||
}
|
||||
|
||||
solver->section ("result");
|
||||
|
||||
FILE * write_result_file = stdout;
|
||||
if (write_result_path)
|
||||
{
|
||||
write_result_file = fopen (write_result_path, "w");
|
||||
if (!write_result_file)
|
||||
APPERR ("could not write solution to '%s'", write_result_path);
|
||||
solver->message ("writing result to '%s'", write_result_path);
|
||||
}
|
||||
|
||||
if (res == 10) {
|
||||
fputs ("s SATISFIABLE\n", write_result_file);
|
||||
if (witness)
|
||||
print_witness (write_result_file);
|
||||
} else if (res == 20) fputs ("s UNSATISFIABLE\n", write_result_file);
|
||||
else fputs ("c UNKNOWN\n", write_result_file);
|
||||
fflush (write_result_file);
|
||||
if (write_result_path)
|
||||
fclose (write_result_file);
|
||||
solver->statistics ();
|
||||
solver->resources ();
|
||||
solver->section ("shutting down");
|
||||
solver->message ("exit %d", res);
|
||||
if (less_pipe) {
|
||||
close (1);
|
||||
pclose (less_pipe);
|
||||
}
|
||||
#ifndef __WIN32
|
||||
if (time_limit > 0)
|
||||
alarm (0);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The real initialization is delayed.
|
||||
|
||||
void App::init () {
|
||||
|
||||
assert (!solver);
|
||||
|
||||
#ifndef __WIN32
|
||||
time_limit = -1;
|
||||
#endif
|
||||
force_strict_parsing = 1;
|
||||
force_writing = false;
|
||||
max_var = 0;
|
||||
timesup = false;
|
||||
|
||||
// Call 'new Solver' only after setting 'reportdefault' and do not
|
||||
// add this call to the member initialization above. This is because for
|
||||
// stand-alone usage the default report default value is 'true' while for
|
||||
// library usage it should remain 'false'. See the explanation in
|
||||
// 'options.hpp' related to 'reportdefault' for details.
|
||||
|
||||
CaDiCaL::Options::reportdefault = 1;
|
||||
solver = new Solver ();
|
||||
Signal::set (this);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
App::App () : solver (0) { } // Only partially initialize the app.
|
||||
|
||||
App::~App () {
|
||||
if (!solver) return; // Only partially initialized.
|
||||
Signal::reset ();
|
||||
delete solver;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef QUIET
|
||||
|
||||
void App::signal_message (const char * msg, int sig) {
|
||||
solver->message (
|
||||
"%s%s %ssignal %d%s (%s)%s",
|
||||
tout.red_code (), msg,
|
||||
tout.bright_red_code (), sig,
|
||||
tout.red_code (), Signal::name (sig),
|
||||
tout.normal_code ());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void App::catch_signal (int sig) {
|
||||
#ifndef QUIET
|
||||
if (!get ("quiet")) {
|
||||
solver->message ();
|
||||
signal_message ("caught", sig);
|
||||
solver->section ("result");
|
||||
solver->message ("UNKNOWN");
|
||||
solver->statistics ();
|
||||
solver->resources ();
|
||||
solver->message ();
|
||||
signal_message ("raising", sig);
|
||||
}
|
||||
#else
|
||||
(void) sig;
|
||||
#endif
|
||||
}
|
||||
|
||||
void App::catch_alarm () {
|
||||
// Both approaches work. We keep them here for illustration purposes.
|
||||
#if 0 // THIS IS AN ALTERNATIVE WE WANT TO KEEP AROUND.
|
||||
solver->terminate (); // Immediate asynchronous call into solver.
|
||||
#else
|
||||
timesup = true; // Wait for solver to call 'App::terminate ()'.
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end of 'namespace CaDiCaL'
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The actual app is allocated on the stack and then its 'main' function is
|
||||
// called. This looks like that there are no static variables, which is not
|
||||
// completely true, since both the signal handler connected to the app as
|
||||
// well as the terminal have statically allocated components as well as the
|
||||
// options table 'Options::table'. All are shared among solvers.
|
||||
|
||||
int main (int argc, char ** argv) {
|
||||
CaDiCaL::App app;
|
||||
int res = app.main (argc, argv);
|
||||
return res;
|
||||
}
|
891
hCaD_V2/src/cadical.hpp
Normal file
891
hCaD_V2/src/cadical.hpp
Normal file
@ -0,0 +1,891 @@
|
||||
#ifndef _cadical_hpp_INCLUDED
|
||||
#define _cadical_hpp_INCLUDED
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*========================================================================*/
|
||||
|
||||
// This provides the actual API of the CaDiCaL solver, which is implemented
|
||||
// in the class 'Solver' below. Beside its constructor and destructor most
|
||||
// important is the IPASIR part which you can find between 'BEGIN IPASIR'
|
||||
// and 'END IPASIR' comments below. The following '[Example]' below might
|
||||
// also be a good starting point to understand the API.
|
||||
|
||||
/*========================================================================*/
|
||||
|
||||
// [Example]
|
||||
//
|
||||
// The internal solver state follows the IPASIR API model used in the
|
||||
// incremental track of the SAT competition. State transitions are
|
||||
// triggered by member function calls, declared and described below.
|
||||
//
|
||||
// Consider the following code (from 'test/api/example.cpp') of API usage:
|
||||
//
|
||||
// CaDiCaL::Solver * solver = new CaDiCaL::Solver;
|
||||
//
|
||||
// // ------------------------------------------------------------------
|
||||
// // Encode Problem and check without assumptions.
|
||||
//
|
||||
// enum { TIE = 1, SHIRT = 2 };
|
||||
//
|
||||
// solver->add (-TIE), solver->add (SHIRT), solver->add (0);
|
||||
// solver->add (TIE), solver->add (SHIRT), solver->add (0);
|
||||
// solver->add (-TIE), solver->add (-SHIRT), solver->add (0);
|
||||
//
|
||||
// int res = solver->solve (); // Solve instance.
|
||||
// assert (res == 10); // Check it is 'SATISFIABLE'.
|
||||
//
|
||||
// res = solver->val (TIE); // Obtain assignment of 'TIE'.
|
||||
// assert (res < 0); // Check 'TIE' assigned to 'false'.
|
||||
//
|
||||
// res = solver->val (SHIRT); // Obtain assignment of 'SHIRT'.
|
||||
// assert (res > 0); // Check 'SHIRT' assigned to 'true'.
|
||||
//
|
||||
// // ------------------------------------------------------------------
|
||||
// // Incrementally solve again under one assumption.
|
||||
//
|
||||
// solver->assume (TIE); // Now force 'TIE' to true.
|
||||
//
|
||||
// res = solver->solve (); // Solve again incrementally.
|
||||
// assert (res == 20); // Check it is 'UNSATISFIABLE'.
|
||||
//
|
||||
// res = solver->failed (TIE); // Check 'TIE' responsible.
|
||||
// assert (res); // Yes, 'TIE' in core.
|
||||
//
|
||||
// res = solver->failed (SHIRT); // Check 'SHIRT' responsible.
|
||||
// assert (!res); // No, 'SHIRT' not in core.
|
||||
//
|
||||
// // ------------------------------------------------------------------
|
||||
// // Incrementally solve once more under another assumption.
|
||||
//
|
||||
// solver->assume (-SHIRT); // Now force 'SHIRT' to false.
|
||||
//
|
||||
// res = solver->solve (); // Solve again incrementally.
|
||||
// assert (res == 20); // Check it is 'UNSATISFIABLE'.
|
||||
//
|
||||
// res = solver->failed (TIE); // Check 'TIE' responsible.
|
||||
// assert (!res); // No, 'TIE' not in core.
|
||||
//
|
||||
// res = solver->failed (-SHIRT); // Check '!SHIRT' responsible.
|
||||
// assert (res); // Yes, '!SHIRT' in core.
|
||||
//
|
||||
// // ------------------------------------------------------------------
|
||||
//
|
||||
// delete solver;
|
||||
|
||||
/*========================================================================*/
|
||||
|
||||
// [States and Transitions]
|
||||
//
|
||||
// Compared to IPASIR we also use an 'ADDING' state in which the solver
|
||||
// stays while adding non-zero literals until the clause is completed
|
||||
// through adding a zero literal. The additional 'INITIALIZING',
|
||||
// 'CONFIGURING' and 'DELETING' states are also not part of IPASIR but also
|
||||
// useful for testing and debugging.
|
||||
//
|
||||
// We have the following transitions which are all synchronous except for
|
||||
// the reentrant 'terminate' call:
|
||||
//
|
||||
// new
|
||||
// INITIALIZING --------------------------> CONFIGURING
|
||||
//
|
||||
// set / trace
|
||||
// CONFIGURING --------------------------> CONFIGURING
|
||||
//
|
||||
// add (non zero literal)
|
||||
// VALID --------------------------> ADDING
|
||||
//
|
||||
// add (zero literal)
|
||||
// VALID --------------------------> UNKNOWN
|
||||
//
|
||||
// assume (non zero literal)
|
||||
// READY --------------------------> UNKNOWN
|
||||
//
|
||||
// solve
|
||||
// READY --------------------------> SOLVING
|
||||
//
|
||||
// (internal)
|
||||
// SOLVING --------------------------> READY
|
||||
//
|
||||
// val (non zero literal)
|
||||
// SATISFIED --------------------------> SATISFIED
|
||||
//
|
||||
// failed (non zero literal )
|
||||
// UNSATISFIED --------------------------> UNSATISFIED
|
||||
//
|
||||
// delete
|
||||
// VALID --------------------------> DELETING
|
||||
//
|
||||
// where
|
||||
//
|
||||
// READY = CONFIGURING | UNKNOWN | SATISFIED | UNSATISFIED
|
||||
// VALID = READY | ADDING
|
||||
// INVALID = INITIALIZING | DELETING
|
||||
//
|
||||
// The 'SOLVING' state is only visible in different contexts, i.e., from
|
||||
// another thread or from a signal handler. It is used to implement
|
||||
// 'terminate'. Here is the only asynchronous transition:
|
||||
//
|
||||
// terminate (asynchronously)
|
||||
// SOLVING -------------------------> UNKNOWN
|
||||
//
|
||||
// The important behaviour to remember is that adding or assuming a literal
|
||||
// (immediately) destroys the satisfying assignment in the 'SATISFIED' state
|
||||
// and vice versa resets all assumptions in the 'UNSATISFIED' state. This
|
||||
// is exactly the behaviour required by the IPASIR interface.
|
||||
//
|
||||
// Furthermore, the model can only be queried through 'val' in the
|
||||
// 'SATISFIED' state, while extracting failed assumptions with 'val' only in
|
||||
// the 'UNSATISFIED' state. Solving can only be started in the 'UNKNOWN' or
|
||||
// 'CONFIGURING' state or after the previous call to 'solve' yielded an
|
||||
// 'UNKNOWN, 'SATISFIED' or 'UNSATISFIED' state.
|
||||
//
|
||||
// All literals have to be valid literals too, i.e., 32-bit integers
|
||||
// different from 'INT_MIN'. If any of these requirements is violated the
|
||||
// solver aborts with an 'API contract violation' message.
|
||||
//
|
||||
// HINT: If you do not understand why a contract is violated you can run
|
||||
// 'mobical' on the failing API call trace. Point the environment variable
|
||||
// 'CADICAL_API_TRACE' to the file where you want to save the trace during
|
||||
// execution of your program linking against the library. You probably need
|
||||
// for 'mobical' to use the option '--do-not-enforce-contracts' though to
|
||||
// force running into the same contract violation.
|
||||
//
|
||||
// Additional API calls (like 'freeze' and 'melt') do not change the state
|
||||
// of the solver and are all described below.
|
||||
|
||||
/*========================================================================*/
|
||||
|
||||
// States are represented by a bit-set in order to combine them.
|
||||
|
||||
enum State
|
||||
{
|
||||
INITIALIZING = 1, // during initialization (invalid)
|
||||
CONFIGURING = 2, // configure options (with 'set')
|
||||
UNKNOWN = 4, // ready to call 'solve'
|
||||
ADDING = 8, // adding clause literals (zero missing)
|
||||
SOLVING = 16, // while solving (within 'solve')
|
||||
SATISFIED = 32, // satisfiable allows 'val'
|
||||
UNSATISFIED = 64, // unsatisfiable allows 'failed'
|
||||
DELETING = 128, // during and after deletion (invalid)
|
||||
|
||||
// These combined states are used to check contracts.
|
||||
|
||||
READY = CONFIGURING | UNKNOWN | SATISFIED | UNSATISFIED,
|
||||
VALID = READY | ADDING,
|
||||
INVALID = INITIALIZING | DELETING
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Opaque classes needed in the API and declared in the same namespace.
|
||||
|
||||
class File;
|
||||
struct Internal;
|
||||
struct External;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Forward declaration of call-back classes. See bottom of this file.
|
||||
|
||||
class Learner;
|
||||
class Terminator;
|
||||
class ClauseIterator;
|
||||
class WitnessIterator;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
class Solver {
|
||||
|
||||
public:
|
||||
|
||||
// ====== BEGIN IPASIR ===================================================
|
||||
|
||||
// This section implements the corresponding IPASIR functionality.
|
||||
|
||||
Solver ();
|
||||
~Solver ();
|
||||
|
||||
static const char * signature (); // name of this library
|
||||
|
||||
// Core functionality as in the IPASIR incremental SAT solver interface.
|
||||
// (recall 'READY = CONFIGURING | UNKNOWN | SATISFIED | UNSATISFIED').
|
||||
// Further note that 'lit' is required to be different from 'INT_MIN' and
|
||||
// different from '0' except for 'add'.
|
||||
|
||||
// Add valid literal to clause or zero to terminate clause.
|
||||
//
|
||||
// require (VALID) // recall 'VALID = READY | ADDING'
|
||||
// if (lit) ensure (ADDING) // and thus VALID but not READY
|
||||
// if (!lit) ensure (UNKNOWN) // and thus READY
|
||||
//
|
||||
void add (int lit);
|
||||
|
||||
// Assume valid non zero literal for next call to 'solve'. These
|
||||
// assumptions are reset after the call to 'solve' as well as after
|
||||
// returning from 'simplify' and 'lookahead.
|
||||
//
|
||||
// require (READY)
|
||||
// ensure (UNKNOWN)
|
||||
//
|
||||
void assume (int lit);
|
||||
|
||||
// Try to solve the current formula. Returns
|
||||
//
|
||||
// 0 = UNSOLVED (limit reached or interrupted through 'terminate')
|
||||
// 10 = SATISFIABLE
|
||||
// 20 = UNSATISFIABLE
|
||||
//
|
||||
// require (READY)
|
||||
// ensure (UNKNOWN | SATISFIED | UNSATISFIED)
|
||||
//
|
||||
// Note, that while in this call the solver actually transitions to state
|
||||
// 'SOLVING', which however is only visible from a different context,
|
||||
// i.e., from a different thread or from a signal handler. Only right
|
||||
// before returning from this call it goes into a 'READY' state.
|
||||
//
|
||||
int solve ();
|
||||
|
||||
// Get value (-lit=false, lit=true) of valid non-zero literal.
|
||||
//
|
||||
// require (SATISFIED)
|
||||
// ensure (SATISFIED)
|
||||
//
|
||||
int val (int lit);
|
||||
|
||||
// Determine whether the valid non-zero literal is in the core.
|
||||
// Returns 'true' if the literal is in the core and 'false' otherwise.
|
||||
// Note that the core does not have to be minimal.
|
||||
//
|
||||
// require (UNSATISFIED)
|
||||
// ensure (UNSATISFIED)
|
||||
//
|
||||
bool failed (int lit);
|
||||
|
||||
// Add call-back which is checked regularly for termination. There can
|
||||
// only be one terminator connected. If a second (non-zero) one is added
|
||||
// the first one is implicitly disconnected.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
void connect_terminator (Terminator * terminator);
|
||||
void disconnect_terminator ();
|
||||
|
||||
// Add call-back which allows to export learned clauses.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
void connect_learner (Learner * learner);
|
||||
void disconnect_learner ();
|
||||
|
||||
// ====== END IPASIR =====================================================
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// This function determines a good splitting literal. The result can be
|
||||
// zero if the formula is proven to be satisfiable or unsatisfiable. This
|
||||
// can then be checked by 'state ()'. If the formula is empty and
|
||||
// the function is not able to determine satisfiability also zero is
|
||||
// returned but the state remains unknown.
|
||||
//
|
||||
// require (READY)
|
||||
// ensure (UNKNOWN|SATISFIED|UNSATISFIED)
|
||||
//
|
||||
int lookahead(void);
|
||||
|
||||
struct CubesWithStatus {
|
||||
int status;
|
||||
std::vector<std::vector<int>> cubes;
|
||||
};
|
||||
|
||||
CubesWithStatus generate_cubes(int, int min_depth = 0);
|
||||
|
||||
void reset_assumptions();
|
||||
|
||||
// Return the current state of the solver as defined above.
|
||||
//
|
||||
const State & state () const { return _state; }
|
||||
|
||||
// Similar to 'state ()' but using the staddard competition exit codes of
|
||||
// '10' for 'SATISFIABLE', '20' for 'UNSATISFIABLE' and '0' otherwise.
|
||||
//
|
||||
int status () const {
|
||||
if (_state == SATISFIED) return 10;
|
||||
else if (_state == UNSATISFIED) return 20;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static const char * version (); // return version string
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// Copy 'this' into a fresh 'other'. The copy procedure is not a deep
|
||||
// clone, but only copies irredundant clauses and units. It also makes
|
||||
// sure that witness reconstruction works with the copy as with the
|
||||
// original formula such that both solvers have the same models.
|
||||
// Assumptions are not copied. Options however are copied as well as
|
||||
// flags which remember the current state of variables in preprocessing.
|
||||
//
|
||||
// require (READY) // for 'this'
|
||||
// ensure (READY) // for 'this'
|
||||
//
|
||||
// other.require (CONFIGURING)
|
||||
// other.ensure (CONFIGURING | UNKNOWN)
|
||||
//
|
||||
void copy (Solver & other) const;
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// Variables are usually added and initialized implicitly whenever a
|
||||
// literal is used as an argument except for the functions 'val', 'fixed',
|
||||
// 'failed' and 'frozen'. However, the library internally keeps a maximum
|
||||
// variable index, which can be queried.
|
||||
//
|
||||
// require (VALID | SOLVING)
|
||||
// ensure (VALID | SOLVING)
|
||||
//
|
||||
int vars ();
|
||||
|
||||
// Increase the maximum variable index explicitly. This function makes
|
||||
// sure that at least 'min_max_var' variables are initialized. Since it
|
||||
// might need to reallocate tables, it destroys a satisfying assignment
|
||||
// and has the same state transition and conditions as 'assume' etc.
|
||||
//
|
||||
// require (READY)
|
||||
// ensure (UNKNOWN)
|
||||
//
|
||||
void reserve (int min_max_var);
|
||||
|
||||
#ifndef NTRACING
|
||||
//------------------------------------------------------------------------
|
||||
// This function can be used to write API calls to a file. The same
|
||||
// format is used which 'mobical' can read, execute and also shrink
|
||||
// through delta debugging.
|
||||
//
|
||||
// Tracing API calls can also be achieved by using the environment
|
||||
// variable 'CADICAL_API_TRACE'. That alternative is useful if you do not
|
||||
// want to change the source code using the solver, e.g., if you only have
|
||||
// a binary with the solver linked in. However, that method only allows
|
||||
// to trace one solver instance, while with the following function API
|
||||
// tracing can be enabled for different solver instances individually.
|
||||
//
|
||||
// The solver will flush the file after every trace API call but does not
|
||||
// close it during deletion. It remains owned by the user of the library.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
void trace_api_calls (FILE * file);
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Option handling.
|
||||
|
||||
// Determine whether 'name' is a valid option name.
|
||||
//
|
||||
static bool is_valid_option (const char * name);
|
||||
|
||||
// Determine whether 'name' enables a specific preprocessing technique.
|
||||
//
|
||||
static bool is_preprocessing_option (const char * name);
|
||||
|
||||
// Determine whether 'arg' is a valid long option of the form '--<name>',
|
||||
// '--<name>=<val>' or '--no-<name>' similar to 'set_long_option' below.
|
||||
// Legal values are 'true', 'false', or '[-]<mantissa>[e<exponent>]'.
|
||||
|
||||
static bool is_valid_long_option (const char * arg);
|
||||
|
||||
// Get the current value of the option 'name'. If 'name' is invalid then
|
||||
// zero is returned. Here '--...' arguments as invalid options.
|
||||
//
|
||||
int get (const char * name);
|
||||
|
||||
// Set the default verbose message prefix (default "c ").
|
||||
//
|
||||
void prefix (const char * verbose_message_prefix);
|
||||
|
||||
// Explicit version of setting an option. If the option '<name>' exists
|
||||
// and '<val>' can be parsed then 'true' is returned. If the option value
|
||||
// is out of range the actual value is computed as the closest (minimum or
|
||||
// maximum) value possible, but still 'true' is returned.
|
||||
//
|
||||
// require (CONFIGURING)
|
||||
// ensure (CONFIGURING)
|
||||
//
|
||||
// Thus options can only bet set right after initialization.
|
||||
//
|
||||
bool set (const char * name, int val);
|
||||
|
||||
// This function accepts options in command line syntax:
|
||||
//
|
||||
// '--<name>=<val>', '--<name>', or '--no-<name>'
|
||||
//
|
||||
// It actually calls the previous 'set' function after parsing 'arg'. The
|
||||
// same values are expected as for 'is_valid_long_option' above and as
|
||||
// with 'set' any value outside of the range of legal values for a
|
||||
// particular option are set to either the minimum or maximum depending on
|
||||
// which side of the valid interval they lie.
|
||||
//
|
||||
// require (CONFIGURING)
|
||||
// ensure (CONFIGURING)
|
||||
//
|
||||
bool set_long_option (const char * arg);
|
||||
|
||||
// Determine whether 'name' is a valid configuration.
|
||||
//
|
||||
static bool is_valid_configuration (const char *);
|
||||
|
||||
// Overwrite (some) options with the forced values of the configuration.
|
||||
// The result is 'true' iff the 'name' is a valid configuration.
|
||||
//
|
||||
// require (CONFIGURING)
|
||||
// ensure (CONFIGURING)
|
||||
//
|
||||
bool configure (const char *);
|
||||
|
||||
// Increase preprocessing and inprocessing limits by '10^<val>'. Values
|
||||
// below '0' are ignored and values above '9' are reduced to '9'.
|
||||
//
|
||||
// require (READY)
|
||||
// ensure (READY)
|
||||
//
|
||||
void optimize (int val);
|
||||
|
||||
// Specify search limits, where currently 'name' can be "conflicts",
|
||||
// "decisions", "preprocessing", or "localsearch". The first two limits
|
||||
// are unbounded by default. Thus using a negative limit for conflicts or
|
||||
// decisions switches back to the default of unlimited search (for that
|
||||
// particular limit). The preprocessing limit determines the number of
|
||||
// preprocessing rounds, which is zero by default. Similarly, the local
|
||||
// search limit determines the number of local search rounds (also zero by
|
||||
// default). As with 'set', the return value denotes whether the limit
|
||||
// 'name' is valid. These limits are only valid for the next 'solve' or
|
||||
// 'simplify' call and reset to their default after 'solve' returns (as
|
||||
// well as overwritten and reset during calls to 'simplify' and
|
||||
// 'lookahead'). We actually also have an internal "terminate" limit
|
||||
// which however should only be used for testing and debugging.
|
||||
//
|
||||
// require (READY)
|
||||
// ensure (READY)
|
||||
//
|
||||
bool limit (const char * arg, int val);
|
||||
bool is_valid_limit (const char * arg);
|
||||
|
||||
// The number of currently active variables and clauses can be queried by
|
||||
// these functions. Variables become active if a clause is added with it.
|
||||
// They become inactive if they are eliminated or fixed at the root level
|
||||
// Clauses become inactive if they are satisfied, subsumed, eliminated.
|
||||
// Redundant clauses are reduced regularly and thus the 'redundant'
|
||||
// function is less useful.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
int active () const; // Number of active variables.
|
||||
int64_t redundant () const; // Number of active redundant clauses.
|
||||
int64_t irredundant () const; // Number of active irredundant clauses.
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// This function executes the given number of preprocessing rounds. It is
|
||||
// similar to 'solve' with 'limits ("preprocessing", rounds)' except that
|
||||
// no CDCL nor local search, nor lucky phases are executed. The result
|
||||
// values are also the same: 0=unknown, 10=satisfiable, 20=unsatisfiable.
|
||||
// As 'solve' it resets current assumptions and limits before returning.
|
||||
// The numbers of rounds should not be negative. If the number of rounds
|
||||
// is zero only clauses are restored (if necessary) and top level unit
|
||||
// propagation is performed, which both take some time.
|
||||
//
|
||||
// require (READY)
|
||||
// ensure (UNKNOWN | SATISFIED | UNSATISFIED)
|
||||
//
|
||||
int simplify (int rounds = 3);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Force termination of 'solve' asynchronously.
|
||||
//
|
||||
// require (SOLVING | READY)
|
||||
// ensure (UNKNOWN) // actually not immediately (synchronously)
|
||||
//
|
||||
void terminate ();
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// We have the following common reference counting functions, which avoid
|
||||
// to restore clauses but require substantial user guidance. This was the
|
||||
// only way to use inprocessing in incremental SAT solving in Lingeling
|
||||
// (and before in MiniSAT's 'freeze' / 'thaw') and which did not use
|
||||
// automatic clause restoring. In general this is slower than
|
||||
// restoring clauses and should not be used.
|
||||
//
|
||||
// In essence the user freezes variables which potentially are still
|
||||
// needed in clauses added or assumptions used after the next 'solve'
|
||||
// call. As in Lingeling you can freeze a variable multiple times, but
|
||||
// then have to melt it the same number of times again in order to enable
|
||||
// variable eliminating on it etc. The arguments can be literals
|
||||
// (negative indices) but conceptually variables are frozen.
|
||||
//
|
||||
// In the old way of doing things without restore you should not use a
|
||||
// variable incrementally (in 'add' or 'assume'), which was used before
|
||||
// and potentially could have been eliminated in a previous 'solve' call.
|
||||
// This can lead to spurious satisfying assignment. In order to check
|
||||
// this API contract one can use the 'checkfrozen' option. This has the
|
||||
// drawback that restoring clauses implicitly would fail with a fatal
|
||||
// error message even if in principle the solver could just restore
|
||||
// clauses. Thus this option is disabled by default.
|
||||
//
|
||||
// See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
bool frozen (int lit) const;
|
||||
void freeze (int lit);
|
||||
void melt (int lit); // Also needs 'require (frozen (lit))'.
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// Root level assigned variables can be queried with this function.
|
||||
// It returns '1' if the literal is implied by the formula, '-1' if its
|
||||
// negation is implied, or '0' if this is unclear at this point.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
int fixed (int lit) const;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Force the default decision phase of a variable to a certain value.
|
||||
//
|
||||
void phase (int lit);
|
||||
void unphase (int lit);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// Enables clausal proof tracing in DRAT format and returns 'true' if
|
||||
// successfully opened for writing. Writing proofs has to be enabled
|
||||
// before calling 'solve', 'add' and 'dimacs', that is in state
|
||||
// 'CONFIGURING'. Otherwise only partial proofs would be written.
|
||||
//
|
||||
// require (CONFIGURING)
|
||||
// ensure (CONFIGURING)
|
||||
//
|
||||
bool trace_proof (FILE * file, const char * name); // Write DRAT proof.
|
||||
bool trace_proof (const char * path); // Open & write proof.
|
||||
|
||||
// Flush proof trace file.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
void flush_proof_trace ();
|
||||
|
||||
// Close proof trace early.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
void close_proof_trace ();
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
static void usage (); // print usage information for long options
|
||||
|
||||
static void configurations (); // print configuration usage options
|
||||
|
||||
// require (!DELETING)
|
||||
// ensure (!DELETING)
|
||||
//
|
||||
void statistics (); // print statistics
|
||||
void resources (); // print resource usage (time and memory)
|
||||
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
void options (); // print current option and value list
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Traverse irredundant clauses or the extension stack in reverse order.
|
||||
//
|
||||
// The return value is false if traversal is aborted early due to one of
|
||||
// the visitor functions returning false. See description of the
|
||||
// iterators below for more details on how to use these functions.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
bool traverse_clauses (ClauseIterator &) const;
|
||||
bool traverse_witnesses_backward (WitnessIterator &) const;
|
||||
bool traverse_witnesses_forward (WitnessIterator &) const;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Files with explicit path argument support compressed input and output
|
||||
// if appropriate helper functions 'gzip' etc. are available. They are
|
||||
// called through opening a pipe to an external command.
|
||||
//
|
||||
// If the 'strict' argument is zero then the number of variables and
|
||||
// clauses specified in the DIMACS headers are ignored, i.e., the header
|
||||
// 'p cnf 0 0' is always legal. If the 'strict' argument is larger '1'
|
||||
// strict formatting of the header is required, i.e., single spaces
|
||||
// everywhere and no trailing white space.
|
||||
//
|
||||
// Returns zero if successful and otherwise an error message.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
const char * read_dimacs (FILE * file,
|
||||
const char * name, int & vars, int strict = 1);
|
||||
|
||||
const char * read_dimacs (const char * path, int & vars, int strict = 1);
|
||||
|
||||
// The following routines work the same way but parse both DIMACS and
|
||||
// INCCNF files (with 'p inccnf' header and 'a <cube>' lines). If the
|
||||
// parser finds and 'p inccnf' header or cubes then '*incremental' is set
|
||||
// to true and the cubes are stored in the given vector (each cube
|
||||
// terminated by a zero).
|
||||
|
||||
const char * read_dimacs (FILE * file,
|
||||
const char * name, int & vars, int strict,
|
||||
bool & incremental, std::vector<int> & cubes);
|
||||
|
||||
const char * read_dimacs (const char * path, int & vars, int strict,
|
||||
bool & incremental, std::vector<int> & cubes);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Write current irredundant clauses and all derived unit clauses
|
||||
// to a file in DIMACS format. Clauses on the extension stack are
|
||||
// not included, nor any redundant clauses.
|
||||
//
|
||||
// The 'min_max_var' parameter gives a lower bound on the number '<vars>'
|
||||
// of variables used in the DIMACS 'p cnf <vars> ...' header.
|
||||
//
|
||||
// Returns zero if successful and otherwise an error message.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
const char * write_dimacs (const char * path, int min_max_var = 0);
|
||||
|
||||
// The extension stack for reconstruction a solution can be written too.
|
||||
//
|
||||
const char * write_extension (const char * path);
|
||||
|
||||
// Print build configuration to a file with prefix 'c '. If the file
|
||||
// is '<stdout>' or '<stderr>' then terminal color codes might be used.
|
||||
//
|
||||
static void build (FILE * file, const char * prefix = "c ");
|
||||
|
||||
private:
|
||||
|
||||
//==== start of state ====================================================
|
||||
|
||||
State _state; // API states as discussed above.
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// The 'Solver' class is a 'facade' object for 'External'. It exposes the
|
||||
// public API of 'External' but hides everything else (except for the some
|
||||
// private functions). It is supposed to make it easier to understand the
|
||||
// API and use the solver through the API.
|
||||
|
||||
// This approach has the benefit of decoupling this header file from all
|
||||
// internal data structures, which is particularly useful if the rest of
|
||||
// the source is not available. For instance if only a CaDiCaL library is
|
||||
// installed in a system, then only this header file has to be installed
|
||||
// too, and still allows to compile and link against the library.
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// More precisely the CaDiCaL code is split into three layers:
|
||||
//
|
||||
// Solver: facade object providing the actual API of the solver
|
||||
// External: communication layer between 'Solver' and 'Internal'
|
||||
// Internal: the actual solver code
|
||||
//
|
||||
// The 'External' and 'Internal' layers are declared and implemented in
|
||||
// the corresponding '{external,internal}.{hpp,cpp}' files (as expected),
|
||||
// while the 'Solver' facade class is defined in 'cadical.hpp' (here) but
|
||||
// implemented in 'solver.cpp'. The reason for this naming mismatch is,
|
||||
// that we want to use 'cadical.hpp' for the library header (this header
|
||||
// file) and call the binary of the stand alone SAT also 'cadical', which
|
||||
// is more naturally implemented in 'cadical.cpp'.
|
||||
//
|
||||
// Separating 'External' from 'Internal' also allows us to map external
|
||||
// literals to internal literals, which is useful with many fixed or
|
||||
// eliminated variables (during 'compact' the internal variable range is
|
||||
// reduced and external variables are remapped). Such an approach is also
|
||||
// necessary, if we want to use extended resolution in the future (such as
|
||||
// bounded variable addition).
|
||||
//
|
||||
Internal * internal; // Hidden internal solver.
|
||||
External * external; // Hidden API to internal solver mapping.
|
||||
|
||||
#ifndef NTRACING
|
||||
// The API calls to the solver can be traced by setting the environment
|
||||
// variable 'CADICAL_API_TRACE' to point to the path of a file to which
|
||||
// API calls are written. The same format is used which 'mobical' can
|
||||
// read, execute and also shrink through delta debugging.
|
||||
//
|
||||
// The environment variable is read in the constructor and the trace is
|
||||
// opened for writing and then closed again in the destructor.
|
||||
//
|
||||
// Alternatively one case use 'trace_api_calls'. Both
|
||||
//
|
||||
bool close_trace_api_file; // Close file if owned by solver it.
|
||||
FILE * trace_api_file; // Also acts as flag that we are tracing.
|
||||
|
||||
static bool tracing_api_through_environment;
|
||||
|
||||
//===== end of state ====================================================
|
||||
|
||||
void trace_api_call (const char *) const;
|
||||
void trace_api_call (const char *, int) const;
|
||||
void trace_api_call (const char *, const char *, int) const;
|
||||
#endif
|
||||
|
||||
void transition_to_unknown_state ();
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Used in the stand alone solver application 'App' and the model based
|
||||
// tester 'Mobical'. So only these two classes need direct access to the
|
||||
// otherwise more application specific functions listed here together with
|
||||
// the internal DIMACS parser.
|
||||
|
||||
friend class App;
|
||||
friend class Mobical;
|
||||
friend class Parser;
|
||||
|
||||
// Read solution in competition format for debugging and testing.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
const char * read_solution (const char * path);
|
||||
|
||||
// This gives warning messages for wrong 'printf' style format string usage.
|
||||
// Apparently (on 'gcc 9' at least) the first argument is 'this' here.
|
||||
// TODO: support for other compilers (beside 'gcc' and 'clang').
|
||||
//
|
||||
# define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION,VARIADIC_ARGUMENT_POSITION) \
|
||||
__attribute__ ((format (printf, FORMAT_POSITION, VARIADIC_ARGUMENT_POSITION)))
|
||||
|
||||
// Messages in a common style.
|
||||
//
|
||||
// require (VALID | DELETING)
|
||||
// ensure (VALID | DELETING)
|
||||
//
|
||||
void section (const char *); // print section header
|
||||
void message (const char *, ...) // ordinary message
|
||||
CADICAL_ATTRIBUTE_FORMAT (2, 3);
|
||||
|
||||
void message (); // empty line - only prefix
|
||||
void error (const char *, ...) // produce error message
|
||||
CADICAL_ATTRIBUTE_FORMAT (2, 3);
|
||||
|
||||
// Explicit verbose level ('section' and 'message' use '0').
|
||||
//
|
||||
// require (VALID | DELETING)
|
||||
// ensure (VALID | DELETING)
|
||||
//
|
||||
void verbose (int level, const char *, ...)
|
||||
CADICAL_ATTRIBUTE_FORMAT (3, 4);
|
||||
|
||||
// Factoring out common code to both 'read_dimacs' functions above.
|
||||
//
|
||||
// require (VALID)
|
||||
// ensure (VALID)
|
||||
//
|
||||
const char * read_dimacs (File *, int &, int strict,
|
||||
bool * incremental = 0,
|
||||
std::vector<int> * = 0);
|
||||
|
||||
// Factored out common code for 'solve', 'simplify' and 'lookahead'.
|
||||
//
|
||||
int call_external_solve_and_check_results (bool preprocess_only);
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Print DIMACS file to '<stdout>' for debugging and testing purposes,
|
||||
// including derived units and assumptions. Since it will print in terms
|
||||
// of internal literals it is otherwise not really useful. To write a
|
||||
// DIMACS formula in terms of external variables use 'write_dimacs'.
|
||||
//
|
||||
// require (!INITIALIZING)
|
||||
// ensure (!INITIALIZING)
|
||||
//
|
||||
void dump_cnf ();
|
||||
friend struct DumpCall; // Mobical calls 'dump_cnf' in 'DumpCall::execute'
|
||||
};
|
||||
|
||||
/*========================================================================*/
|
||||
|
||||
// Connected terminators are checked for termination regularly. If the
|
||||
// 'terminate' function of the terminator returns true the solver is
|
||||
// terminated synchronously as soon it calls this function.
|
||||
|
||||
class Terminator {
|
||||
public:
|
||||
virtual ~Terminator () { }
|
||||
virtual bool terminate () = 0;
|
||||
};
|
||||
|
||||
// Connected learners which can be used to export learned clauses.
|
||||
// The 'learning' can check the size of the learn clause and only if it
|
||||
// returns true then the individual literals of the learned clause are given
|
||||
// to the learn through 'learn' one by one terminated by a zero literal.
|
||||
|
||||
class Learner {
|
||||
public:
|
||||
virtual ~Learner () { }
|
||||
virtual bool learning (int size) = 0;
|
||||
virtual void learn (int lit) = 0;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Allows to traverse all remaining irredundant clauses. Satisfied and
|
||||
// eliminated clauses are not included, nor any derived units unless such
|
||||
// a unit literal is frozen. Falsified literals are skipped. If the solver
|
||||
// is inconsistent only the empty clause is traversed.
|
||||
//
|
||||
// If 'clause' returns false traversal aborts early.
|
||||
|
||||
class ClauseIterator {
|
||||
public:
|
||||
virtual ~ClauseIterator () { }
|
||||
virtual bool clause (const std::vector<int> &) = 0;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Allows to traverse all clauses on the extension stack together with their
|
||||
// witness cubes. If the solver is inconsistent, i.e., an empty clause is
|
||||
// found and the formula is unsatisfiable, then nothing is traversed.
|
||||
//
|
||||
// The clauses traversed in 'traverse_clauses' together with the clauses on
|
||||
// the extension stack are logically equivalent to the original clauses.
|
||||
// See our SAT'19 paper for more details.
|
||||
//
|
||||
// The witness literals can be used to extend and fix an assignment on the
|
||||
// remaining clauses to satisfy the clauses on the extension stack too.
|
||||
//
|
||||
// All derived units of non-frozen variables are included too.
|
||||
//
|
||||
// If 'witness' returns false traversal aborts early.
|
||||
|
||||
class WitnessIterator {
|
||||
public:
|
||||
virtual ~WitnessIterator () { }
|
||||
virtual bool witness (const std::vector<int> & clause,
|
||||
const std::vector<int> & witness) = 0;
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
}
|
||||
|
||||
#endif
|
172
hCaD_V2/src/ccadical.cpp
Normal file
172
hCaD_V2/src/ccadical.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
#include "cadical.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct Wrapper : Learner, Terminator {
|
||||
|
||||
Solver * solver;
|
||||
struct {
|
||||
void * state;
|
||||
int (*function) (void *);
|
||||
} terminator;
|
||||
|
||||
struct {
|
||||
void * state;
|
||||
int max_length;
|
||||
int * begin_clause, * end_clause, * capacity_clause;
|
||||
void (*function) (void *, int *);
|
||||
} learner;
|
||||
|
||||
bool terminate () {
|
||||
if (!terminator.function)
|
||||
return false;
|
||||
return terminator.function (terminator.state);
|
||||
}
|
||||
|
||||
bool learning (int size) {
|
||||
if (!learner.function)
|
||||
return false;
|
||||
return size <= learner.max_length;
|
||||
}
|
||||
|
||||
void learn (int lit) {
|
||||
if (learner.end_clause == learner.capacity_clause) {
|
||||
size_t count = learner.end_clause - learner.begin_clause;
|
||||
size_t size = count ? 2*count : 1;
|
||||
learner.begin_clause = (int*)
|
||||
realloc (learner.begin_clause, size * sizeof (int));
|
||||
learner.end_clause = learner.begin_clause + count;
|
||||
learner.capacity_clause = learner.begin_clause + size;
|
||||
}
|
||||
*learner.end_clause++ = lit;
|
||||
if (lit)
|
||||
return;
|
||||
learner.function (learner.state, learner.begin_clause);
|
||||
learner.end_clause = learner.begin_clause;
|
||||
}
|
||||
|
||||
Wrapper () : solver (new Solver ()) {
|
||||
memset (&terminator, 0, sizeof terminator);
|
||||
memset (&learner, 0, sizeof learner);
|
||||
}
|
||||
|
||||
~Wrapper () {
|
||||
terminator.function = 0;
|
||||
if (learner.begin_clause) free (learner.begin_clause);
|
||||
delete solver; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace CaDiCaL;
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "ccadical.h"
|
||||
|
||||
const char * ccadical_signature (void) {
|
||||
return Solver::signature ();
|
||||
}
|
||||
|
||||
CCaDiCaL * ccadical_init (void) {
|
||||
return (CCaDiCaL*) new Wrapper ();
|
||||
}
|
||||
|
||||
void ccadical_release (CCaDiCaL * wrapper) {
|
||||
delete (Wrapper*) wrapper;
|
||||
}
|
||||
|
||||
void ccadical_set_option (CCaDiCaL * wrapper,
|
||||
const char * name, int val) {
|
||||
((Wrapper*) wrapper)->solver->set (name, val);
|
||||
}
|
||||
|
||||
void ccadical_limit (CCaDiCaL * wrapper,
|
||||
const char * name, int val) {
|
||||
((Wrapper*) wrapper)->solver->limit (name, val);
|
||||
}
|
||||
|
||||
int ccadical_get_option (CCaDiCaL * wrapper, const char * name) {
|
||||
return ((Wrapper*) wrapper)->solver->get (name);
|
||||
}
|
||||
|
||||
void ccadical_add (CCaDiCaL * wrapper, int lit) {
|
||||
((Wrapper*) wrapper)->solver->add (lit);
|
||||
}
|
||||
|
||||
void ccadical_assume (CCaDiCaL * wrapper, int lit) {
|
||||
((Wrapper*) wrapper)->solver->assume (lit);
|
||||
}
|
||||
|
||||
int ccadical_solve (CCaDiCaL * wrapper) {
|
||||
return ((Wrapper*) wrapper)->solver->solve ();
|
||||
}
|
||||
|
||||
int ccadical_simplify (CCaDiCaL * wrapper) {
|
||||
return ((Wrapper*) wrapper)->solver->simplify ();
|
||||
}
|
||||
|
||||
int ccadical_val (CCaDiCaL * wrapper, int lit) {
|
||||
return ((Wrapper*) wrapper)->solver->val (lit);
|
||||
}
|
||||
|
||||
int ccadical_failed (CCaDiCaL * wrapper, int lit) {
|
||||
return ((Wrapper*) wrapper)->solver->failed (lit);
|
||||
}
|
||||
|
||||
void ccadical_print_statistics (CCaDiCaL * wrapper) {
|
||||
((Wrapper*) wrapper)->solver->statistics ();
|
||||
}
|
||||
|
||||
void ccadical_terminate (CCaDiCaL * wrapper) {
|
||||
((Wrapper*) wrapper)->solver->terminate ();
|
||||
}
|
||||
|
||||
int64_t ccadical_active (CCaDiCaL * wrapper) {
|
||||
return ((Wrapper*) wrapper)->solver->active ();
|
||||
}
|
||||
|
||||
int64_t ccadical_irredundant (CCaDiCaL * wrapper) {
|
||||
return ((Wrapper*) wrapper)->solver->irredundant ();
|
||||
}
|
||||
|
||||
int ccadical_fixed (CCaDiCaL * wrapper, int lit) {
|
||||
return ((Wrapper*) wrapper)->solver->fixed (lit);
|
||||
}
|
||||
|
||||
void ccadical_set_terminate (CCaDiCaL * ptr,
|
||||
void * state, int (*terminate)(void *)) {
|
||||
Wrapper * wrapper = (Wrapper *) ptr;
|
||||
wrapper->terminator.state = state;
|
||||
wrapper->terminator.function = terminate;
|
||||
if (terminate) wrapper->solver->connect_terminator (wrapper);
|
||||
else wrapper->solver->disconnect_terminator ();
|
||||
}
|
||||
|
||||
void ccadical_set_learn (CCaDiCaL * ptr,
|
||||
void * state, int max_length,
|
||||
void (*learn)(void * state, int * clause)) {
|
||||
Wrapper * wrapper = (Wrapper *) ptr;
|
||||
wrapper->learner.state = state;
|
||||
wrapper->learner.max_length = max_length;
|
||||
wrapper->learner.function = learn;
|
||||
if (learn) wrapper->solver->connect_learner (wrapper);
|
||||
else wrapper->solver->disconnect_learner ();
|
||||
}
|
||||
|
||||
void ccadical_freeze (CCaDiCaL * ptr, int lit) {
|
||||
((Wrapper*) ptr)->solver->freeze (lit);
|
||||
}
|
||||
|
||||
void ccadical_melt (CCaDiCaL * ptr, int lit) {
|
||||
((Wrapper*) ptr)->solver->melt (lit);
|
||||
}
|
||||
|
||||
int ccadical_frozen (CCaDiCaL * ptr, int lit) {
|
||||
return ((Wrapper*) ptr)->solver->frozen (lit);
|
||||
}
|
||||
|
||||
}
|
63
hCaD_V2/src/ccadical.h
Normal file
63
hCaD_V2/src/ccadical.h
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef _ccadical_h_INCLUDED
|
||||
#define _ccadical_h_INCLUDED
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// C wrapper for CaDiCaL's C++ API following IPASIR.
|
||||
|
||||
typedef struct CCaDiCaL CCaDiCaL;
|
||||
|
||||
const char * ccadical_signature (void);
|
||||
CCaDiCaL * ccadical_init (void);
|
||||
void ccadical_release (CCaDiCaL *);
|
||||
|
||||
void ccadical_add (CCaDiCaL *, int lit);
|
||||
void ccadical_assume (CCaDiCaL *, int lit);
|
||||
int ccadical_solve (CCaDiCaL *);
|
||||
int ccadical_val (CCaDiCaL *, int lit);
|
||||
int ccadical_failed (CCaDiCaL *, int lit);
|
||||
|
||||
void ccadical_set_terminate (CCaDiCaL *,
|
||||
void * state, int (*terminate)(void * state));
|
||||
|
||||
void ccadical_set_learn (CCaDiCaL *,
|
||||
void * state, int max_length, void (*learn)(void * state, int * clause));
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Non-IPASIR conformant 'C' functions.
|
||||
|
||||
void ccadical_set_option (CCaDiCaL *, const char * name, int val);
|
||||
void ccadical_limit (CCaDiCaL *, const char * name, int limit);
|
||||
int ccadical_get_option (CCaDiCaL *, const char * name);
|
||||
void ccadical_print_statistics (CCaDiCaL *);
|
||||
int64_t ccadical_active (CCaDiCaL *);
|
||||
int64_t ccadical_irredundant (CCaDiCaL *);
|
||||
int ccadical_fixed (CCaDiCaL *, int lit);
|
||||
void ccadical_terminate (CCaDiCaL *);
|
||||
void ccadical_freeze (CCaDiCaL *, int lit);
|
||||
int ccadical_frozen (CCaDiCaL *, int lit);
|
||||
void ccadical_melt (CCaDiCaL *, int lit);
|
||||
int ccadical_simplify (CCaDiCaL *);
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Support legacy names used before moving to more IPASIR conforming names.
|
||||
|
||||
#define ccadical_reset ccadical_release
|
||||
#define ccadical_sat ccadical_solve
|
||||
#define ccadical_deref ccadical_val
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
555
hCaD_V2/src/checker.cpp
Normal file
555
hCaD_V2/src/checker.cpp
Normal file
@ -0,0 +1,555 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <inttypes.h>
|
||||
}
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
inline unsigned Checker::l2u (int lit) {
|
||||
assert (lit);
|
||||
assert (lit != INT_MIN);
|
||||
unsigned res = 2*(abs (lit) - 1);
|
||||
if (lit < 0) res++;
|
||||
return res;
|
||||
}
|
||||
|
||||
inline signed char Checker::val (int lit) {
|
||||
assert (lit);
|
||||
assert (lit != INT_MIN);
|
||||
assert (abs (lit) < size_vars);
|
||||
assert (vals[lit] == -vals[-lit]);
|
||||
return vals[lit];
|
||||
}
|
||||
|
||||
signed char & Checker::mark (int lit) {
|
||||
const unsigned u = l2u (lit);
|
||||
assert (u < marks.size ());
|
||||
return marks[u];
|
||||
}
|
||||
|
||||
inline CheckerWatcher & Checker::watcher (int lit) {
|
||||
const unsigned u = l2u (lit);
|
||||
assert (u < watchers.size ());
|
||||
return watchers[u];
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
CheckerClause * Checker::new_clause () {
|
||||
const size_t size = simplified.size ();
|
||||
assert (size > 1), assert (size <= UINT_MAX);
|
||||
const size_t bytes = sizeof (CheckerClause) + (size - 2) * sizeof (int);
|
||||
CheckerClause * res = (CheckerClause *) new char [bytes];
|
||||
res->next = 0;
|
||||
res->hash = last_hash;
|
||||
res->size = size;
|
||||
int * literals = res->literals, * p = literals;
|
||||
for (const auto & lit : simplified)
|
||||
*p++ = lit;
|
||||
num_clauses++;
|
||||
|
||||
// First two literals are used as watches and should not be false.
|
||||
//
|
||||
for (unsigned i = 0; i < 2; i++) {
|
||||
int lit = literals[i];
|
||||
if (!val (lit)) continue;
|
||||
for (unsigned j = i + 1; j < size; j++) {
|
||||
int other = literals[j];
|
||||
if (val (other)) continue;
|
||||
swap (literals[i], literals[j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert (!val (literals [0]));
|
||||
assert (!val (literals [1]));
|
||||
watcher (literals[0]).push_back (CheckerWatch (literals[1], res));
|
||||
watcher (literals[1]).push_back (CheckerWatch (literals[0], res));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void Checker::delete_clause (CheckerClause * c) {
|
||||
if (c->size) {
|
||||
assert (c->size > 1);
|
||||
assert (num_clauses);
|
||||
num_clauses--;
|
||||
} else {
|
||||
assert (num_garbage);
|
||||
num_garbage--;
|
||||
}
|
||||
delete [] (char*) c;
|
||||
}
|
||||
|
||||
void Checker::enlarge_clauses () {
|
||||
assert (num_clauses == size_clauses);
|
||||
const uint64_t new_size_clauses = size_clauses ? 2*size_clauses : 1;
|
||||
LOG ("CHECKER enlarging clauses of checker from %" PRIu64 " to %" PRIu64,
|
||||
(uint64_t) size_clauses, (uint64_t) new_size_clauses);
|
||||
CheckerClause ** new_clauses;
|
||||
new_clauses = new CheckerClause * [ new_size_clauses ];
|
||||
clear_n (new_clauses, new_size_clauses);
|
||||
for (uint64_t i = 0; i < size_clauses; i++) {
|
||||
for (CheckerClause * c = clauses[i], * next; c; c = next) {
|
||||
next = c->next;
|
||||
const uint64_t h = reduce_hash (c->hash, new_size_clauses);
|
||||
c->next = new_clauses[h];
|
||||
new_clauses[h] = c;
|
||||
}
|
||||
}
|
||||
delete [] clauses;
|
||||
clauses = new_clauses;
|
||||
size_clauses = new_size_clauses;
|
||||
}
|
||||
|
||||
bool Checker::clause_satisfied (CheckerClause * c) {
|
||||
for (unsigned i = 0; i < c->size; i++)
|
||||
if (val (c->literals[i]) > 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The main reason why we have an explicit garbage collection phase is that
|
||||
// removing clauses from watcher lists eagerly might lead to an accumulated
|
||||
// quadratic algorithm. Thus we delay removing garbage clauses from watcher
|
||||
// lists until garbage collection (even though we remove garbage clauses on
|
||||
// the fly during propagation too). We also remove satisfied clauses.
|
||||
//
|
||||
void Checker::collect_garbage_clauses () {
|
||||
|
||||
stats.collections++;
|
||||
|
||||
for (size_t i = 0; i < size_clauses; i++) {
|
||||
CheckerClause ** p = clauses + i, * c;
|
||||
while ((c = *p)) {
|
||||
if (clause_satisfied (c)) {
|
||||
c->size = 0; // mark as garbage
|
||||
*p = c->next;
|
||||
c->next = garbage;
|
||||
garbage = c;
|
||||
num_garbage++;
|
||||
assert (num_clauses);
|
||||
num_clauses--;
|
||||
} else p = &c->next;
|
||||
}
|
||||
}
|
||||
|
||||
LOG ("CHECKER collecting %" PRIu64 " garbage clauses %.0f%%",
|
||||
num_garbage, percent (num_garbage, num_clauses));
|
||||
|
||||
for (int lit = -size_vars + 1; lit < size_vars; lit++) {
|
||||
if (!lit) continue;
|
||||
CheckerWatcher & ws = watcher (lit);
|
||||
const auto end = ws.end ();
|
||||
auto j = ws.begin (), i = j;
|
||||
for (;i != end; i++) {
|
||||
CheckerWatch & w = *i;
|
||||
if (w.clause->size) *j++ = w;
|
||||
}
|
||||
if (j == ws.end ()) continue;
|
||||
if (j == ws.begin ()) erase_vector (ws);
|
||||
else ws.resize (j - ws.begin ());
|
||||
}
|
||||
|
||||
for (CheckerClause * c = garbage, * next; c; c = next)
|
||||
next = c->next, delete_clause (c);
|
||||
|
||||
assert (!num_garbage);
|
||||
garbage = 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
Checker::Checker (Internal * i)
|
||||
:
|
||||
internal (i),
|
||||
size_vars (0), vals (0),
|
||||
inconsistent (false), num_clauses (0), num_garbage (0),
|
||||
size_clauses (0), clauses (0), garbage (0),
|
||||
next_to_propagate (0), last_hash (0)
|
||||
{
|
||||
LOG ("CHECKER new");
|
||||
|
||||
// Initialize random number table for hash function.
|
||||
//
|
||||
Random random (42);
|
||||
for (unsigned n = 0; n < num_nonces; n++) {
|
||||
uint64_t nonce = random.next ();
|
||||
if (!(nonce & 1)) nonce++;
|
||||
assert (nonce), assert (nonce & 1);
|
||||
nonces[n] = nonce;
|
||||
}
|
||||
|
||||
memset (&stats, 0, sizeof (stats)); // Initialize statistics.
|
||||
}
|
||||
|
||||
Checker::~Checker () {
|
||||
LOG ("CHECKER delete");
|
||||
vals -= size_vars;
|
||||
delete [] vals;
|
||||
for (size_t i = 0; i < size_clauses; i++)
|
||||
for (CheckerClause * c = clauses[i], * next; c; c = next)
|
||||
next = c->next, delete_clause (c);
|
||||
for (CheckerClause * c = garbage, * next; c; c = next)
|
||||
next = c->next, delete_clause (c);
|
||||
delete [] clauses;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The simplicity for accessing 'vals' and 'watchers' directly through a
|
||||
// signed integer literal, comes with the price of slightly more complex
|
||||
// code in deleting and enlarging the checker data structures.
|
||||
|
||||
void Checker::enlarge_vars (int64_t idx) {
|
||||
|
||||
assert (0 < idx), assert (idx <= INT_MAX);
|
||||
|
||||
int64_t new_size_vars = size_vars ? 2*size_vars : 2;
|
||||
while (idx >= new_size_vars) new_size_vars *= 2;
|
||||
LOG ("CHECKER enlarging variables of checker from %" PRId64 " to %" PRId64 "",
|
||||
size_vars, new_size_vars);
|
||||
|
||||
signed char * new_vals;
|
||||
new_vals = new signed char [ 2*new_size_vars ];
|
||||
clear_n (new_vals, 2*new_size_vars);
|
||||
new_vals += new_size_vars;
|
||||
if (size_vars) // To make sanitizer happy (without '-O').
|
||||
memcpy ((void*) (new_vals - size_vars),
|
||||
(void*) (vals - size_vars), 2*size_vars);
|
||||
vals -= size_vars;
|
||||
delete [] vals;
|
||||
vals = new_vals;
|
||||
|
||||
watchers.resize (2*new_size_vars);
|
||||
marks.resize (2*new_size_vars);
|
||||
|
||||
assert (idx < new_size_vars);
|
||||
size_vars = new_size_vars;
|
||||
}
|
||||
|
||||
inline void Checker::import_literal (int lit) {
|
||||
assert (lit);
|
||||
assert (lit != INT_MIN);
|
||||
int idx = abs (lit);
|
||||
if (idx >= size_vars) enlarge_vars (idx);
|
||||
simplified.push_back (lit);
|
||||
unsimplified.push_back (lit);
|
||||
}
|
||||
|
||||
void Checker::import_clause (const vector<int> & c) {
|
||||
for (const auto & lit : c)
|
||||
import_literal (lit);
|
||||
}
|
||||
|
||||
struct lit_smaller {
|
||||
bool operator () (int a, int b) const {
|
||||
int c = abs (a), d = abs (b);
|
||||
if (c < d) return true;
|
||||
if (c > d) return false;
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
bool Checker::tautological () {
|
||||
sort (simplified.begin (), simplified.end (), lit_smaller ());
|
||||
const auto end = simplified.end ();
|
||||
auto j = simplified.begin ();
|
||||
int prev = 0;
|
||||
for (auto i = j; i != end; i++) {
|
||||
int lit = *i;
|
||||
if (lit == prev) continue; // duplicated literal
|
||||
if (lit == -prev) return true; // tautological clause
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) return true; // satisfied literal and clause
|
||||
*j++ = prev = lit;
|
||||
}
|
||||
simplified.resize (j - simplified.begin ());
|
||||
return false;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
uint64_t Checker::reduce_hash (uint64_t hash, uint64_t size) {
|
||||
assert (size > 0);
|
||||
unsigned shift = 32;
|
||||
uint64_t res = hash;
|
||||
while ((((uint64_t)1) << shift) > size) {
|
||||
res ^= res >> shift;
|
||||
shift >>= 1;
|
||||
}
|
||||
res &= size - 1;
|
||||
assert (res < size);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint64_t Checker::compute_hash () {
|
||||
unsigned i = 0, j = 0;
|
||||
uint64_t tmp = 0;
|
||||
for (i = 0; i < simplified.size (); i++) {
|
||||
int lit = simplified[i];
|
||||
tmp += nonces[j++] * (uint64_t) lit;
|
||||
if (j == num_nonces) j = 0;
|
||||
}
|
||||
return last_hash = tmp;
|
||||
}
|
||||
|
||||
CheckerClause ** Checker::find () {
|
||||
stats.searches++;
|
||||
CheckerClause ** res, * c;
|
||||
const uint64_t hash = compute_hash ();
|
||||
const unsigned size = simplified.size ();
|
||||
const uint64_t h = reduce_hash (hash, size_clauses);
|
||||
for (const auto & lit : simplified) mark (lit) = true;
|
||||
for (res = clauses + h; (c = *res); res = &c->next) {
|
||||
if (c->hash == hash && c->size == size) {
|
||||
bool found = true;
|
||||
const int * literals = c->literals;
|
||||
for (unsigned i = 0; found && i != size; i++)
|
||||
found = mark (literals[i]);
|
||||
if (found) break;
|
||||
}
|
||||
stats.collisions++;
|
||||
}
|
||||
for (const auto & lit : simplified) mark (lit) = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
void Checker::insert () {
|
||||
stats.insertions++;
|
||||
if (num_clauses == size_clauses) enlarge_clauses ();
|
||||
const uint64_t h = reduce_hash (compute_hash (), size_clauses);
|
||||
CheckerClause * c = new_clause ();
|
||||
c->next = clauses[h];
|
||||
clauses[h] = c;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
inline void Checker::assign (int lit) {
|
||||
assert (!val (lit));
|
||||
vals[lit] = 1;
|
||||
vals[-lit] = -1;
|
||||
trail.push_back (lit);
|
||||
}
|
||||
|
||||
inline void Checker::assume (int lit) {
|
||||
signed char tmp = val (lit);
|
||||
if (tmp > 0) return;
|
||||
assert (!tmp);
|
||||
stats.assumptions++;
|
||||
assign (lit);
|
||||
}
|
||||
|
||||
void Checker::backtrack (unsigned previously_propagated) {
|
||||
|
||||
assert (previously_propagated <= trail.size ());
|
||||
|
||||
while (trail.size () > previously_propagated) {
|
||||
int lit = trail.back ();
|
||||
assert (val (lit) > 0);
|
||||
assert (val (-lit) < 0);
|
||||
vals[lit] = vals[-lit] = 0;
|
||||
trail.pop_back ();
|
||||
}
|
||||
|
||||
trail.resize (previously_propagated);
|
||||
next_to_propagate = previously_propagated;
|
||||
assert (trail.size () == next_to_propagate);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is a standard propagation routine without using blocking literals
|
||||
// nor without saving the last replacement position.
|
||||
|
||||
bool Checker::propagate () {
|
||||
bool res = true;
|
||||
while (res && next_to_propagate < trail.size ()) {
|
||||
int lit = trail[next_to_propagate++];
|
||||
stats.propagations++;
|
||||
assert (val (lit) > 0);
|
||||
assert (abs (lit) < size_vars);
|
||||
CheckerWatcher & ws = watcher (-lit);
|
||||
const auto end = ws.end ();
|
||||
auto j = ws.begin (), i = j;
|
||||
for (; res && i != end; i++) {
|
||||
CheckerWatch & w = *j++ = *i;
|
||||
const int blit = w.blit;
|
||||
assert (blit != -lit);
|
||||
const signed char blit_val = val (blit);
|
||||
if (blit_val > 0) continue;
|
||||
const unsigned size = w.size;
|
||||
if (size == 2) { // not precise since
|
||||
if (blit_val < 0) res = false; // clause might be garbage
|
||||
else assign (w.blit); // but still sound
|
||||
} else {
|
||||
assert (size > 2);
|
||||
CheckerClause * c = w.clause;
|
||||
if (!c->size) { j--; continue; } // skip garbage clauses
|
||||
assert (size == c->size);
|
||||
int * lits = c->literals;
|
||||
int other = lits[0]^lits[1]^(-lit);
|
||||
assert (other != -lit);
|
||||
signed char other_val = val (other);
|
||||
if (other_val > 0) { j[-1].blit = other; continue; }
|
||||
lits[0] = other, lits[1] = -lit;
|
||||
unsigned k;
|
||||
int replacement = 0;
|
||||
signed char replacement_val = -1;
|
||||
for (k = 2; k < size; k++)
|
||||
if ((replacement_val = val (replacement = lits[k])) >= 0)
|
||||
break;
|
||||
if (replacement_val >= 0) {
|
||||
watcher (replacement).push_back (CheckerWatch (-lit, c));
|
||||
swap (lits[1], lits[k]);
|
||||
j--;
|
||||
} else if (!other_val) assign (other);
|
||||
else res = false;
|
||||
}
|
||||
}
|
||||
while (i != end) *j++ = *i++;
|
||||
ws.resize (j - ws.begin ());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Checker::check () {
|
||||
stats.checks++;
|
||||
if (inconsistent) return true;
|
||||
unsigned previously_propagated = next_to_propagate;
|
||||
for (const auto & lit : simplified)
|
||||
assume (-lit);
|
||||
bool res = !propagate ();
|
||||
backtrack (previously_propagated);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Checker::add_clause (const char * type) {
|
||||
#ifndef LOGGING
|
||||
(void) type;
|
||||
#endif
|
||||
|
||||
int unit = 0;
|
||||
for (const auto & lit : simplified) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp < 0) continue;
|
||||
assert (!tmp);
|
||||
if (unit) { unit = INT_MIN; break; }
|
||||
unit = lit;
|
||||
}
|
||||
|
||||
if (simplified.empty ()) {
|
||||
LOG ("CHECKER added empty %s clause", type);
|
||||
inconsistent = true;
|
||||
} if (!unit) {
|
||||
LOG ("CHECKER added and checked falsified %s clause", type);
|
||||
inconsistent = true;
|
||||
} else if (unit != INT_MIN) {
|
||||
LOG ("CHECKER added and checked %s unit clause %d", type, unit);
|
||||
assign (unit);
|
||||
stats.units++;
|
||||
if (!propagate ()) {
|
||||
LOG ("CHECKER inconsistent after propagating %s unit", type);
|
||||
inconsistent = true;
|
||||
}
|
||||
} else insert ();
|
||||
}
|
||||
|
||||
void Checker::add_original_clause (const vector<int> & c) {
|
||||
if (inconsistent) return;
|
||||
START (checking);
|
||||
LOG (c, "CHECKER addition of original clause");
|
||||
stats.added++;
|
||||
stats.original++;
|
||||
import_clause (c);
|
||||
if (tautological ())
|
||||
LOG ("CHECKER ignoring satisfied original clause");
|
||||
else add_clause ("original");
|
||||
simplified.clear ();
|
||||
unsimplified.clear ();
|
||||
STOP (checking);
|
||||
}
|
||||
|
||||
void Checker::add_derived_clause (const vector<int> & c) {
|
||||
if (inconsistent) return;
|
||||
START (checking);
|
||||
LOG (c, "CHECKER addition of derived clause");
|
||||
stats.added++;
|
||||
stats.derived++;
|
||||
import_clause (c);
|
||||
if (tautological ())
|
||||
LOG ("CHECKER ignoring satisfied derived clause");
|
||||
else if (!check ()) {
|
||||
fatal_message_start ();
|
||||
fputs ("failed to check derived clause:\n", stderr);
|
||||
for (const auto & lit : unsimplified)
|
||||
fprintf (stderr, "%d ", lit);
|
||||
fputc ('0', stderr);
|
||||
fatal_message_end ();
|
||||
} else add_clause ("derived");
|
||||
simplified.clear ();
|
||||
unsimplified.clear ();
|
||||
STOP (checking);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Checker::delete_clause (const vector<int> & c) {
|
||||
if (inconsistent) return;
|
||||
START (checking);
|
||||
LOG (c, "CHECKER checking deletion of clause");
|
||||
stats.deleted++;
|
||||
import_clause (c);
|
||||
if (!tautological ()) {
|
||||
CheckerClause ** p = find (), * d = *p;
|
||||
if (d) {
|
||||
assert (d->size > 1);
|
||||
// Remove from hash table, mark as garbage, connect to garbage list.
|
||||
num_garbage++;
|
||||
assert (num_clauses);
|
||||
num_clauses--;
|
||||
*p = d->next;
|
||||
d->next = garbage;
|
||||
garbage = d;
|
||||
d->size = 0;
|
||||
// If there are enough garbage clauses collect them.
|
||||
if (num_garbage > 0.5 * max ((size_t) size_clauses, (size_t) size_vars))
|
||||
collect_garbage_clauses ();
|
||||
} else {
|
||||
fatal_message_start ();
|
||||
fputs ("deleted clause not in proof:\n", stderr);
|
||||
for (const auto & lit : unsimplified)
|
||||
fprintf (stderr, "%d ", lit);
|
||||
fputc ('0', stderr);
|
||||
fatal_message_end ();
|
||||
}
|
||||
}
|
||||
simplified.clear ();
|
||||
unsimplified.clear ();
|
||||
STOP (checking);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Checker::dump () {
|
||||
int max_var = 0;
|
||||
for (uint64_t i = 0; i < size_clauses; i++)
|
||||
for (CheckerClause * c = clauses[i]; c; c = c->next)
|
||||
for (unsigned i = 0; i < c->size; i++)
|
||||
if (abs (c->literals[i]) > max_var)
|
||||
max_var = abs (c->literals[i]);
|
||||
printf ("p cnf %d %" PRIu64 "\n", max_var, num_clauses);
|
||||
for (uint64_t i = 0; i < size_clauses; i++)
|
||||
for (CheckerClause * c = clauses[i]; c; c = c->next) {
|
||||
for (unsigned i = 0; i < c->size; i++)
|
||||
printf ("%d ", c->literals[i]);
|
||||
printf ("0\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
165
hCaD_V2/src/checker.hpp
Normal file
165
hCaD_V2/src/checker.hpp
Normal file
@ -0,0 +1,165 @@
|
||||
#ifndef _checker_hpp_INCLUDED
|
||||
#define _checker_hpp_INCLUDED
|
||||
|
||||
#include "observer.hpp" // Alphabetically after 'checker'.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This checker implements an online forward DRUP proof checker enabled by
|
||||
// 'opts.checkproof' (requires 'opts.check' also to be enabled). This is
|
||||
// useful for model basted testing (and delta-debugging), where we can not
|
||||
// rely on an external proof checker such as 'drat-trim'. We also do not
|
||||
// have yet a flow for offline incremental proof checking, while this
|
||||
// checker here can also be used in an incremental setting.
|
||||
//
|
||||
// In essence the checker implements is a simple propagation online SAT
|
||||
// solver with an additional hash table to find clauses fast for
|
||||
// 'delete_clause'. It requires its own data structure for clauses
|
||||
// ('CheckerClause') and watches ('CheckerWatch').
|
||||
//
|
||||
// In our experiments the checker slows down overall SAT solving time by a
|
||||
// factor of 3, which we contribute to its slightly less efficient
|
||||
// implementation.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
struct CheckerClause {
|
||||
CheckerClause * next; // collision chain link for hash table
|
||||
uint64_t hash; // previously computed full 64-bit hash
|
||||
unsigned size; // zero if this is a garbage clause
|
||||
int literals[2]; // otherwise 'literals' of length 'size'
|
||||
};
|
||||
|
||||
struct CheckerWatch {
|
||||
int blit;
|
||||
unsigned size;
|
||||
CheckerClause * clause;
|
||||
CheckerWatch () { }
|
||||
CheckerWatch (int b, CheckerClause * c) :
|
||||
blit (b), size (c->size), clause (c)
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef vector<CheckerWatch> CheckerWatcher;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
class Checker : public Observer {
|
||||
|
||||
Internal * internal;
|
||||
|
||||
// Capacity of variable values.
|
||||
//
|
||||
int64_t size_vars;
|
||||
|
||||
// For the assignment we want to have an as fast access as possible and
|
||||
// thus we use an array which can also be indexed by negative literals and
|
||||
// is actually valid in the range [-size_vars+1, ..., size_vars-1].
|
||||
//
|
||||
signed char * vals;
|
||||
|
||||
// The 'watchers' and 'marks' data structures are not that time critical
|
||||
// and thus we access them by first mapping a literal to 'unsigned'.
|
||||
//
|
||||
static unsigned l2u (int lit);
|
||||
vector<CheckerWatcher> watchers; // watchers of literals
|
||||
vector<signed char> marks; // mark bits of literals
|
||||
|
||||
signed char & mark (int lit);
|
||||
CheckerWatcher & watcher (int lit);
|
||||
|
||||
bool inconsistent; // found or added empty clause
|
||||
|
||||
uint64_t num_clauses; // number of clauses in hash table
|
||||
uint64_t num_garbage; // number of garbage clauses
|
||||
uint64_t size_clauses; // size of clause hash table
|
||||
CheckerClause ** clauses; // hash table of clauses
|
||||
CheckerClause * garbage; // linked list of garbage clauses
|
||||
|
||||
vector<int> unsimplified; // original clause for reporting
|
||||
vector<int> simplified; // clause for sorting
|
||||
|
||||
vector<int> trail; // for propagation
|
||||
|
||||
unsigned next_to_propagate; // next to propagate on trail
|
||||
|
||||
void enlarge_vars (int64_t idx);
|
||||
void import_literal (int lit);
|
||||
void import_clause (const vector<int> &);
|
||||
bool tautological ();
|
||||
|
||||
static const unsigned num_nonces = 4;
|
||||
|
||||
uint64_t nonces[num_nonces]; // random numbers for hashing
|
||||
uint64_t last_hash; // last computed hash value of clause
|
||||
uint64_t compute_hash (); // compute and save hash value of clause
|
||||
|
||||
// Reduce hash value to the actual size.
|
||||
//
|
||||
static uint64_t reduce_hash (uint64_t hash, uint64_t size);
|
||||
|
||||
void enlarge_clauses (); // enlarge hash table for clauses
|
||||
void insert (); // insert clause in hash table
|
||||
CheckerClause ** find (); // find clause position in hash table
|
||||
|
||||
void add_clause (const char * type);
|
||||
|
||||
void collect_garbage_clauses ();
|
||||
|
||||
CheckerClause * new_clause ();
|
||||
void delete_clause (CheckerClause *);
|
||||
|
||||
signed char val (int lit); // returns '-1', '0' or '1'
|
||||
|
||||
bool clause_satisfied (CheckerClause*);
|
||||
|
||||
void assign (int lit); // assign a literal to true
|
||||
void assume (int lit); // assume a literal
|
||||
bool propagate (); // propagate and check for conflicts
|
||||
void backtrack (unsigned); // prepare for next clause
|
||||
bool check (); // check simplified clause is implied
|
||||
|
||||
struct {
|
||||
|
||||
int64_t added; // number of added clauses
|
||||
int64_t original; // number of added original clauses
|
||||
int64_t derived; // number of added derived clauses
|
||||
|
||||
int64_t deleted; // number of deleted clauses
|
||||
|
||||
int64_t assumptions; // number of assumed literals
|
||||
int64_t propagations; // number of propagated literals
|
||||
|
||||
int64_t insertions; // number of clauses added to hash table
|
||||
int64_t collisions; // number of hash collisions in 'find'
|
||||
int64_t searches; // number of searched clauses in 'find'
|
||||
|
||||
int64_t checks; // number of implication checks
|
||||
|
||||
int64_t collections; // garbage collections
|
||||
int64_t units;
|
||||
|
||||
} stats;
|
||||
|
||||
public:
|
||||
|
||||
Checker (Internal *);
|
||||
~Checker ();
|
||||
|
||||
// The following three implement the 'Observer' interface.
|
||||
//
|
||||
void add_original_clause (const vector<int> &);
|
||||
void add_derived_clause (const vector<int> &);
|
||||
void delete_clause (const vector<int> &);
|
||||
|
||||
void print_stats ();
|
||||
void dump (); // for debugging purposes only
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
442
hCaD_V2/src/clause.cpp
Normal file
442
hCaD_V2/src/clause.cpp
Normal file
@ -0,0 +1,442 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Signed marking or unmarking of a clause or the global 'clause'.
|
||||
|
||||
void Internal::mark (Clause * c) {
|
||||
for (const auto & lit : *c) mark (lit);
|
||||
}
|
||||
|
||||
void Internal::mark2 (Clause * c) {
|
||||
for (const auto & lit : *c) mark2 (lit);
|
||||
}
|
||||
|
||||
void Internal::unmark (Clause * c) {
|
||||
for (const auto & lit : *c) unmark (lit);
|
||||
}
|
||||
|
||||
void Internal::mark_clause () {
|
||||
for (const auto & lit : clause) mark (lit);
|
||||
}
|
||||
|
||||
void Internal::unmark_clause () {
|
||||
for (const auto & lit : clause) unmark (lit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Mark the variables of an irredundant clause to 'have been removed', which
|
||||
// will trigger these variables to be considered again in the next bounded
|
||||
// variable elimination phase. This is called from 'mark_garbage' below.
|
||||
// Note that 'mark_removed (int lit)' will also mark the blocking flag of
|
||||
// '-lit' to trigger reconsidering blocking clauses on '-lit'.
|
||||
|
||||
void Internal::mark_removed (Clause * c, int except) {
|
||||
LOG (c, "marking removed");
|
||||
assert (!c->redundant);
|
||||
for (const auto & lit : *c)
|
||||
if (lit != except)
|
||||
mark_removed (lit);
|
||||
}
|
||||
|
||||
// Mark the variables of a (redundant or irredundant) clause to 'have been
|
||||
// added', which triggers clauses with such a variables, to be considered
|
||||
// both as a subsumed or subsuming clause in the next subsumption phase.
|
||||
// This function is called from 'new_clause' below as well as in situations
|
||||
// where a clause is shrunken (and thus needs to be at least considered
|
||||
// again to subsume a larger clause). We also use this to tell
|
||||
// 'ternary' preprocessing reconsider clauses on an added literal as well as
|
||||
// trying to block clauses on it.
|
||||
|
||||
inline void Internal::mark_added (int lit, int size, bool redundant) {
|
||||
mark_subsume (lit);
|
||||
if (size == 3)
|
||||
mark_ternary (lit);
|
||||
if (!redundant)
|
||||
mark_block (lit);
|
||||
}
|
||||
|
||||
void Internal::mark_added (Clause * c) {
|
||||
LOG (c, "marking added");
|
||||
assert (likely_to_be_kept_clause (c));
|
||||
for (const auto & lit : *c)
|
||||
mark_added (lit, c->size, c->redundant);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
Clause * Internal::new_clause (bool red, int glue) {
|
||||
|
||||
assert (clause.size () <= (size_t) INT_MAX);
|
||||
const int size = (int) clause.size ();
|
||||
assert (size >= 2);
|
||||
|
||||
if (glue > size) glue = size;
|
||||
|
||||
// Determine whether this clauses should be kept all the time.
|
||||
//
|
||||
bool keep;
|
||||
if (!red) keep = true;
|
||||
else if (glue <= opts.reducetier1glue) keep = true;
|
||||
else keep = false;
|
||||
|
||||
size_t bytes = Clause::bytes (size);
|
||||
Clause * c = (Clause *) new char[bytes];
|
||||
|
||||
stats.added.total++;
|
||||
#ifdef LOGGING
|
||||
c->id = stats.added.total;
|
||||
#endif
|
||||
|
||||
c->conditioned = false;
|
||||
c->covered = false;
|
||||
c->enqueued = false;
|
||||
c->frozen = false;
|
||||
c->garbage = false;
|
||||
c->gate = false;
|
||||
c->hyper = false;
|
||||
c->instantiated = false;
|
||||
c->keep = keep;
|
||||
c->moved = false;
|
||||
c->reason = false;
|
||||
c->redundant = red;
|
||||
c->transred = false;
|
||||
c->subsume = false;
|
||||
c->vivified = false;
|
||||
c->vivify = false;
|
||||
c->used = 0;
|
||||
|
||||
c->glue = glue;
|
||||
c->size = size;
|
||||
c->pos = 2;
|
||||
|
||||
for (int i = 0; i < size; i++) c->literals[i] = clause[i];
|
||||
|
||||
// Just checking that we did not mess up our sophisticated memory layout.
|
||||
// This might be compiler dependent though. Crucial for correctness.
|
||||
//
|
||||
assert (c->bytes () == bytes);
|
||||
|
||||
stats.current.total++;
|
||||
stats.added.total++;
|
||||
|
||||
if (red) {
|
||||
stats.current.redundant++;
|
||||
stats.added.redundant++;
|
||||
} else {
|
||||
stats.irrbytes += bytes;
|
||||
stats.current.irredundant++;
|
||||
stats.added.irredundant++;
|
||||
}
|
||||
|
||||
clauses.push_back (c);
|
||||
LOG (c, "new pointer %p", (void*) c);
|
||||
|
||||
if (likely_to_be_kept_clause (c)) mark_added (c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::promote_clause (Clause * c, int new_glue) {
|
||||
assert (c->redundant);
|
||||
if (c->keep) return;
|
||||
if (c->hyper) return;
|
||||
int old_glue = c->glue;
|
||||
if (new_glue >= old_glue) return;
|
||||
if (!c->keep && new_glue <= opts.reducetier1glue) {
|
||||
LOG (c, "promoting with new glue %d to tier1", new_glue);
|
||||
stats.promoted1++;
|
||||
c->keep = true;
|
||||
} else if (old_glue > opts.reducetier2glue &&
|
||||
new_glue <= opts.reducetier2glue) {
|
||||
LOG (c, "promoting with new glue %d to tier2", new_glue);
|
||||
stats.promoted2++;
|
||||
c->used = 2;
|
||||
} else if (c->keep)
|
||||
LOG (c, "keeping with new glue %d in tier1", new_glue);
|
||||
else if (old_glue <= opts.reducetier2glue)
|
||||
LOG (c, "keeping with new glue %d in tier2", new_glue);
|
||||
else
|
||||
LOG (c, "keeping with new glue %d in tier3", new_glue);
|
||||
stats.improvedglue++;
|
||||
c->glue = new_glue;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Shrinking a clause, e.g., removing one or more literals, requires to fix
|
||||
// the 'pos' field, if it exists and points after the new last literal, has
|
||||
// to adjust the global statistics counter of allocated bytes for
|
||||
// irredundant clauses, and also adjust the glue value of redundant clauses
|
||||
// if the size becomes smaller than the glue. Also mark the literals in the
|
||||
// resulting clause as 'added'. The result is the number of (aligned)
|
||||
// removed bytes, resulting from shrinking the clause.
|
||||
//
|
||||
size_t Internal::shrink_clause (Clause * c, int new_size) {
|
||||
|
||||
assert (new_size >= 2);
|
||||
assert (new_size < c->size);
|
||||
#ifndef NDEBUG
|
||||
for (int i = c->size; i < new_size; i++)
|
||||
c->literals[i] = 0;
|
||||
#endif
|
||||
|
||||
if (c->pos >= new_size) c->pos = 2;
|
||||
|
||||
size_t old_bytes = c->bytes ();
|
||||
c->size = new_size;
|
||||
size_t new_bytes = c->bytes ();
|
||||
size_t res = old_bytes - new_bytes;
|
||||
|
||||
if (c->redundant) promote_clause (c, min (c->size-1, c->glue));
|
||||
else if (old_bytes > new_bytes) {
|
||||
assert (stats.irrbytes >= (int64_t) res);
|
||||
stats.irrbytes -= res;
|
||||
}
|
||||
|
||||
if (likely_to_be_kept_clause (c)) mark_added (c);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
// This is the 'raw' deallocation of a clause. If the clause is in the
|
||||
// arena nothing happens. If the clause is not in the arena its memory is
|
||||
// reclaimed immediately.
|
||||
|
||||
void Internal::deallocate_clause (Clause * c) {
|
||||
char * p = (char*) c;
|
||||
if (arena.contains (p)) return;
|
||||
LOG (c, "deallocate pointer %p", (void*) c);
|
||||
delete [] p;
|
||||
}
|
||||
|
||||
void Internal::delete_clause (Clause * c) {
|
||||
LOG (c, "delete pointer %p", (void*) c);
|
||||
size_t bytes = c->bytes ();
|
||||
stats.collected += bytes;
|
||||
if (c->garbage) {
|
||||
assert (stats.garbage >= (int64_t) bytes);
|
||||
stats.garbage -= bytes;
|
||||
|
||||
// See the discussion in 'propagate' on avoiding to eagerly trace binary
|
||||
// clauses as deleted (produce 'd ...' lines) as soon they are marked
|
||||
// garbage. We avoid this and only trace them as deleted when they are
|
||||
// actually deleted here. This allows the solver to propagate binary
|
||||
// garbage clauses without producing incorrect 'd' lines. The effect
|
||||
// from the proof perspective is that the deletion of these binary
|
||||
// clauses occurs later in the proof file.
|
||||
//
|
||||
if (proof && c->size == 2)
|
||||
proof->delete_clause (c);
|
||||
}
|
||||
deallocate_clause (c);
|
||||
}
|
||||
|
||||
// We want to eagerly update statistics as soon clauses are marked garbage.
|
||||
// Otherwise 'report' for instance gives wrong numbers after 'subsume'
|
||||
// before the next 'reduce'. Thus we factored out marking and accounting
|
||||
// for garbage clauses.
|
||||
//
|
||||
// Eagerly deleting clauses instead is problematic, since references to these
|
||||
// clauses need to be flushed, which is too costly to do eagerly.
|
||||
//
|
||||
// We also update garbage statistics at this point. This helps to
|
||||
// determine whether the garbage collector should be called during for
|
||||
// instance bounded variable elimination, which usually generates lots of
|
||||
// garbage clauses.
|
||||
//
|
||||
// In order not to miss any update to these clause statistics we call
|
||||
// 'check_clause_stats' after garbage collection in debugging mode.
|
||||
//
|
||||
void Internal::mark_garbage (Clause * c) {
|
||||
|
||||
assert (!c->garbage);
|
||||
|
||||
// Delay tracing deletion of binary clauses. See the discussion above in
|
||||
// 'delete_clause' and also in 'propagate'.
|
||||
//
|
||||
if (proof && c->size != 2)
|
||||
proof->delete_clause (c);
|
||||
|
||||
assert (stats.current.total > 0);
|
||||
stats.current.total--;
|
||||
|
||||
size_t bytes = c->bytes ();
|
||||
if (c->redundant) {
|
||||
assert (stats.current.redundant > 0);
|
||||
stats.current.redundant--;
|
||||
} else {
|
||||
assert (stats.current.irredundant > 0);
|
||||
stats.current.irredundant--;
|
||||
assert (stats.irrbytes >= (int64_t) bytes);
|
||||
stats.irrbytes -= bytes;
|
||||
mark_removed (c);
|
||||
}
|
||||
stats.garbage += bytes;
|
||||
c->garbage = true;
|
||||
c->used = 0;
|
||||
|
||||
LOG (c, "marked garbage pointer %p", (void*) c);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Almost the same function as 'search_assign' except that we do not pretend
|
||||
// to learn a new unit clause (which was confusing in log files).
|
||||
|
||||
void Internal::assign_original_unit (int lit) {
|
||||
assert (!level);
|
||||
const int idx = vidx (lit);
|
||||
assert (!vals[idx]);
|
||||
assert (!flags (idx).eliminated ());
|
||||
Var & v = var (idx);
|
||||
v.level = level;
|
||||
v.trail = (int) trail.size ();
|
||||
v.reason = 0;
|
||||
const signed char tmp = sign (lit);
|
||||
vals[idx] = tmp;
|
||||
vals[-idx] = -tmp;
|
||||
assert (val (lit) > 0);
|
||||
assert (val (-lit) < 0);
|
||||
trail.push_back (lit);
|
||||
LOG ("original unit assign %d", lit);
|
||||
mark_fixed (lit);
|
||||
if (propagate ()) return;
|
||||
LOG ("propagation of original unit results in conflict");
|
||||
learn_empty_clause ();
|
||||
}
|
||||
|
||||
// New clause added through the API, e.g., while parsing a DIMACS file.
|
||||
//
|
||||
void Internal::add_new_original_clause () {
|
||||
if (level) backtrack ();
|
||||
LOG (original, "original clause");
|
||||
bool skip = false;
|
||||
if (unsat) {
|
||||
LOG ("skipping clause since formula already inconsistent");
|
||||
skip = true;
|
||||
} else {
|
||||
assert (clause.empty ());
|
||||
for (const auto & lit : original) {
|
||||
int tmp = marked (lit);
|
||||
if (tmp > 0) {
|
||||
LOG ("removing duplicated literal %d", lit);
|
||||
} else if (tmp < 0) {
|
||||
LOG ("tautological since both %d and %d occur", -lit, lit);
|
||||
skip = true;
|
||||
} else {
|
||||
mark (lit);
|
||||
tmp = val (lit);
|
||||
if (tmp < 0) {
|
||||
LOG ("removing falsified literal %d", lit);
|
||||
} else if (tmp > 0) {
|
||||
LOG ("satisfied since literal %d true", lit);
|
||||
skip = true;
|
||||
} else {
|
||||
clause.push_back (lit);
|
||||
assert (flags (lit).status != Flags::UNUSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto & lit : original)
|
||||
unmark (lit);
|
||||
}
|
||||
if (skip) {
|
||||
if (proof) proof->delete_clause (original);
|
||||
} else {
|
||||
size_t size = clause.size ();
|
||||
if (!size) {
|
||||
if (!unsat) {
|
||||
if (!original.size ()) VERBOSE (1, "found empty original clause");
|
||||
else MSG ("found falsified original clause");
|
||||
unsat = true;
|
||||
}
|
||||
} else if (size == 1) {
|
||||
assign_original_unit (clause[0]);
|
||||
} else {
|
||||
Clause * c = new_clause (false);
|
||||
watch_clause (c);
|
||||
}
|
||||
if (original.size () > size) {
|
||||
external->check_learned_clause ();
|
||||
if (proof) {
|
||||
proof->add_derived_clause (clause);
|
||||
proof->delete_clause (original);
|
||||
}
|
||||
}
|
||||
}
|
||||
clause.clear ();
|
||||
}
|
||||
|
||||
// Add learned new clause during conflict analysis and watch it. Requires
|
||||
// that the clause is at least of size 2, and the first two literals
|
||||
// are assigned at the highest decision level.
|
||||
//
|
||||
Clause * Internal::new_learned_redundant_clause (int glue) {
|
||||
assert (clause.size () > 1);
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 2; i < clause.size (); i++)
|
||||
assert (var (clause[0]).level >= var (clause[i]).level),
|
||||
assert (var (clause[1]).level >= var (clause[i]).level);
|
||||
#endif
|
||||
external->check_learned_clause ();
|
||||
Clause * res = new_clause (true, glue);
|
||||
if (proof) proof->add_derived_clause (res);
|
||||
assert (watching ());
|
||||
watch_clause (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Add hyper binary resolved clause during 'probing'.
|
||||
//
|
||||
Clause * Internal::new_hyper_binary_resolved_clause (bool red, int glue) {
|
||||
external->check_learned_clause ();
|
||||
Clause * res = new_clause (red, glue);
|
||||
if (proof) proof->add_derived_clause (res);
|
||||
assert (watching ());
|
||||
watch_clause (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Add hyper ternary resolved clause during 'ternary'.
|
||||
//
|
||||
Clause * Internal::new_hyper_ternary_resolved_clause (bool red) {
|
||||
external->check_learned_clause ();
|
||||
size_t size = clause.size ();
|
||||
Clause * res = new_clause (red, size);
|
||||
if (proof) proof->add_derived_clause (res);
|
||||
assert (!watching ());
|
||||
return res;
|
||||
}
|
||||
|
||||
// Add a new clause with same glue and redundancy as 'orig' but literals are
|
||||
// assumed to be in 'clause' in 'decompose' and 'vivify'.
|
||||
//
|
||||
Clause * Internal::new_clause_as (const Clause * orig) {
|
||||
external->check_learned_clause ();
|
||||
const int new_glue = orig->glue;
|
||||
Clause * res = new_clause (orig->redundant, new_glue);
|
||||
assert (!orig->redundant || !orig->keep || res->keep);
|
||||
if (proof) proof->add_derived_clause (res);
|
||||
assert (watching ());
|
||||
watch_clause (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Add resolved clause during resolution, e.g., bounded variable
|
||||
// elimination, but do not connect its occurrences here.
|
||||
//
|
||||
Clause * Internal::new_resolved_irredundant_clause () {
|
||||
external->check_learned_clause ();
|
||||
Clause * res = new_clause (false);
|
||||
if (proof) proof->add_derived_clause (res);
|
||||
assert (!watching ());
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
151
hCaD_V2/src/clause.hpp
Normal file
151
hCaD_V2/src/clause.hpp
Normal file
@ -0,0 +1,151 @@
|
||||
#ifndef _clause_hpp_INCLUDED
|
||||
#define _clause_hpp_INCLUDED
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
typedef int * literal_iterator;
|
||||
typedef const int * const_literal_iterator;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The 'Clause' data structure is very important. There are usually many
|
||||
// clauses and accessing them is a hot-spot. Thus we use common
|
||||
// optimizations to reduce memory and improve cache usage, even though this
|
||||
// induces some complexity in understanding the code.
|
||||
//
|
||||
// The most important optimization is to 'embed' the actual literals in the
|
||||
// clause. This requires a variadic size structure and thus strictly is not
|
||||
// 'C' conform, but supported by all compilers we used. The alternative is
|
||||
// to store the actual literals somewhere else, which not only needs more
|
||||
// memory but more importantly also requires another memory access and thus
|
||||
// is very costly.
|
||||
|
||||
struct Clause {
|
||||
#ifdef LOGGING
|
||||
int64_t id; // Only useful for debugging.
|
||||
#endif
|
||||
|
||||
bool conditioned:1; // Tried for globally blocked clause elimination.
|
||||
bool covered:1; // Already considered for covered clause elimination.
|
||||
bool enqueued:1; // Enqueued on backward queue.
|
||||
bool frozen:1; // Temporarily frozen (in covered clause elimination).
|
||||
bool garbage:1; // can be garbage collected unless it is a 'reason'
|
||||
bool gate:1 ; // Clause part of a gate (function definition).
|
||||
bool hyper:1; // redundant hyper binary or ternary resolved
|
||||
bool instantiated:1;// tried to instantiate
|
||||
bool keep:1; // always keep this clause (if redundant)
|
||||
bool moved:1; // moved during garbage collector ('copy' valid)
|
||||
bool reason:1; // reason / antecedent clause can not be collected
|
||||
bool redundant:1; // aka 'learned' so not 'irredundant' (original)
|
||||
bool transred:1; // already checked for transitive reduction
|
||||
bool subsume:1; // not checked in last subsumption round
|
||||
unsigned used:2; // resolved in conflict analysis since last 'reduce'
|
||||
bool vivified:1; // clause already vivified
|
||||
bool vivify:1; // clause scheduled to be vivified
|
||||
|
||||
// The glucose level ('LBD' or short 'glue') is a heuristic value for the
|
||||
// expected usefulness of a learned clause, where smaller glue is consider
|
||||
// more useful. During learning the 'glue' is determined as the number of
|
||||
// decisions in the learned clause. Thus the glue of a clause is a strict
|
||||
// upper limit on the smallest number of decisions needed to make it
|
||||
// propagate. For instance a binary clause will propagate if one of its
|
||||
// literals is set to false. Similarly a learned clause with glue 1 can
|
||||
// propagate after one decision, one with glue 2 after 2 decisions etc.
|
||||
// In some sense the glue is an abstraction of the size of the clause.
|
||||
//
|
||||
// See the IJCAI'09 paper by Audemard & Simon for more details. We
|
||||
// switched back and forth between keeping the glue stored in a clause and
|
||||
// using it only initially to determine whether it is kept, that is
|
||||
// survives clause reduction. The latter strategy is not bad but also
|
||||
// does not allow to use glue values for instance in 'reduce'.
|
||||
//
|
||||
// More recently we also update the glue and promote clauses to lower
|
||||
// level tiers during conflict analysis. The idea of using three tiers is
|
||||
// also due to Chanseok Oh and thus used in all recent 'Maple...' solvers.
|
||||
// Tier one are the always kept clauses with low glue at most
|
||||
// 'opts.reducetier1glue' (default '2'). The second tier contains all
|
||||
// clauses with glue larger than 'opts.reducetier1glue' but smaller or
|
||||
// equal than 'opts.reducetier2glue' (default '6'). The third tier
|
||||
// consists of clauses with glue larger than 'opts.reducetier2glue'.
|
||||
//
|
||||
// Clauses in tier one are not deleted in 'reduce'. Clauses in tier
|
||||
// two require to be unused in two consecutive 'reduce' intervals before
|
||||
// being collected while for clauses in tier three not being used since
|
||||
// the last 'reduce' call makes them deletion candidates. Clauses derived
|
||||
// by hyper binary or ternary resolution (even though small and thus with
|
||||
// low glue) are always removed if they remain unused during one interval.
|
||||
// See 'mark_useless_redundant_clauses_as_garbage' in 'reduce.cpp' and
|
||||
// 'bump_clause' in 'analyze.cpp'.
|
||||
//
|
||||
int glue;
|
||||
|
||||
int size; // Actual size of 'literals' (at least 2).
|
||||
int pos; // Position of last watch replacement [Gent'13].
|
||||
|
||||
union {
|
||||
|
||||
int literals[2]; // Of variadic 'size' (shrunken if strengthened).
|
||||
|
||||
Clause * copy; // Only valid if 'moved', then that's where to.
|
||||
//
|
||||
// The 'copy' field is only valid for 'moved' clauses in the moving
|
||||
// garbage collector 'copy_non_garbage_clauses' for keeping clauses
|
||||
// compactly in a contiguous memory arena. Otherwise, most of
|
||||
// the time, 'literals' is valid. See 'collect.cpp' for details.
|
||||
};
|
||||
|
||||
literal_iterator begin () { return literals; }
|
||||
literal_iterator end () { return literals + size; }
|
||||
|
||||
const_literal_iterator begin () const { return literals; }
|
||||
const_literal_iterator end () const { return literals + size; }
|
||||
|
||||
static size_t bytes (int size) {
|
||||
|
||||
// Memory sanitizer insists that clauses put into consecutive memory in
|
||||
// the arena are still 8 byte aligned. We could also allocate 8 byte
|
||||
// aligned memory there. However, assuming the real memory foot print
|
||||
// of a clause is 8 bytes anyhow, we just allocate 8 byte aligned memory
|
||||
// all the time (even if allocated outside of the arena).
|
||||
//
|
||||
assert (size > 1);
|
||||
return align ((size - 2) * sizeof (int) + sizeof (Clause), 8);
|
||||
}
|
||||
|
||||
size_t bytes () const { return bytes (size); }
|
||||
|
||||
// Check whether this clause is ready to be collected and deleted. The
|
||||
// 'reason' flag is only there to protect reason clauses in 'reduce',
|
||||
// which does not backtrack to the root level. If garbage collection is
|
||||
// triggered from a preprocessor, which backtracks to the root level, then
|
||||
// 'reason' is false for sure. We want to use the same garbage collection
|
||||
// code though for both situations and thus hide here this variance.
|
||||
//
|
||||
bool collect () const { return !reason && garbage; }
|
||||
};
|
||||
|
||||
struct clause_smaller_size {
|
||||
bool operator () (const Clause * a, const Clause * b) {
|
||||
return a->size < b->size;
|
||||
}
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Place literals over the same variable close to each other. This would
|
||||
// allow eager removal of identical literals and detection of tautological
|
||||
// clauses but is only currently used for better logging (see also
|
||||
// 'opts.logsort' in 'logging.cpp').
|
||||
|
||||
struct clause_lit_less_than {
|
||||
bool operator () (int a, int b) const {
|
||||
int s = abs (a), t = abs (b);
|
||||
return s < t || (s == t && a < b);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
429
hCaD_V2/src/collect.cpp
Normal file
429
hCaD_V2/src/collect.cpp
Normal file
@ -0,0 +1,429 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Returns the positive number '1' ( > 0) if the given clause is root level
|
||||
// satisfied or the negative number '-1' ( < 0) if it is not root level
|
||||
// satisfied but contains a root level falsified literal. Otherwise, if it
|
||||
// contains neither a satisfied nor falsified literal, then '0' is returned.
|
||||
|
||||
int Internal::clause_contains_fixed_literal (Clause * c) {
|
||||
int satisfied = 0, falsified = 0;
|
||||
for (const auto & lit : *c) {
|
||||
const int tmp = fixed (lit);
|
||||
if (tmp > 0) {
|
||||
LOG (c, "root level satisfied literal %d in", lit);
|
||||
satisfied++;
|
||||
}
|
||||
if (tmp < 0) {
|
||||
LOG (c, "root level falsified literal %d in", lit);
|
||||
falsified++;
|
||||
}
|
||||
}
|
||||
if (satisfied) return 1;
|
||||
else if (falsified) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
// Assume that the clause is not root level satisfied but contains a literal
|
||||
// set to false (root level falsified literal), so it can be shrunken. The
|
||||
// clause data is not actually reallocated at this point to avoid dealing
|
||||
// with issues of special policies for watching binary clauses or whether a
|
||||
// clause is extended or not. Only its size field is adjusted accordingly
|
||||
// after flushing out root level falsified literals.
|
||||
|
||||
void Internal::remove_falsified_literals (Clause * c) {
|
||||
const const_literal_iterator end = c->end ();
|
||||
const_literal_iterator i;
|
||||
int num_non_false = 0;
|
||||
for (i = c->begin (); num_non_false < 2 && i != end; i++)
|
||||
if (fixed (*i) >= 0) num_non_false++;
|
||||
if (num_non_false < 2) return;
|
||||
if (proof) proof->flush_clause (c);
|
||||
literal_iterator j = c->begin ();
|
||||
for (i = j; i != end; i++) {
|
||||
const int lit = *j++ = *i, tmp = fixed (lit);
|
||||
assert (tmp <= 0);
|
||||
if (tmp >= 0) continue;
|
||||
LOG ("flushing %d", lit);
|
||||
j--;
|
||||
}
|
||||
stats.collected += shrink_clause (c, j - c->begin ());
|
||||
}
|
||||
|
||||
// If there are new units (fixed variables) since the last garbage
|
||||
// collection we go over all clauses, mark satisfied ones as garbage and
|
||||
// flush falsified literals. Otherwise if no new units have been generated
|
||||
// since the last garbage collection just skip this step.
|
||||
|
||||
void Internal::mark_satisfied_clauses_as_garbage () {
|
||||
|
||||
if (last.collect.fixed >= stats.all.fixed) return;
|
||||
last.collect.fixed = stats.all.fixed;
|
||||
|
||||
LOG ("marking satisfied clauses and removing falsified literals");
|
||||
|
||||
for (const auto & c : clauses) {
|
||||
if (c->garbage) continue;
|
||||
const int tmp = clause_contains_fixed_literal (c);
|
||||
if (tmp > 0) mark_garbage (c);
|
||||
else if (tmp < 0) remove_falsified_literals (c);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Reason clauses can not be collected.
|
||||
//
|
||||
// We protect reasons before and release protection after garbage collection
|
||||
// (actually within garbage collection).
|
||||
//
|
||||
// For 'reduce' we still need to make sure that all clauses which should not
|
||||
// be removed are marked as such and thus we need to call it before marking
|
||||
// clauses to be flushed.
|
||||
|
||||
void Internal::protect_reasons () {
|
||||
LOG ("protecting reason clauses of all assigned variables on trail");
|
||||
assert (!protected_reasons);
|
||||
size_t count = 0;
|
||||
for (const auto & lit : trail) {
|
||||
if (!active (lit)) continue;
|
||||
assert (val (lit));
|
||||
Var & v = var (lit);
|
||||
assert (v.level > 0);
|
||||
Clause * reason = v.reason;
|
||||
if (!reason) continue;
|
||||
LOG (reason, "protecting assigned %d reason %p", lit, (void*) reason);
|
||||
assert (!reason->reason);
|
||||
reason->reason = true;
|
||||
count++;
|
||||
}
|
||||
LOG ("protected %zd reason clauses referenced on trail", count);
|
||||
protected_reasons = true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// After garbage collection we reset the 'reason' flag of the reasons
|
||||
// of assigned literals on the trail.
|
||||
|
||||
void Internal::unprotect_reasons () {
|
||||
LOG ("unprotecting reasons clauses of all assigned variables on trail");
|
||||
assert (protected_reasons);
|
||||
size_t count = 0;
|
||||
for (const auto & lit : trail) {
|
||||
if (!active (lit)) continue;
|
||||
assert (val (lit));
|
||||
Var & v = var (lit);
|
||||
assert (v.level > 0);
|
||||
Clause * reason = v.reason;
|
||||
if (!reason) continue;
|
||||
LOG (reason, "unprotecting assigned %d reason %p", lit, (void*) reason);
|
||||
assert (reason->reason);
|
||||
reason->reason = false;
|
||||
count++;
|
||||
}
|
||||
LOG ("unprotected %zd reason clauses referenced on trail", count);
|
||||
protected_reasons = false;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Update occurrence lists before deleting garbage clauses in the context of
|
||||
// preprocessing, e.g., during bounded variable elimination 'elim'. The
|
||||
// result is the number of remaining clauses, which in this context means
|
||||
// the number of non-garbage clauses.
|
||||
|
||||
size_t Internal::flush_occs (int lit) {
|
||||
Occs & os = occs (lit);
|
||||
const const_occs_iterator end = os.end ();
|
||||
occs_iterator j = os.begin ();
|
||||
const_occs_iterator i;
|
||||
size_t res = 0;
|
||||
Clause * c;
|
||||
for (i = j; i != end; i++) {
|
||||
c = *i;
|
||||
if (c->collect ()) continue;
|
||||
*j++ = c->moved ? c->copy : c;
|
||||
assert (!c->redundant);
|
||||
res++;
|
||||
}
|
||||
os.resize (j - os.begin ());
|
||||
shrink_occs (os);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Update watch lists before deleting garbage clauses in the context of
|
||||
// 'reduce' where we watch and no occurrence lists. We have to protect
|
||||
// reason clauses not be collected and thus we have this additional check
|
||||
// hidden in 'Clause.collect', which for the root level context of
|
||||
// preprocessing is actually redundant.
|
||||
|
||||
inline void Internal::flush_watches (int lit, Watches & saved) {
|
||||
assert (saved.empty ());
|
||||
Watches & ws = watches (lit);
|
||||
const const_watch_iterator end = ws.end ();
|
||||
watch_iterator j = ws.begin ();
|
||||
const_watch_iterator i;
|
||||
for (i = j; i != end; i++) {
|
||||
Watch w = *i;
|
||||
Clause * c = w.clause;
|
||||
if (c->collect ()) continue;
|
||||
if (c->moved) c = w.clause = c->copy;
|
||||
w.size = c->size;
|
||||
const int new_blit_pos = (c->literals[0] == lit);
|
||||
assert (c->literals[!new_blit_pos] == lit); /*FW1*/
|
||||
w.blit = c->literals[new_blit_pos];
|
||||
if (w.binary ()) *j++ = w;
|
||||
else saved.push_back (w);
|
||||
}
|
||||
ws.resize (j - ws.begin ());
|
||||
for (const auto & w : saved) ws.push_back (w);
|
||||
saved.clear ();
|
||||
shrink_vector (ws);
|
||||
}
|
||||
|
||||
void Internal::flush_all_occs_and_watches () {
|
||||
if (occurring ())
|
||||
for (auto idx : vars)
|
||||
flush_occs (idx), flush_occs (-idx);
|
||||
|
||||
if (watching ()) {
|
||||
Watches tmp;
|
||||
for (auto idx : vars)
|
||||
flush_watches (idx, tmp), flush_watches (-idx, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::update_reason_references () {
|
||||
LOG ("update assigned reason references");
|
||||
size_t count = 0;
|
||||
for (auto & lit : trail) {
|
||||
if (!active (lit)) continue;
|
||||
Var & v = var (lit);
|
||||
Clause * c = v.reason;
|
||||
if (!c) continue;
|
||||
LOG (c, "updating assigned %d reason", lit);
|
||||
assert (c->reason);
|
||||
assert (c->moved);
|
||||
Clause * d = c->copy;
|
||||
v.reason = d;
|
||||
count++;
|
||||
}
|
||||
LOG ("updated %zd assigned reason references", count);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is a simple garbage collector which does not move clauses. It needs
|
||||
// less space than the arena based clause allocator, but is not as cache
|
||||
// efficient, since the copying garbage collector can put clauses together
|
||||
// which are likely accessed after each other.
|
||||
|
||||
void Internal::delete_garbage_clauses () {
|
||||
|
||||
flush_all_occs_and_watches ();
|
||||
|
||||
LOG ("deleting garbage clauses");
|
||||
int64_t collected_bytes = 0, collected_clauses = 0;
|
||||
const auto end = clauses.end ();
|
||||
auto j = clauses.begin (), i = j;
|
||||
while (i != end) {
|
||||
Clause * c = *j++ = *i++;
|
||||
if (!c->collect ()) continue;
|
||||
collected_bytes += c->bytes ();
|
||||
collected_clauses++;
|
||||
delete_clause (c);
|
||||
j--;
|
||||
}
|
||||
clauses.resize (j - clauses.begin ());
|
||||
shrink_vector (clauses);
|
||||
|
||||
PHASE ("collect", stats.collections,
|
||||
"collected %" PRId64 " bytes of %" PRId64 " garbage clauses",
|
||||
collected_bytes, collected_clauses);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is the start of the copying garbage collector using the arena. At
|
||||
// the core is the following function, which copies a clause to the 'to'
|
||||
// space of the arena. Be careful if this clause is a reason of an
|
||||
// assignment. In that case update the reason reference.
|
||||
//
|
||||
void Internal::copy_clause (Clause * c) {
|
||||
LOG (c, "moving");
|
||||
assert (!c->moved);
|
||||
char * p = (char*) c;
|
||||
char * q = arena.copy (p, c->bytes ());
|
||||
c->copy = (Clause *) q;
|
||||
c->moved = true;
|
||||
LOG ("copied clause[%" PRId64 "] from %p to %p",
|
||||
c->id, (void*) c, (void*) c->copy);
|
||||
}
|
||||
|
||||
// This is the moving garbage collector.
|
||||
|
||||
void Internal::copy_non_garbage_clauses () {
|
||||
|
||||
size_t collected_clauses = 0, collected_bytes = 0;
|
||||
size_t moved_clauses = 0, moved_bytes = 0;
|
||||
|
||||
// First determine 'moved_bytes' and 'collected_bytes'.
|
||||
//
|
||||
for (const auto & c : clauses)
|
||||
if (!c->collect ()) moved_bytes += c->bytes (), moved_clauses++;
|
||||
else collected_bytes += c->bytes (), collected_clauses++;
|
||||
|
||||
PHASE ("collect", stats.collections,
|
||||
"moving %zd bytes %.0f%% of %zd non garbage clauses",
|
||||
moved_bytes,
|
||||
percent (moved_bytes, collected_bytes + moved_bytes),
|
||||
moved_clauses);
|
||||
|
||||
// Prepare 'to' space of size 'moved_bytes'.
|
||||
//
|
||||
arena.prepare (moved_bytes);
|
||||
|
||||
// Keep clauses in arena in the same order.
|
||||
//
|
||||
if (opts.arenacompact)
|
||||
for (const auto & c : clauses)
|
||||
if (!c->collect () && arena.contains (c))
|
||||
copy_clause (c);
|
||||
|
||||
if (opts.arenatype == 1 || !watching ()) {
|
||||
|
||||
// Localize according to current clause order.
|
||||
|
||||
// If the option 'opts.arenatype == 1' is set, then this means the
|
||||
// solver uses the original order of clauses. If there are no watches,
|
||||
// we can not use the watched based copying policies below. This
|
||||
// happens if garbage collection is triggered during bounded variable
|
||||
// elimination.
|
||||
|
||||
// Copy clauses according to the order of calling 'copy_clause', which
|
||||
// in essence just gives a compacting garbage collector, since their
|
||||
// relative order is kept, and actually already gives the largest
|
||||
// benefit due to better cache locality.
|
||||
|
||||
for (const auto & c : clauses)
|
||||
if (!c->moved && !c->collect ())
|
||||
copy_clause (c);
|
||||
|
||||
} else if (opts.arenatype == 2) {
|
||||
|
||||
// Localize according to (original) variable order.
|
||||
|
||||
// This is almost the version used by MiniSAT and descendants.
|
||||
// Our version uses saved phases too.
|
||||
|
||||
for (int sign = -1; sign <= 1; sign += 2)
|
||||
for (auto idx : vars)
|
||||
for (const auto & w : watches (sign * likely_phase (idx)))
|
||||
if (!w.clause->moved && !w.clause->collect ())
|
||||
copy_clause (w.clause);
|
||||
|
||||
} else {
|
||||
|
||||
// Localize according to decision queue order.
|
||||
|
||||
// This is the default for search. It allocates clauses in the order of
|
||||
// the decision queue and also uses saved phases. It seems faster than
|
||||
// the MiniSAT version and thus we keep 'opts.arenatype == 3'.
|
||||
|
||||
assert (opts.arenatype == 3);
|
||||
|
||||
for (int sign = -1; sign <= 1; sign += 2)
|
||||
for (int idx = queue.last; idx; idx = link (idx).prev)
|
||||
for (const auto & w : watches (sign * likely_phase (idx)))
|
||||
if (!w.clause->moved && !w.clause->collect ())
|
||||
copy_clause (w.clause);
|
||||
}
|
||||
|
||||
// Do not forget to move clauses which are not watched, which happened in
|
||||
// a rare situation, and now is only left as defensive code.
|
||||
//
|
||||
for (const auto & c : clauses)
|
||||
if (!c->collect () && !c->moved)
|
||||
copy_clause (c);
|
||||
|
||||
flush_all_occs_and_watches ();
|
||||
update_reason_references ();
|
||||
|
||||
// Replace and flush clause references in 'clauses'.
|
||||
//
|
||||
const auto end = clauses.end ();
|
||||
auto j = clauses.begin (), i = j;
|
||||
for (; i != end; i++) {
|
||||
Clause * c = *i;
|
||||
if (c->collect ()) delete_clause (c);
|
||||
else assert (c->moved), *j++ = c->copy, deallocate_clause (c);
|
||||
}
|
||||
clauses.resize (j - clauses.begin ());
|
||||
if (clauses.size () < clauses.capacity ()/2) shrink_vector (clauses);
|
||||
|
||||
if (opts.arenasort)
|
||||
rsort (clauses.begin (), clauses.end (), pointer_rank ());
|
||||
|
||||
// Release 'from' space completely and then swap 'to' with 'from'.
|
||||
//
|
||||
arena.swap ();
|
||||
|
||||
PHASE ("collect", stats.collections,
|
||||
"collected %zd bytes %.0f%% of %zd garbage clauses",
|
||||
collected_bytes,
|
||||
percent (collected_bytes, collected_bytes + moved_bytes),
|
||||
collected_clauses);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Maintaining clause statistics is complex and error prone but necessary
|
||||
// for proper scheduling of garbage collection, particularly during bounded
|
||||
// variable elimination. With this function we can check whether these
|
||||
// statistics are updated correctly.
|
||||
|
||||
void Internal::check_clause_stats () {
|
||||
#ifndef NDEBUG
|
||||
int64_t irredundant = 0, redundant = 0, total = 0, irrbytes = 0;
|
||||
for (const auto & c : clauses) {
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) redundant++; else irredundant++;
|
||||
if (!c->redundant) irrbytes += c->bytes ();
|
||||
total++;
|
||||
}
|
||||
assert (stats.current.irredundant == irredundant);
|
||||
assert (stats.current.redundant == redundant);
|
||||
assert (stats.current.total == total);
|
||||
assert (stats.irrbytes == irrbytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::arenaing () {
|
||||
return opts.arena && (stats.collections > 1);
|
||||
}
|
||||
|
||||
void Internal::garbage_collection () {
|
||||
if (unsat) return;
|
||||
START (collect);
|
||||
report ('G', 1);
|
||||
stats.collections++;
|
||||
mark_satisfied_clauses_as_garbage ();
|
||||
if (!protected_reasons) protect_reasons ();
|
||||
if (arenaing ()) copy_non_garbage_clauses ();
|
||||
else delete_garbage_clauses ();
|
||||
check_clause_stats ();
|
||||
check_var_stats ();
|
||||
unprotect_reasons ();
|
||||
report ('C', 1);
|
||||
STOP (collect);
|
||||
}
|
||||
|
||||
}
|
416
hCaD_V2/src/compact.cpp
Normal file
416
hCaD_V2/src/compact.cpp
Normal file
@ -0,0 +1,416 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Compacting removes holes generated by inactive variables (fixed,
|
||||
// eliminated, substituted or pure) by mapping active variables indices down
|
||||
// to a contiguous interval of indices.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::compacting () {
|
||||
if (level) return false;
|
||||
if (!opts.compact) return false;
|
||||
if (stats.conflicts < lim.compact) return false;
|
||||
int inactive = max_var - active ();
|
||||
assert (inactive >= 0);
|
||||
if (!inactive) return false;
|
||||
if (inactive < opts.compactmin) return false;
|
||||
return inactive >= (1e-3 * opts.compactlim) * max_var;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
struct Mapper {
|
||||
|
||||
Internal * internal;
|
||||
int new_max_var; // New 'max_var' after compacting.
|
||||
int * table; // Old variable index to new literal map.
|
||||
int first_fixed; // First fixed variable index.
|
||||
int map_first_fixed; // Mapped literal of first fixed variable.
|
||||
signed char first_fixed_val; // Value of first fixed variable.
|
||||
size_t new_vsize;
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// We produce a compacting garbage collector like map of old 'src' to
|
||||
// new 'dst' variables. Inactive variables are just skipped except for
|
||||
// fixed ones which will be mapped to the first fixed variable (in the
|
||||
// appropriate phase). This avoids to handle the case 'fixed value'
|
||||
// separately as it is done in Lingeling, where fixed variables are
|
||||
// mapped to the internal variable '1'.
|
||||
//
|
||||
Mapper (Internal * i) :
|
||||
internal (i),
|
||||
new_max_var (0),
|
||||
first_fixed (0),
|
||||
map_first_fixed (0),
|
||||
first_fixed_val (0)
|
||||
{
|
||||
table = new int [ internal->max_var + 1u ];
|
||||
clear_n (table, internal->max_var + 1u);
|
||||
|
||||
assert (!internal->level);
|
||||
|
||||
for (auto src : internal->vars) {
|
||||
const Flags & f = internal->flags (src);
|
||||
if (f.active ()) table[src] = ++new_max_var;
|
||||
else if (f.fixed () && !first_fixed)
|
||||
table[first_fixed = src] = map_first_fixed = ++new_max_var;
|
||||
}
|
||||
|
||||
first_fixed_val = first_fixed ? internal->val (first_fixed) : 0;
|
||||
new_vsize = new_max_var + 1u;
|
||||
}
|
||||
|
||||
~Mapper () { delete [] table; }
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// Map old variable indices. A result of zero means not mapped.
|
||||
//
|
||||
int map_idx (int src) {
|
||||
assert (0 < src);
|
||||
assert (src <= internal->max_var);
|
||||
const int res = table[src];
|
||||
assert (res <= new_max_var);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// The 'map_idx' above is just a look-up into the 'table'. Here we have
|
||||
// to care about signedness of 'src', and in addition that fixed variables
|
||||
// have all to be mapped to the first fixed variable 'first_fixed'.
|
||||
//
|
||||
int map_lit (int src) {
|
||||
int res = map_idx (abs (src));
|
||||
if (!res) {
|
||||
const signed char tmp = internal->val (src);
|
||||
if (tmp) {
|
||||
assert (first_fixed);
|
||||
res = map_first_fixed;
|
||||
if (tmp != first_fixed_val) res = -res;
|
||||
}
|
||||
} else if ((src) < 0) res = -res;
|
||||
assert (abs (res) <= new_max_var);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// Map positive variable indices in vector.
|
||||
//
|
||||
template<class T>
|
||||
void map_vector (vector<T> & v) {
|
||||
for (auto src : internal->vars) {
|
||||
const int dst = map_idx (src);
|
||||
if (!dst) continue;
|
||||
assert (0 < dst);
|
||||
assert (dst <= src);
|
||||
v[dst] = v[src];
|
||||
}
|
||||
v.resize (new_vsize);
|
||||
shrink_vector (v);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// Map positive and negative variable indices in two-sided vector.
|
||||
//
|
||||
template<class T>
|
||||
void map2_vector (vector<T> & v) {
|
||||
for (auto src : internal->vars) {
|
||||
const int dst = map_idx (src);
|
||||
if (!dst) continue;
|
||||
assert (0 < dst);
|
||||
assert (dst <= src);
|
||||
v[2*dst] = v[2*src];
|
||||
v[2*dst + 1] = v[2*src + 1];
|
||||
}
|
||||
v.resize (2*new_vsize);
|
||||
shrink_vector (v);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
// Map a vector of literals, flush inactive literals, then resize and
|
||||
// shrink it to fit the new size after flushing.
|
||||
//
|
||||
void map_flush_and_shrink_lits (vector<int> & v) {
|
||||
const auto end = v.end ();
|
||||
auto j = v.begin (), i = j;
|
||||
for (; i != end; i++) {
|
||||
const int src = *i;
|
||||
int dst = map_idx (abs (src));
|
||||
assert (abs (dst) <= abs (src));
|
||||
if (!dst) continue;
|
||||
if (src < 0) dst = -dst;
|
||||
*j++ = dst;
|
||||
}
|
||||
v.resize (j - v.begin ());
|
||||
shrink_vector (v);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
static signed char * ignore_clang_analyze_memory_leak_warning;
|
||||
|
||||
void Internal::compact () {
|
||||
|
||||
START (compact);
|
||||
|
||||
assert (active () < max_var);
|
||||
|
||||
stats.compacts++;
|
||||
|
||||
assert (!level);
|
||||
assert (!unsat);
|
||||
assert (!conflict);
|
||||
assert (clause.empty ());
|
||||
assert (levels.empty ());
|
||||
assert (analyzed.empty ());
|
||||
assert (minimized.empty ());
|
||||
assert (control.size () == 1);
|
||||
assert (propagated == trail.size ());
|
||||
|
||||
garbage_collection ();
|
||||
|
||||
Mapper mapper (this);
|
||||
|
||||
if (mapper.first_fixed)
|
||||
LOG ("found first fixed %d",
|
||||
sign (mapper.first_fixed_val)*mapper.first_fixed);
|
||||
else LOG ("no variable fixed");
|
||||
|
||||
if (!assumptions.empty ()) {
|
||||
assert (!external->assumptions.empty ());
|
||||
LOG ("temporarily reset internal assumptions");
|
||||
reset_assumptions ();
|
||||
}
|
||||
|
||||
/*======================================================================*/
|
||||
// In this first part we only map stuff without reallocation / shrinking.
|
||||
/*======================================================================*/
|
||||
|
||||
// Flush the external indices. This has to occur before we map 'vals'.
|
||||
//
|
||||
for (auto eidx : external->vars) {
|
||||
int src = external->e2i[eidx];
|
||||
if (!src) continue;
|
||||
int dst = mapper.map_lit (src);
|
||||
LOG ("compact %" PRId64 " maps external %d to internal %d from internal %d",
|
||||
stats.compacts, eidx, dst, src);
|
||||
external->e2i[eidx] = dst;
|
||||
}
|
||||
|
||||
// Map the literals in all clauses.
|
||||
//
|
||||
for (const auto & c : clauses) {
|
||||
assert (!c->garbage);
|
||||
for (auto & src : *c) {
|
||||
assert (!val (src));
|
||||
int dst;
|
||||
dst = mapper.map_lit (src);
|
||||
assert (dst || c->garbage);
|
||||
src = dst;
|
||||
}
|
||||
}
|
||||
|
||||
// Map the blocking literals in all watches.
|
||||
//
|
||||
if (!wtab.empty ())
|
||||
for (auto lit : lits)
|
||||
for (auto & w : watches (lit))
|
||||
w.blit = mapper.map_lit (w.blit);
|
||||
|
||||
// We first flush inactive variables and map the links in the queue. This
|
||||
// has to be done before we map the actual links data structure 'links'.
|
||||
{
|
||||
int prev = 0, mapped_prev = 0, next;
|
||||
for (int idx = queue.first; idx; idx = next) {
|
||||
next = links[idx].next;
|
||||
if (idx == mapper.first_fixed) continue;
|
||||
const int dst = mapper.map_idx (idx);
|
||||
if (!dst) continue;
|
||||
assert (active (idx));
|
||||
if (prev) links[prev].next = dst; else queue.first = dst;
|
||||
links[idx].prev = mapped_prev;
|
||||
mapped_prev = dst;
|
||||
prev = idx;
|
||||
}
|
||||
if (prev) links[prev].next = 0; else queue.first = 0;
|
||||
queue.unassigned = queue.last = mapped_prev;
|
||||
}
|
||||
|
||||
/*======================================================================*/
|
||||
// In the second part we map, flush and shrink arrays.
|
||||
/*======================================================================*/
|
||||
|
||||
mapper.map_flush_and_shrink_lits (trail);
|
||||
propagated = trail.size ();
|
||||
if (mapper.first_fixed) {
|
||||
assert (trail.size () == 1);
|
||||
var (mapper.first_fixed).trail = 0; // before mapping 'vtab'
|
||||
} else assert (trail.empty ());
|
||||
|
||||
if (!probes.empty ())
|
||||
mapper.map_flush_and_shrink_lits (probes);
|
||||
|
||||
/*======================================================================*/
|
||||
// In the third part we map stuff and also reallocate memory.
|
||||
/*======================================================================*/
|
||||
|
||||
// Now we continue in reverse order of allocated bytes, e.g., see
|
||||
// 'Internal::enlarge' which reallocates in order of allocated bytes.
|
||||
|
||||
mapper.map_vector (ftab);
|
||||
mapper.map_vector (parents);
|
||||
mapper.map_vector (marks);
|
||||
mapper.map_vector (phases.saved);
|
||||
mapper.map_vector (phases.forced);
|
||||
mapper.map_vector (phases.target);
|
||||
mapper.map_vector (phases.best);
|
||||
mapper.map_vector (phases.prev);
|
||||
mapper.map_vector (phases.min);
|
||||
|
||||
if(opts.psids) mapper.map2_vector(phases.act);
|
||||
// Special code for 'frozentab'.
|
||||
//
|
||||
for (auto src : vars) {
|
||||
const int dst = mapper.map_idx (src);
|
||||
if (!dst) continue;
|
||||
if (src == dst) continue;
|
||||
assert (dst < src);
|
||||
frozentab[dst] += frozentab[src];
|
||||
frozentab[src] = 0;
|
||||
}
|
||||
frozentab.resize (mapper.new_vsize);
|
||||
shrink_vector (frozentab);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (!external->assumptions.empty ()) {
|
||||
|
||||
for (const auto & elit : external->assumptions) {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
int eidx = abs (elit);
|
||||
assert (eidx <= external->max_var);
|
||||
int ilit = external->e2i[eidx];
|
||||
assert (ilit); // Because we froze all!!!
|
||||
if (elit < 0) ilit = -ilit;
|
||||
assume (ilit);
|
||||
}
|
||||
|
||||
PHASE ("compact", stats.compacts,
|
||||
"reassumed %zd external assumptions",
|
||||
external->assumptions.size ());
|
||||
}
|
||||
|
||||
// Special case for 'val' as for 'val' we trade branch less code for
|
||||
// memory and always allocated an [-maxvar,...,maxvar] array.
|
||||
{
|
||||
signed char * new_vals = new signed char [ 2*mapper.new_vsize ];
|
||||
ignore_clang_analyze_memory_leak_warning = new_vals;
|
||||
new_vals += mapper.new_vsize;
|
||||
for (auto src : vars)
|
||||
new_vals[-mapper.map_idx (src)] = vals[-src];
|
||||
for (auto src : vars)
|
||||
new_vals[mapper.map_idx (src)] = vals[src];
|
||||
new_vals[0] = 0;
|
||||
vals -= vsize;
|
||||
delete [] vals;
|
||||
vals = new_vals;
|
||||
}
|
||||
|
||||
mapper.map_vector (i2e);
|
||||
mapper.map2_vector (ptab);
|
||||
mapper.map_vector (btab);
|
||||
mapper.map_vector (gtab);
|
||||
mapper.map_vector (links);
|
||||
mapper.map_vector (vtab);
|
||||
if (!ntab.empty ()) mapper.map2_vector (ntab);
|
||||
if (!wtab.empty ()) mapper.map2_vector (wtab);
|
||||
if (!otab.empty ()) mapper.map2_vector (otab);
|
||||
if (!big.empty ()) mapper.map2_vector (big);
|
||||
|
||||
/*======================================================================*/
|
||||
// In the fourth part we map the binary heap for scores.
|
||||
/*======================================================================*/
|
||||
|
||||
// The simplest way to map a binary heap is to get all elements from the
|
||||
// heap and reinsert them. This could be slightly improved in terms of
|
||||
// speed if we add a 'flush (int * map)' function to 'Heap', but that is
|
||||
// pretty complicated and would require that the 'Heap' knows that mapped
|
||||
// elements with 'zero' destination should be flushed.
|
||||
|
||||
vector<int> saved;
|
||||
assert (saved.empty ());
|
||||
if (!scores.empty ()) {
|
||||
while (!scores.empty ()) {
|
||||
const int src = scores.front ();
|
||||
scores.pop_front ();
|
||||
const int dst = mapper.map_idx (src);
|
||||
if (!dst) continue;
|
||||
if (src == mapper.first_fixed) continue;
|
||||
saved.push_back (dst);
|
||||
}
|
||||
scores.erase ();
|
||||
}
|
||||
mapper.map_vector (stab);
|
||||
if (!saved.empty ()) {
|
||||
for (const auto & idx : saved)
|
||||
scores.push_back (idx);
|
||||
scores.shrink ();
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
PHASE ("compact", stats.compacts,
|
||||
"reducing internal variables from %d to %d",
|
||||
max_var, mapper.new_max_var);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Need to adjust the target and best assigned counters too.
|
||||
|
||||
size_t new_target_assigned = 0, new_best_assigned = 0;
|
||||
|
||||
for (auto idx : Range (mapper.new_max_var)) {
|
||||
if (phases.target[idx]) new_target_assigned++;
|
||||
if (phases.best[idx]) new_best_assigned++;
|
||||
}
|
||||
|
||||
LOG ("reset target assigned from %zd to %zd",
|
||||
target_assigned, new_target_assigned);
|
||||
LOG ("reset best assigned from %zd to %zd",
|
||||
best_assigned, new_best_assigned);
|
||||
|
||||
target_assigned = new_target_assigned;
|
||||
best_assigned = new_best_assigned;
|
||||
no_conflict_until = 0;
|
||||
|
||||
INIT_EMA (averages.current.trail.fast, opts.ematrailfast);
|
||||
INIT_EMA (averages.current.trail.slow, opts.ematrailslow);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
max_var = mapper.new_max_var;
|
||||
vsize = mapper.new_vsize;
|
||||
|
||||
stats.unused = 0;
|
||||
stats.inactive = stats.now.fixed = mapper.first_fixed ? 1 : 0;
|
||||
stats.now.substituted = stats.now.eliminated = stats.now.pure = 0;
|
||||
|
||||
check_var_stats ();
|
||||
|
||||
int64_t delta = opts.compactint * (stats.compacts + 1);
|
||||
lim.compact = stats.conflicts + delta;
|
||||
|
||||
PHASE ("compact", stats.compacts,
|
||||
"new compact limit %" PRId64 " after %" PRId64 " conflicts",
|
||||
lim.compact, delta);
|
||||
|
||||
STOP (compact);
|
||||
}
|
||||
|
||||
}
|
898
hCaD_V2/src/condition.cpp
Normal file
898
hCaD_V2/src/condition.cpp
Normal file
@ -0,0 +1,898 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Globally blocked clause elimination (which we call here 'conditioning')
|
||||
// is described first in the PhD thesis of Benjamin Kiesl from 2019. An
|
||||
// extended version, which in particular describes the algorithm implemented
|
||||
// below is in our invited ATVA'19 paper [KieslHeuleBiere-ATVA'19]. This
|
||||
// accordingly needs witnesses consisting potentially of more than one
|
||||
// literal. It is the first technique implemented in CaDiCaL with this
|
||||
// feature (PR clause elimination thus should work in principle too).
|
||||
|
||||
// Basically globally blocked clauses are like set blocked clauses, except
|
||||
// that the witness cube (of literals to be flipped during reconstruction)
|
||||
// can contain variables which are not in the blocked clause. This
|
||||
// can simulate some interesting global optimizations like 'headlines' from
|
||||
// the FAN algorithm for ATPG. The technique was actually motivated to
|
||||
// simulate this optimization. It turns out that globally blocked clauses
|
||||
// can be seen as 'conditional autarkies', where in essence the condition
|
||||
// cube is the negation of the globally blocked redundant clause (it
|
||||
// needs to contain one autarky literal though) and the autarky part
|
||||
// represents the witness.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Elimination of globally blocked clauses is first tried in regular
|
||||
// intervals in terms of the number of conflicts. Then the main heuristics
|
||||
// is to trigger 'condition' if the decision level is above the current
|
||||
// moving average of the back jump level.
|
||||
|
||||
// TODO We might need to consider less frequent conditioning.
|
||||
|
||||
bool Internal::conditioning () {
|
||||
|
||||
if (!opts.condition) return false;
|
||||
if (!preprocessing && !opts.inprocessing) return false;
|
||||
if (preprocessing) assert (lim.preprocessing);
|
||||
|
||||
// Triggered in regular 'opts.conditionint' conflict intervals.
|
||||
//
|
||||
if (lim.condition > stats.conflicts) return false;
|
||||
|
||||
if (!level) return false; // One decision necessary.
|
||||
|
||||
if (level <= averages.current.jump) return false; // Main heuristic.
|
||||
|
||||
if (!stats.current.irredundant) return false;
|
||||
double remain = active ();
|
||||
if (!remain) return false;
|
||||
double ratio = stats.current.irredundant / remain;
|
||||
return ratio <= opts.conditionmaxrat;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// We start with the current assignment and then temporarily unassign
|
||||
// literals. They are reassigned afterwards. The global state of the CDCL
|
||||
// solver should not change though. Thus we copied from 'search_unassign'
|
||||
// in 'backtrack.cpp' what is needed to unassign literals and then from
|
||||
// 'search_assign' in 'propagate.cpp' what is needed for reassigning
|
||||
// literals, but restricted the copied code to only updating the actual
|
||||
// assignment (in 'vals') and not changing anything else.
|
||||
|
||||
// We use temporarily unassigning for two purposes. First, if a conditional
|
||||
// literal does not occur negated in a candidate clause it is unassigned.
|
||||
// Second, as a minor optimization, we first unassign all root-level
|
||||
// assigned (fixed) literals, to avoid checking the decision level of
|
||||
// literals during the procedure.
|
||||
|
||||
void Internal::condition_unassign (int lit) {
|
||||
LOG ("condition unassign %d", lit);
|
||||
assert (val (lit) > 0);
|
||||
vals[lit] = vals[-lit] = 0;
|
||||
}
|
||||
|
||||
void Internal::condition_assign (int lit) {
|
||||
LOG ("condition assign %d", lit);
|
||||
assert (!val (lit));
|
||||
vals[lit] = 1;
|
||||
vals[-lit] = -1;
|
||||
assert (val (lit) > 0);
|
||||
assert (val (-lit) < 0);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The current partition into conditional part and autarky part during
|
||||
// refinement is represented through a conditional bit in 'marks'.
|
||||
|
||||
inline bool Internal::is_conditional_literal (int lit) const {
|
||||
return val (lit) > 0 && getbit (lit, 0);
|
||||
}
|
||||
|
||||
inline bool Internal::is_autarky_literal (int lit) const {
|
||||
return val (lit) > 0 && !getbit (lit, 0);
|
||||
}
|
||||
|
||||
inline void Internal::mark_as_conditional_literal (int lit) {
|
||||
LOG ("marking %d as conditional literal", lit);
|
||||
assert (val (lit) > 0);
|
||||
setbit (lit, 0);
|
||||
assert (is_conditional_literal (lit));
|
||||
assert (!is_autarky_literal (lit));
|
||||
}
|
||||
|
||||
inline void Internal::unmark_as_conditional_literal (int lit) {
|
||||
LOG ("unmarking %d as conditional literal", lit);
|
||||
assert (is_conditional_literal (lit));
|
||||
unsetbit (lit, 0);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// We also need to know the literals which are in the current clause. These
|
||||
// are just marked (also in 'marks' but with the (signed) upper two bits).
|
||||
// We need a signed mark here, since we have to distinguish positive and
|
||||
// negative occurrences of literals in the candidate clause.
|
||||
|
||||
inline bool Internal::is_in_candidate_clause (int lit) const {
|
||||
return marked67 (lit) > 0;
|
||||
}
|
||||
|
||||
inline void Internal::mark_in_candidate_clause (int lit) {
|
||||
LOG ("marking %d as literal of the candidate clause", lit);
|
||||
mark67 (lit);
|
||||
assert (is_in_candidate_clause (lit));
|
||||
assert (!is_in_candidate_clause (-lit));
|
||||
}
|
||||
|
||||
inline void Internal::unmark_in_candidate_clause (int lit) {
|
||||
LOG ("unmarking %d as literal of the candidate clause", lit);
|
||||
assert (is_in_candidate_clause (lit));
|
||||
unmark67 (lit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
struct less_conditioned {
|
||||
bool operator () (Clause * a, Clause * b) {
|
||||
return !a->conditioned && b->conditioned;
|
||||
}
|
||||
};
|
||||
|
||||
// This is the function for eliminating globally blocked clauses. It is
|
||||
// triggered during CDCL search according to 'conditioning' above and uses
|
||||
// the current assignment as basis to find globally blocked clauses.
|
||||
|
||||
long Internal::condition_round (long delta) {
|
||||
|
||||
long limit, props = 0;
|
||||
if (LONG_MAX - delta < stats.condprops) limit = LONG_MAX;
|
||||
else limit = stats.condprops + delta;
|
||||
|
||||
size_t initial_trail_level = trail.size ();
|
||||
int initial_level = level;
|
||||
|
||||
LOG ("initial trail level %zd", initial_trail_level);
|
||||
|
||||
protect_reasons ();
|
||||
|
||||
#if defined(LOGGING) || !defined(NDEBUG)
|
||||
int additionally_assigned = 0;
|
||||
#endif
|
||||
|
||||
for (auto idx : vars) {
|
||||
const signed char tmp = val (idx);
|
||||
Var & v = var (idx);
|
||||
if (tmp) {
|
||||
if (v.level) {
|
||||
const int lit = tmp < 0 ? -idx : idx;
|
||||
if (!active (idx)) {
|
||||
LOG ("temporarily unassigning inactive literal %d", lit);
|
||||
condition_unassign (lit);
|
||||
} if (frozen (idx)) {
|
||||
LOG ("temporarily unassigning frozen literal %d", lit);
|
||||
condition_unassign (lit);
|
||||
}
|
||||
}
|
||||
} else if (frozen (idx)) {
|
||||
LOG ("keeping frozen literal %d unassigned", idx);
|
||||
} else if (!active (idx)) {
|
||||
LOG ("keeping inactive literal %d unassigned", idx);
|
||||
} else { // if (preprocessing) {
|
||||
if (initial_level == level) {
|
||||
level++;
|
||||
LOG ("new condition decision level");
|
||||
}
|
||||
const int lit = decide_phase (idx, true);
|
||||
condition_assign (lit);
|
||||
v.level = level;
|
||||
trail.push_back (lit);
|
||||
#if defined(LOGGING) || !defined(NDEBUG)
|
||||
additionally_assigned++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
LOG ("assigned %d additional literals", additionally_assigned);
|
||||
|
||||
// We compute statistics about the size of the assignments.
|
||||
//
|
||||
// The initial assignment consists of the non-root-level assigned literals
|
||||
// split into a conditional and an autarky part. The conditional part
|
||||
// consists of literals assigned true and occurring negated in a clause
|
||||
// (touch the clause), which does not contain another literal assigned to
|
||||
// true. This initial partition is the same for all refinements used in
|
||||
// checking whether a candidate clause is globally blocked.
|
||||
//
|
||||
// For each candidate clause some of the conditional literals have to be
|
||||
// unassigned, and the autarky is shrunken by turning some of the autarky
|
||||
// literals into conditional literals (which might get unassigned in a
|
||||
// later refinement though).
|
||||
//
|
||||
// The fix-point of this procedure produces a final assignment, which
|
||||
// consists of the remaining assigned literals, again split into a
|
||||
// conditional and an autarky part.
|
||||
//
|
||||
struct { size_t assigned, conditional, autarky; } initial, remain;
|
||||
|
||||
initial.assigned = 0;
|
||||
for (auto idx : vars) {
|
||||
const signed char tmp = val (idx);
|
||||
if (!tmp) continue;
|
||||
if (!var (idx).level) continue;
|
||||
LOG ("initial assignment %ds", tmp < 0 ? -idx : idx);
|
||||
initial.assigned++;
|
||||
}
|
||||
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"initial assignment of size %zd", initial.assigned);
|
||||
|
||||
// For each candidate clause we refine the assignment (monotonically),
|
||||
// by unassigning some conditional literals and turning some autarky
|
||||
// literals into conditionals.
|
||||
//
|
||||
// As the conditional part is usually smaller than the autarky part our
|
||||
// implementation only explicitly maintains the initial conditional part,
|
||||
// with conditional bit set to true through 'mark_as_conditional_literal'.
|
||||
// The autarky part consists of all literals assigned true which do not
|
||||
// have their conditional bit set to true. Since in both cases the
|
||||
// literal has to be assigned true, we only need a single bit for both the
|
||||
// literal as well as its negation (it does not have to be 'signed').
|
||||
//
|
||||
vector<int> conditional;
|
||||
|
||||
vector<Clause *> candidates; // Gather candidate clauses.
|
||||
size_t watched = 0; // Number of watched clauses.
|
||||
|
||||
initial.autarky = initial.assigned; // Initially all are in autarky
|
||||
initial.conditional = 0; // and none in conditional part.
|
||||
|
||||
// Upper bound on the number of watched clauses. In principle one could
|
||||
// use 'SIZE_MAX' but this is not available by default (yet).
|
||||
//
|
||||
const size_t size_max = clauses.size () + 1;
|
||||
|
||||
// Initialize additional occurrence lists.
|
||||
//
|
||||
init_occs ();
|
||||
|
||||
// Number of previously conditioned and unconditioned candidates.
|
||||
//
|
||||
size_t conditioned = 0, unconditioned = 0;
|
||||
|
||||
// Now go over all (non-garbage) irredundant clauses and check whether
|
||||
// they are candidates, have to be watched, or whether they force the
|
||||
// negation of some of their literals to be conditional initially.
|
||||
//
|
||||
for (const auto & c : clauses) {
|
||||
if (c->garbage) continue; // Can already be ignored.
|
||||
if (c->redundant) continue; // Ignore redundant clauses too.
|
||||
|
||||
// First determine the following numbers for the candidate clause
|
||||
// (restricted to non-root-level assignments).
|
||||
//
|
||||
int positive = 0; // Number true literals.
|
||||
int negative = 0; // Number false literals.
|
||||
int watch = 0; // True Literal to watch.
|
||||
//
|
||||
size_t minsize = size_max; // Number of occurrences of 'watch'.
|
||||
//
|
||||
// But also ignore root-level satisfied but not yet garbage clauses.
|
||||
//
|
||||
bool satisfied = false; // Root level satisfied.
|
||||
//
|
||||
for (const_literal_iterator l = c->begin ();
|
||||
!satisfied && l != c->end ();
|
||||
l++)
|
||||
{
|
||||
const int lit = *l;
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp && !var (lit).level) satisfied = (tmp > 0);
|
||||
else if (tmp < 0) negative++;
|
||||
else if (tmp > 0) {
|
||||
const size_t size = occs (lit).size ();
|
||||
if (size < minsize) watch = lit, minsize = size;
|
||||
positive++;
|
||||
}
|
||||
}
|
||||
if (satisfied) { // Ignore root-level satisfied clauses.
|
||||
mark_garbage (c); // But mark them as garbage already now.
|
||||
continue; // ... with next clause 'c'.
|
||||
}
|
||||
|
||||
// Candidates are clauses with at least a positive literal in it.
|
||||
//
|
||||
if (positive > 0) {
|
||||
LOG (c, "found %d positive literals in candidate", positive);
|
||||
candidates.push_back (c);
|
||||
if (c->conditioned) conditioned++;
|
||||
else unconditioned++;
|
||||
}
|
||||
|
||||
// Only one positive literal in each clauses with also at least one
|
||||
// negative literal has to be watched in occurrence lists. These
|
||||
// watched clauses will be checked to contain only negative literals as
|
||||
// soon such a positive literal is unassigned. If this is the case
|
||||
// these false literals have to be unassigned and potentially new
|
||||
// conditional literals have to be determined.
|
||||
//
|
||||
// Note that only conditional literals are unassigned. However it does
|
||||
// not matter that we might also watch autarky literals, because either
|
||||
// such an autarky literal remains a witness that the clause is
|
||||
// satisfied as long it remains an autarky literal. Otherwise at one
|
||||
// point it becomes conditional and is unassigned, but then a
|
||||
// replacement watch will be searched.
|
||||
//
|
||||
if (negative > 0 && positive > 0) {
|
||||
LOG (c, "found %d negative literals in candidate", negative);
|
||||
assert (watch);
|
||||
assert (val (watch) > 0);
|
||||
Occs & os = occs (watch);
|
||||
assert (os.size () == minsize);
|
||||
os.push_back (c);
|
||||
watched++;
|
||||
LOG (c, "watching %d with %zd occurrences in", watch, minsize);
|
||||
}
|
||||
|
||||
// The initial global conditional part for the current assignment is
|
||||
// extracted from clauses with only negative literals. It is the same
|
||||
// for all considered candidate clauses. These negative literals make up
|
||||
// the global conditional part, are marked here.
|
||||
//
|
||||
if (negative > 0 && !positive) {
|
||||
|
||||
size_t new_conditionals = 0;
|
||||
|
||||
for (const_literal_iterator l = c->begin ();
|
||||
l != c->end ();
|
||||
l++)
|
||||
{
|
||||
const int lit = *l;
|
||||
signed char tmp = val (lit);
|
||||
if (!tmp) continue;
|
||||
assert (tmp < 0);
|
||||
if (!var (lit).level) continue; // Not unassigned yet!
|
||||
if (is_conditional_literal (-lit)) continue;
|
||||
mark_as_conditional_literal (-lit);
|
||||
conditional.push_back (-lit);
|
||||
new_conditionals++;
|
||||
}
|
||||
if (new_conditionals > 0)
|
||||
LOG (c, "marked %zu negations of literals as conditional in",
|
||||
new_conditionals);
|
||||
|
||||
initial.conditional += new_conditionals;
|
||||
assert (initial.autarky >= new_conditionals);
|
||||
initial.autarky -= new_conditionals;
|
||||
}
|
||||
|
||||
} // End of loop over all clauses to collect candidates etc.
|
||||
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"found %zd candidate clauses", candidates.size ());
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"watching %zu literals and clauses", watched);
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"initially %zd conditional literals %.0f%%",
|
||||
initial.conditional, percent (initial.conditional, initial.assigned));
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"initially %zd autarky literals %.0f%%",
|
||||
initial.autarky, percent (initial.autarky, initial.assigned));
|
||||
#ifdef LOGGING
|
||||
for (size_t i = 0; i < conditional.size (); i++) {
|
||||
LOG ("initial conditional %d", conditional[i]);
|
||||
assert (is_conditional_literal (conditional[i]));
|
||||
}
|
||||
for (size_t i = 0; i < trail.size (); i++)
|
||||
if (is_autarky_literal (trail[i]))
|
||||
LOG ("initial autarky %d", trail[i]);
|
||||
#endif
|
||||
assert (initial.conditional == conditional.size ());
|
||||
assert (initial.assigned == initial.conditional + initial.autarky);
|
||||
|
||||
stats.condassinit += initial.assigned;
|
||||
stats.condcondinit += initial.conditional;
|
||||
stats.condautinit += initial.autarky;
|
||||
stats.condassvars += active ();
|
||||
|
||||
// To speed-up and particularly simplify the code we unassign all
|
||||
// root-level variables temporarily, actually all inactive assigned
|
||||
// variables. This allows us to avoid tests on whether an assigned
|
||||
// literal is actually root-level assigned and thus should be ignored (not
|
||||
// considered to be assigned). For this to work we have to ignore root
|
||||
// level satisfied clauses as done above. These are neither candidates
|
||||
// nor have to be watched. Remaining originally root-level assigned
|
||||
// literals in clauses are only set to false.
|
||||
//
|
||||
for (const auto & lit : trail)
|
||||
if (fixed (lit))
|
||||
condition_unassign (lit);
|
||||
|
||||
// Stack to save temporarily unassigned (conditional) literals.
|
||||
//
|
||||
vector<int> unassigned;
|
||||
|
||||
// Make sure to focus on clauses not tried before by marking clauses which
|
||||
// have been checked before using the 'conditioned' bit of clauses. If all
|
||||
// candidates have their bit set, we have to reset it. Since the
|
||||
// assignment might be completely different then last time and thus also
|
||||
// the set of candidates this method does not really exactly lead to a
|
||||
// round robin scheme of scheduling clauses.
|
||||
//
|
||||
// TODO consider computing conditioned and unconditioned over all clauses.
|
||||
//
|
||||
assert (conditioned + unconditioned == candidates.size ());
|
||||
if (conditioned && unconditioned) {
|
||||
stable_sort (candidates.begin (), candidates.end (), less_conditioned ());
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"focusing on %zd candidates %.0f%% not tried last time",
|
||||
unconditioned, percent (unconditioned, candidates.size ()));
|
||||
} else if (conditioned && !unconditioned) {
|
||||
for (auto const & c : candidates) {
|
||||
assert (c->conditioned);
|
||||
c->conditioned = false; // Reset 'conditioned' bit.
|
||||
}
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"all %zd candidates tried before", conditioned);
|
||||
} else {
|
||||
assert (!conditioned);
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"all %zd candidates are fresh", unconditioned);
|
||||
}
|
||||
|
||||
// TODO prune assignments further!
|
||||
// And thus might result in less watched clauses.
|
||||
// So watching should be done here and not earlier.
|
||||
// Also, see below, we might need to consider the negation of unassigned
|
||||
// literals in candidate clauses as being watched.
|
||||
|
||||
// Now try to block all candidate clauses.
|
||||
//
|
||||
long blocked = 0; // Number of Successfully blocked clauses.
|
||||
//
|
||||
size_t untried = candidates.size ();
|
||||
for (const auto & c : candidates) {
|
||||
|
||||
if (initial.autarky <= 0) break;
|
||||
|
||||
if (c->reason) continue;
|
||||
|
||||
bool terminated_or_limit_hit = true;
|
||||
if (terminated_asynchronously ())
|
||||
LOG ("asynchronous termination detected");
|
||||
else if (stats.condprops >= limit)
|
||||
LOG ("condition propagation limit %ld hit", limit);
|
||||
else terminated_or_limit_hit = false;
|
||||
|
||||
if (terminated_or_limit_hit) {
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"%zd candidates %.0f%% not tried after %ld propagations",
|
||||
untried, percent (untried, candidates.size ()), props);
|
||||
break;
|
||||
}
|
||||
untried--;
|
||||
|
||||
assert (!c->garbage);
|
||||
assert (!c->redundant);
|
||||
|
||||
LOG (c, "candidate");
|
||||
c->conditioned = 1; // Next time later.
|
||||
|
||||
// We watch an autarky literal in the clause, and can stop trying to
|
||||
// globally block the clause as soon it turns into a conditional
|
||||
// literal and we can not find another one. If the fix-point assignment
|
||||
// is reached and we still have an autarky literal left the watched one
|
||||
// is reported as witness for this clause being globally blocked.
|
||||
//
|
||||
int watched_autarky_literal = 0;
|
||||
|
||||
// First mark all true literals in the candidate clause and find an
|
||||
// autarky literal which witnesses that this clause has still a chance
|
||||
// to be globally blocked.
|
||||
//
|
||||
for (const_literal_iterator l = c->begin (); l != c->end (); l++)
|
||||
{
|
||||
const int lit = *l;
|
||||
mark_in_candidate_clause (lit);
|
||||
if (watched_autarky_literal) continue;
|
||||
if (!is_autarky_literal (lit)) continue;
|
||||
watched_autarky_literal = lit;
|
||||
|
||||
// TODO assign non-assigned literals to false?
|
||||
// Which might need to trigger watching additional clauses.
|
||||
}
|
||||
|
||||
if (!watched_autarky_literal) {
|
||||
LOG ("no initial autarky literal found");
|
||||
for (const_literal_iterator l = c->begin (); l != c->end (); l++)
|
||||
unmark_in_candidate_clause (*l);
|
||||
continue;
|
||||
}
|
||||
|
||||
stats.condcands++; // Only now ...
|
||||
|
||||
LOG ("watching first autarky literal %d", watched_autarky_literal);
|
||||
|
||||
// Save assignment sizes for statistics, logging and checking.
|
||||
//
|
||||
remain = initial;
|
||||
|
||||
// Position of next conditional and unassigned literal to process in the
|
||||
// 'conditional' and the 'unassigned' stack.
|
||||
//
|
||||
struct { size_t conditional, unassigned; } next = { 0, 0 };
|
||||
|
||||
assert (unassigned.empty ());
|
||||
assert (conditional.size () == initial.conditional);
|
||||
|
||||
while (watched_autarky_literal &&
|
||||
stats.condprops < limit &&
|
||||
next.conditional < conditional.size ()) {
|
||||
|
||||
assert (next.unassigned == unassigned.size ());
|
||||
|
||||
const int conditional_lit = conditional[next.conditional++];
|
||||
LOG ("processing next conditional %d", conditional_lit);
|
||||
assert (is_conditional_literal (conditional_lit));
|
||||
|
||||
if (is_in_candidate_clause (-conditional_lit)) {
|
||||
LOG ("conditional %d negated in candidate clause", conditional_lit);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG ("conditional %d does not occur negated in candidate clause",
|
||||
conditional_lit);
|
||||
|
||||
condition_unassign (conditional_lit);
|
||||
assert (!is_conditional_literal (conditional_lit));
|
||||
unassigned.push_back (conditional_lit);
|
||||
|
||||
assert (remain.assigned > 0);
|
||||
assert (remain.conditional > 0);
|
||||
remain.conditional--;
|
||||
remain.assigned--;
|
||||
|
||||
while (watched_autarky_literal &&
|
||||
stats.condprops < limit &&
|
||||
next.unassigned < unassigned.size ())
|
||||
{
|
||||
const int unassigned_lit = unassigned[next.unassigned++];
|
||||
LOG ("processing next unassigned %d", unassigned_lit);
|
||||
assert (!val (unassigned_lit));
|
||||
|
||||
props++;
|
||||
stats.condprops++;
|
||||
|
||||
Occs & os = occs (unassigned_lit);
|
||||
if (os.empty ()) continue;
|
||||
|
||||
// Traverse all watched clauses of 'unassigned_lit' and find
|
||||
// replacement watches or if none is found turn the negation of all
|
||||
// false autarky literals in that clause into conditional literals.
|
||||
// If one of those autarky literals is the watched autarky literal
|
||||
// in the candidate clause, that one has to be updated too.
|
||||
//
|
||||
// We expect that this loop is a hot-spot for the procedure and thus
|
||||
// are more careful about accessing end points for iterating.
|
||||
//
|
||||
auto i = os.begin (), j = i;
|
||||
for (;
|
||||
watched_autarky_literal && j != os.end ();
|
||||
j++)
|
||||
{
|
||||
Clause * d = *i++ = *j;
|
||||
|
||||
int replacement = 0; // New watched literal in 'd'.
|
||||
int negative = 0; // Negative autarky literals in 'd'.
|
||||
|
||||
for (const_literal_iterator l = d->begin ();
|
||||
l != d->end ();
|
||||
l++)
|
||||
{
|
||||
const int lit = *l;
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) replacement = lit;
|
||||
if (tmp < 0 && is_autarky_literal (-lit)) negative++;
|
||||
}
|
||||
|
||||
if (replacement) {
|
||||
LOG ("found replacement %d for unassigned %d",
|
||||
replacement, unassigned_lit);
|
||||
LOG (d, "unwatching %d in", unassigned_lit);
|
||||
i--; // Drop watch!
|
||||
LOG (d, "watching %d in", replacement);
|
||||
|
||||
assert (replacement != unassigned_lit);
|
||||
occs (replacement).push_back (d);
|
||||
|
||||
continue; // ... with next watched clause 'd'.
|
||||
}
|
||||
|
||||
LOG ("no replacement found for unassigned %d", unassigned_lit);
|
||||
|
||||
// Keep watching 'd' by 'unassigned_lit' if no replacement found.
|
||||
|
||||
if (!negative) {
|
||||
LOG (d, "no negative autarky literals left in");
|
||||
continue; // ... with next watched clause 'd'.
|
||||
}
|
||||
|
||||
LOG (d, "found %d negative autarky literals in", negative);
|
||||
|
||||
for (const_literal_iterator l = d->begin ();
|
||||
watched_autarky_literal && l != d->end ();
|
||||
l++)
|
||||
{
|
||||
const int lit = *l;
|
||||
if (!is_autarky_literal (-lit)) continue;
|
||||
mark_as_conditional_literal (-lit);
|
||||
conditional.push_back (-lit);
|
||||
|
||||
remain.conditional++;
|
||||
assert (remain.autarky > 0);
|
||||
remain.autarky--;
|
||||
|
||||
if (-lit != watched_autarky_literal) continue;
|
||||
|
||||
LOG ("need to replace autarky literal %d in candidate", -lit);
|
||||
replacement = 0;
|
||||
|
||||
// TODO save starting point because we only move it forward?
|
||||
|
||||
for (const_literal_iterator k = c->begin ();
|
||||
!replacement && k != c->end ();
|
||||
k++)
|
||||
{
|
||||
const int other = *k;
|
||||
if (is_autarky_literal (other)) replacement = other;
|
||||
}
|
||||
watched_autarky_literal = replacement;
|
||||
|
||||
if (replacement) {
|
||||
LOG (c, "watching autarky %d instead %d in candidate",
|
||||
replacement, watched_autarky_literal);
|
||||
watched_autarky_literal = replacement;
|
||||
} else {
|
||||
LOG ("failed to find an autarky replacement");
|
||||
watched_autarky_literal = 0; // Breaks out of 4 loops!!!!!
|
||||
}
|
||||
} // End of loop of turning autarky literals into conditionals.
|
||||
} // End of loop of all watched clauses of an unassigned literal.
|
||||
//
|
||||
// We might abort the occurrence traversal early but already
|
||||
// removed some watches, thus have to just copy the rest.
|
||||
//
|
||||
if (i < j) {
|
||||
while (j != os.end ()) *i++ = *j++;
|
||||
LOG ("flushed %zd occurrences of %d",
|
||||
os.end () - i, unassigned_lit);
|
||||
os.resize (i - os.begin ());
|
||||
}
|
||||
} // End of loop which goes over all unprocessed unassigned literals.
|
||||
} // End of loop which goes over all unprocessed conditional literals.
|
||||
|
||||
// We are still processing the candidate 'c' and now have reached a
|
||||
// final fix-point assignment partitioned into a conditional and an
|
||||
// autarky part, or during unassigned literals figured that there is no
|
||||
// positive autarky literal left in 'c'.
|
||||
|
||||
LOG ("remaining assignment of size %zd", remain.assigned);
|
||||
LOG ("remaining conditional part of size %zd", remain.conditional);
|
||||
LOG ("remaining autarky part of size %zd", remain.autarky);
|
||||
//
|
||||
assert (remain.assigned - remain.conditional == remain.autarky);
|
||||
//
|
||||
#if defined(LOGGING) || !defined(NDEBUG)
|
||||
//
|
||||
// This is a sanity check, that the size of our implicit representation
|
||||
// of the autarky part matches our 'remain' counts. We need the same
|
||||
// code for determining autarky literals as in the loop below which adds
|
||||
// autarky literals to the extension stack.
|
||||
//
|
||||
struct { size_t assigned, conditional, autarky; } check;
|
||||
check.assigned = check.conditional = check.autarky = 0;
|
||||
for (size_t i = 0; i < trail.size (); i++) {
|
||||
const int lit = trail[i];
|
||||
if (val (lit)) {
|
||||
check.assigned++;
|
||||
if (is_conditional_literal (lit)) {
|
||||
LOG ("remaining conditional %d", lit);
|
||||
assert (!is_autarky_literal (lit));
|
||||
check.conditional++;
|
||||
} else {
|
||||
assert (is_autarky_literal (lit));
|
||||
LOG ("remaining autarky %d", lit);
|
||||
check.autarky++;
|
||||
}
|
||||
} else {
|
||||
assert (!is_autarky_literal (lit));
|
||||
assert (!is_conditional_literal (lit));
|
||||
}
|
||||
}
|
||||
assert (remain.assigned == check.assigned);
|
||||
assert (remain.conditional == check.conditional);
|
||||
assert (remain.autarky == check.autarky);
|
||||
#endif
|
||||
|
||||
// Success if an autarky literal is left in the clause and
|
||||
// we did not abort the loop too early because the propagation
|
||||
// limit was hit.
|
||||
//
|
||||
if (watched_autarky_literal && stats.condprops < limit)
|
||||
{
|
||||
assert (is_autarky_literal (watched_autarky_literal));
|
||||
assert (is_in_candidate_clause (watched_autarky_literal));
|
||||
|
||||
blocked++;
|
||||
stats.conditioned++;
|
||||
LOG (c,
|
||||
"positive autarky literal %d globally blocks",
|
||||
watched_autarky_literal);
|
||||
|
||||
LOG ("remaining %zd assigned literals %.0f%%",
|
||||
remain.assigned, percent (remain.assigned, initial.assigned));
|
||||
LOG ("remaining %zd conditional literals %.0f%%",
|
||||
remain.conditional,
|
||||
percent (remain.conditional, remain.assigned));
|
||||
LOG ("remaining %zd autarky literals %.0f%%",
|
||||
remain.autarky, percent (remain.autarky, remain.assigned));
|
||||
|
||||
// A satisfying assignment of a formula after removing a globally
|
||||
// blocked clause might not satisfy that clause. As for variable
|
||||
// elimination and classical blocked clauses, we thus maintain an
|
||||
// extension stack for reconstructing an assignment which both
|
||||
// satisfies the remaining formula as well as the clause.
|
||||
//
|
||||
// For globally blocked clauses we simply have to flip all literals in
|
||||
// the autarky part and thus save the autarky on the extension stack
|
||||
// in addition to the removed clause. In the classical situation (in
|
||||
// bounded variable elimination etc.) we simply save one literal on
|
||||
// the extension stack.
|
||||
//
|
||||
// TODO find a way to shrink the autarky part or some other way to
|
||||
// avoid pushing too many literals on the extension stack.
|
||||
//
|
||||
external->push_zero_on_extension_stack ();
|
||||
for (const auto & lit : trail)
|
||||
if (is_autarky_literal (lit))
|
||||
external->push_witness_literal_on_extension_stack (lit);
|
||||
external->push_clause_on_extension_stack (c);
|
||||
|
||||
mark_garbage (c);
|
||||
|
||||
stats.condassrem += remain.assigned;
|
||||
stats.condcondrem += remain.conditional;
|
||||
stats.condautrem += remain.autarky;
|
||||
stats.condassirem += initial.assigned;
|
||||
}
|
||||
|
||||
// In this last part specific to one candidate clause, we have to get
|
||||
// back to the initial assignment and reset conditionals. First we
|
||||
// assign all the unassigned literals (if necessary).
|
||||
//
|
||||
if (!unassigned.empty ()) {
|
||||
LOG ("reassigning %zd literals", unassigned.size ());
|
||||
while (!unassigned.empty ()) {
|
||||
const int lit = unassigned.back ();
|
||||
unassigned.pop_back ();
|
||||
condition_assign (lit);
|
||||
}
|
||||
}
|
||||
|
||||
// Then we remove from the conditional stack autarky literals which
|
||||
// became conditional and also reset their 'conditional' bit.
|
||||
//
|
||||
if (initial.conditional < conditional.size ()) {
|
||||
LOG ("flushing %zd autarky literals from conditional stack",
|
||||
conditional.size () - initial.conditional);
|
||||
while (initial.conditional < conditional.size ()) {
|
||||
const int lit = conditional.back ();
|
||||
conditional.pop_back ();
|
||||
unmark_as_conditional_literal (lit);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally unmark all literals in the candidate clause.
|
||||
//
|
||||
for (const_literal_iterator l = c->begin (); l != c->end (); l++)
|
||||
unmark_in_candidate_clause (*l);
|
||||
|
||||
} // End of loop over all candidate clauses.
|
||||
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"globally blocked %ld clauses %.0f%%",
|
||||
blocked, percent (blocked, candidates.size ()));
|
||||
|
||||
// Unmark initial conditional variables.
|
||||
//
|
||||
for (const auto & lit : conditional)
|
||||
unmark_as_conditional_literal (lit);
|
||||
|
||||
erase_vector (unassigned);
|
||||
erase_vector (conditional);
|
||||
erase_vector (candidates);
|
||||
|
||||
// Unassign additionally assigned literals.
|
||||
//
|
||||
int additionally_unassigned = 0;
|
||||
while (trail.size () > initial_trail_level) {
|
||||
int lit = trail.back ();
|
||||
trail.pop_back ();
|
||||
condition_unassign (lit);
|
||||
additionally_unassigned++;
|
||||
}
|
||||
LOG ("unassigned %d additionally assigned literals",
|
||||
additionally_unassigned);
|
||||
assert (additionally_unassigned == additionally_assigned);
|
||||
|
||||
if (level > initial_level) {
|
||||
LOG ("reset condition decision level");
|
||||
level = initial_level;
|
||||
}
|
||||
|
||||
reset_occs ();
|
||||
|
||||
// Reassign previously assigned variables again.
|
||||
//
|
||||
LOG ("reassigning previously assigned variables");
|
||||
for (size_t i = 0; i < initial_trail_level; i++) {
|
||||
const int lit = trail[i];
|
||||
const signed char tmp = val (lit);
|
||||
assert (tmp >= 0);
|
||||
if (!tmp) condition_assign (lit);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (const auto & lit : trail)
|
||||
assert (!marked (lit));
|
||||
#endif
|
||||
|
||||
unprotect_reasons ();
|
||||
|
||||
return blocked;
|
||||
}
|
||||
|
||||
void Internal::condition (bool update_limits) {
|
||||
|
||||
if (unsat) return;
|
||||
if (!stats.current.irredundant) return;
|
||||
|
||||
START_SIMPLIFIER (condition, CONDITION);
|
||||
stats.conditionings++;
|
||||
|
||||
// Propagation limit to avoid too much work in 'condition'. We mark
|
||||
// tried candidate clauses after giving up, such that next time we run
|
||||
// 'condition' we can try them.
|
||||
//
|
||||
long limit = stats.propagations.search;
|
||||
limit *= opts.conditionreleff;
|
||||
limit /= 1000;
|
||||
if (limit < opts.conditionmineff) limit = opts.conditionmineff;
|
||||
if (limit > opts.conditionmaxeff) limit = opts.conditionmaxeff;
|
||||
assert (stats.current.irredundant);
|
||||
limit *= 2.0 * active () / (double) stats.current.irredundant;
|
||||
limit = max (limit, 2l* active ());
|
||||
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"started after %" PRIu64 " conflicts limited by %ld propagations",
|
||||
stats.conflicts, limit);
|
||||
|
||||
long blocked = condition_round (limit);
|
||||
|
||||
STOP_SIMPLIFIER (condition, CONDITION);
|
||||
report ('g', !blocked);
|
||||
|
||||
if (!update_limits) return;
|
||||
|
||||
long delta = opts.conditionint * (stats.conditionings + 1);
|
||||
lim.condition = stats.conflicts + delta;
|
||||
|
||||
PHASE ("condition", stats.conditionings,
|
||||
"next limit at %" PRIu64 " after %ld conflicts",
|
||||
lim.condition, delta);
|
||||
}
|
||||
|
||||
}
|
97
hCaD_V2/src/config.cpp
Normal file
97
hCaD_V2/src/config.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
struct NameVal { const char * name; int val; };
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// These are dummy configurations, which require additional code.
|
||||
|
||||
static NameVal default_config[1]; // With '-pedantic' just '[]' or
|
||||
static NameVal plain_config[1]; // '[0]' gave a warning.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Here we have the pre-defined default configurations.
|
||||
|
||||
static NameVal sat_config[] = {
|
||||
{ "elimreleff", 10 },
|
||||
{ "stabilizeonly", 1 },
|
||||
{ "subsumereleff", 60 },
|
||||
};
|
||||
|
||||
static NameVal unsat_config[] = {
|
||||
{ "stabilize", 0 },
|
||||
{ "walk", 0 },
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#define CONFIGS \
|
||||
\
|
||||
CONFIG(default,"set default advanced internal options") \
|
||||
CONFIG(plain,"disable all internal preprocessing options") \
|
||||
CONFIG(sat,"set internal options to target satisfiable instances") \
|
||||
CONFIG(unsat,"set internal options to target unsatisfiable instances") \
|
||||
|
||||
static const char * configs [] = {
|
||||
#define CONFIG(N,D) #N,
|
||||
CONFIGS
|
||||
#undef CONFIG
|
||||
};
|
||||
|
||||
static size_t num_configs = sizeof configs / sizeof *configs;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Config::has (const char * name) {
|
||||
#define CONFIG(N,D) \
|
||||
if (!strcmp (name, #N)) return true;
|
||||
CONFIGS
|
||||
#undef CONFIG
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Config::set (Options & opts, const char * name) {
|
||||
if (!strcmp (name, "default")) {
|
||||
opts.reset_default_values ();
|
||||
return true;
|
||||
}
|
||||
if (!strcmp (name, "plain")) {
|
||||
opts.disable_preprocessing ();
|
||||
return true;
|
||||
}
|
||||
#define CONFIG(N,D) \
|
||||
do { \
|
||||
if (strcmp (name, #N)) break; \
|
||||
const NameVal * BEGIN = N ## _config; \
|
||||
const NameVal * END = BEGIN + sizeof N ##_config / sizeof (NameVal); \
|
||||
for (const NameVal * P = BEGIN; P != END; P++) { \
|
||||
assert (Options::has (P->name)); \
|
||||
opts.set (P->name, P->val); \
|
||||
} \
|
||||
return true; \
|
||||
} while (0);
|
||||
CONFIGS
|
||||
#undef CONFIG
|
||||
return false;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Config::usage () {
|
||||
#define CONFIG(N,D) \
|
||||
printf (" %-14s " D "\n", "--" #N);
|
||||
CONFIGS
|
||||
#undef CONFIG
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
const char ** Config::begin () { return configs; }
|
||||
const char ** Config::end () { return &configs[num_configs]; }
|
||||
|
||||
}
|
20
hCaD_V2/src/config.hpp
Normal file
20
hCaD_V2/src/config.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef _config_hpp_INCLUDED
|
||||
#define _config_hpp_INCLUDED
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
class Options;
|
||||
|
||||
struct Config {
|
||||
|
||||
static bool has (const char *);
|
||||
static bool set (Options &, const char *);
|
||||
static void usage ();
|
||||
|
||||
static const char ** begin ();
|
||||
static const char ** end ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
494
hCaD_V2/src/configure
vendored
Normal file
494
hCaD_V2/src/configure
vendored
Normal file
@ -0,0 +1,494 @@
|
||||
#!/bin/sh
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Run './configure' to produce a 'makefile' in the 'build' sub-directory or
|
||||
# in any immediate sub-directory different from the 'src', 'scripts' and
|
||||
# 'test' directories.
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
rm -f configure.log
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Common default options.
|
||||
|
||||
all=no
|
||||
debug=no
|
||||
logging=no
|
||||
check=no
|
||||
competition=no
|
||||
coverage=no
|
||||
profile=no
|
||||
contracts=yes
|
||||
tracing=yes
|
||||
unlocked=yes
|
||||
pedantic=no
|
||||
options=""
|
||||
quiet=no
|
||||
m32=no
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
if [ -f ./scripts/colors.sh ]
|
||||
then
|
||||
. ./scripts/colors.sh
|
||||
elif [ -f ../scripts/colors.sh ]
|
||||
then
|
||||
. ../scripts/colors.sh
|
||||
else
|
||||
BAD=""
|
||||
HILITE=""
|
||||
BOLD=""
|
||||
NORMAL=""
|
||||
fi
|
||||
|
||||
die () {
|
||||
if [ -f configure.log ]
|
||||
then
|
||||
checklog=" (check also 'configure.log')"
|
||||
else
|
||||
checklog=""
|
||||
fi
|
||||
cecho "${BOLD}configure:${NORMAL} ${BAD}error:${NORMAL} $*${checklog}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
msg () {
|
||||
cecho "${BOLD}configure:${NORMAL} $*"
|
||||
}
|
||||
|
||||
# if we can find the 'color.sh' script source it and overwrite color codes
|
||||
|
||||
for dir in . ..
|
||||
do
|
||||
[ -f $dir/scripts/colors.sh ] || continue
|
||||
. $dir/scripts/colors.sh || exit 1
|
||||
break
|
||||
done
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Parse and handle command line options.
|
||||
|
||||
usage () {
|
||||
cat << EOF
|
||||
usage: configure [ <option> ... ]
|
||||
|
||||
where '<option>' is one of the following
|
||||
|
||||
-h|--help print this command line summary
|
||||
-g|--debug compile with debugging information
|
||||
-c|--check compile with assertion checking (default for '-g')
|
||||
-l|--log[ging] include logging code (but disabled by default)
|
||||
-a|--all short cut for all above, e.g., '-g -l' (thus also '-c')
|
||||
-q|--quiet exclude message and profiling code (logging too)
|
||||
-p|--pedantic add '--pedantic' and '-Werror' compilation flag
|
||||
-s|--symbols add '-ggdb3' (even for optimized compilation)
|
||||
|
||||
--coverage compile with '-ftest-coverage -fprofile-arcs' for 'gcov'
|
||||
--profile compile with '-pg' to profile with 'gprof'
|
||||
|
||||
--no-contracts compile without API contract checking code
|
||||
--no-tracing compile without API call tracing code
|
||||
|
||||
--competition configure for the competition
|
||||
('--quiet', '--no-contracts', '--no-tracing')
|
||||
|
||||
-f... pass '-f<option>[=<val>]' options to the makefile
|
||||
-m32 pass '-m32' to the compiler (compile for 32 bit)
|
||||
-ggdb3 pass '-ggdb3' to makefile (like '-s')
|
||||
-O|-O[123] pass '-O' or '-O[123]' to the makefile
|
||||
-static... pass '-static...' option to makefile
|
||||
|
||||
The environment variable CXX can be used to set a different C++
|
||||
compiler than the default 'g++'. Similarly you can add additional
|
||||
compilation options by setting CXXFLAGS. For example
|
||||
|
||||
CXX=clang++ CXXFLAGS=-fPIC ./configure
|
||||
|
||||
will enforce to use 'clang++' as C++ compiler and also produce
|
||||
position independent code. In order to be shell independent we also
|
||||
allow to have the following form. Thus for instance
|
||||
|
||||
./configure CXX="g++-8" CXXFLAGS="-fPIC -fsanitize=address"
|
||||
|
||||
will have the same effect as
|
||||
|
||||
CXX="g++-8" ./configure -fPIC -fsanitize=address
|
||||
|
||||
The following configuration options might be usefull during porting the
|
||||
code to a new platform and are usually not necessary to change.
|
||||
|
||||
--no-unlocked force compilation without unlocked IO
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
while [ $# -gt 0 ]
|
||||
do
|
||||
case $1 in
|
||||
|
||||
-h|--help) usage;;
|
||||
|
||||
-a|--all) all=yes;;
|
||||
|
||||
-g|--debug) debug=yes;;
|
||||
-c|--check) check=yes;;
|
||||
-l|--log|--logging) logging=yes;;
|
||||
|
||||
-p|--pedantic) pedantic=yes;;
|
||||
-q|--quiet) quiet=yes;;
|
||||
|
||||
--no-contracts | --no-contract) contracts=no;;
|
||||
--no-tracing | --no-trace) tracing=no;;
|
||||
|
||||
--coverage) coverage=yes;;
|
||||
--profile) profile=yes;;
|
||||
|
||||
--competition) competition=yes;;
|
||||
|
||||
--no-unlocked) unlocked=no;;
|
||||
|
||||
-m32) options="$options $1";m32=yes;;
|
||||
-f*|-ggdb3|-O|-O1|-O2|-O3) options="$options $1";;
|
||||
-s|--symbols) options="$options -ggdb3";;
|
||||
-static*) options="$options $1";;
|
||||
|
||||
CXX=*)
|
||||
CXX="`expr \"$1\" : 'CXX=\(.*\)'`"
|
||||
;;
|
||||
|
||||
CXXFLAGS=*)
|
||||
CXXFLAGS="`expr \"$1\" : 'CXXFLAGS=\(.*\)'`"
|
||||
;;
|
||||
|
||||
*) die "invalid option '$1' (try '-h')";;
|
||||
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
if [ $quiet = yes ]
|
||||
then
|
||||
[ $logging = yes ] && die "can not combine '-q' with '-l'"
|
||||
fi
|
||||
|
||||
if [ $all = yes ]
|
||||
then
|
||||
[ $check = yes ] && die "'-a' subsumes '-c'"
|
||||
[ $debug = yes ] && die "'-a' subsumes '-g'"
|
||||
[ $logging = yes ] && die "'-a' subsumes '-l'"
|
||||
check=yes
|
||||
debug=yes
|
||||
logging=yes
|
||||
elif [ $debug = yes ]
|
||||
then
|
||||
[ $check = yes ] && die "'-g' subsumes '-c'"
|
||||
check=yes
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Generate and enter 'build' directory if not already in sub-directory.
|
||||
|
||||
build_in_default_build_sub_directory () {
|
||||
if [ -d build ]
|
||||
then
|
||||
msg "reusing default 'build' directory"
|
||||
else
|
||||
mkdir build 2>/dev/null || \
|
||||
die "failed to generate 'build' directory"
|
||||
msg "making default 'build' directory"
|
||||
fi
|
||||
cd build
|
||||
msg "building in default ${HILITE}'`pwd`'${NORMAL}"
|
||||
build=build
|
||||
}
|
||||
|
||||
if [ -f configure -a -f makefile.in -a -d src ]
|
||||
then
|
||||
root="`pwd`"
|
||||
build_in_default_build_sub_directory
|
||||
elif [ -f ../configure -a -f ../makefile.in -a -d ../src ]
|
||||
then
|
||||
cwd="`pwd`"
|
||||
build=`basename "$cwd"`
|
||||
root=`dirname "$cwd"`
|
||||
case x"$build" in
|
||||
xsrc|xtest|xscripts)
|
||||
cd ..
|
||||
build_in_default_build_sub_directory
|
||||
;;
|
||||
*)
|
||||
msg "building in ${HILITE}'$build'${NORMAL} sub-directory"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
die "call 'configure' from root of CaDiCaL source or a sub-directory"
|
||||
fi
|
||||
|
||||
msg "root directory '$root'"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
src="$root/src"
|
||||
|
||||
if [ -d "$src" ]
|
||||
then
|
||||
msg "source directory '$src'"
|
||||
else
|
||||
die "could not find source director '$src'"
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Prepare '@CXX@' and '@CXXFLAGS@' parameters for 'makefile.in'
|
||||
|
||||
[ x"$CXX" = x ] && CXX=g++
|
||||
[ x"$CXXFLAGS" = x ] || CXXFLAGS="$CXXFLAGS "
|
||||
|
||||
case x"$CXX" in
|
||||
x*g++*|x*clang++*) CXXFLAGS="${CXXFLAGS}-Wall -Wextra";;
|
||||
*) CXXFLAGS="${CXXFLAGS}-W";;
|
||||
esac
|
||||
|
||||
if [ $debug = yes ]
|
||||
then
|
||||
CXXFLAGS="$CXXFLAGS -g"
|
||||
else
|
||||
case x"$CXX" in
|
||||
x*g++*|x*clang++*) CXXFLAGS="$CXXFLAGS -O3";;
|
||||
*) CXXFLAGS="$CXXFLAGS -O";;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ $m32 = yes ]
|
||||
then
|
||||
case x"$CXX" in
|
||||
x*g++*)
|
||||
options="$options -mpc64"
|
||||
msg "forcing portable 64-bit FPU mode ('-mpc64') for '$CXX'"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ $competition = yes ]
|
||||
then
|
||||
quiet=yes
|
||||
contracts=no
|
||||
tracing=no
|
||||
fi
|
||||
|
||||
[ $check = no ] && CXXFLAGS="$CXXFLAGS -DNDEBUG"
|
||||
[ $logging = yes ] && CXXFLAGS="$CXXFLAGS -DLOGGING"
|
||||
[ $quiet = yes ] && CXXFLAGS="$CXXFLAGS -DQUIET"
|
||||
[ $profile = yes ] && CXXFLAGS="$CXXFLAGS -pg"
|
||||
[ $coverage = yes ] && CXXFLAGS="$CXXFLAGS -ftest-coverage -fprofile-arcs"
|
||||
if [ $pedantic = yes ]
|
||||
then
|
||||
CXXFLAGS="$CXXFLAGS --pedantic -Werror -std=c++11"
|
||||
case x"$CXX" in
|
||||
x*g++*|x*clang++*) CXXFLAGS="${CXXFLAGS} -Wp,-D_GLIBCXX_ASSERTIONS"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
[ $contracts = no ] && CXXFLAGS="$CXXFLAGS -DNCONTRACTS"
|
||||
[ $tracing = no ] && CXXFLAGS="$CXXFLAGS -DNTRACING"
|
||||
|
||||
CXXFLAGS="$CXXFLAGS$options"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
case x"$CXX" in
|
||||
x*g++* | x*clang++*) WERROR="-Werror -pedantic";;
|
||||
*) WERROR="";;
|
||||
esac
|
||||
|
||||
# Check that compilation flags work.
|
||||
|
||||
feature=./configure-hello-world
|
||||
cat <<EOF > $feature.cpp
|
||||
#include <iostream>
|
||||
int main () { std::cout << "hello world" << std::endl; }
|
||||
EOF
|
||||
if $CXX $CXXFLAGS $WERROR -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if [ ! "`$feature.exe 2>>configure.log|tr -d '\r'`" = "hello world" ]
|
||||
then
|
||||
die "execution of '$feature.exe' failed"
|
||||
fi
|
||||
else
|
||||
die "test compilation '$feature.cpp'"
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Since C99/C++11 is becoming the standard newer versions of 'g++' (7.3 for
|
||||
# instance) discourage certain GCC extensions, particularly the GCC version
|
||||
# of variadic macros, if the same concept exists in the standard. This
|
||||
# forced us to replace all GCC style 'ARGS...' macros with '...' and
|
||||
# '__VA_ARGS__'. Otherwise compiling the library with '--pedantic -Werror'
|
||||
# would fail (configuration flag '-p'). However, older versions of 'gcc'
|
||||
# (such as 4.8) would disallow these new forms of macros unless we
|
||||
# explicitly enforce the new standard with '-std=c++11'. Here we try to
|
||||
# figure out whether we need that flag. In earlier versions we used
|
||||
# '-std=c++0x' but due to issues with older MacOS builds we switched to
|
||||
# '-std=c++11' instead.
|
||||
|
||||
feature=./configure-requires-c++11
|
||||
cat <<EOF > $feature.cpp
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
// old variadic macro usage 'ARGS...' / '#ARGS' discouraged in g++-7...
|
||||
// new variadic macro usage '...' / '__VA_ARGS__' available in C99/C++11
|
||||
//
|
||||
#define MACRO(FMT, ...) printf (FMT "\n", __VA_ARGS__)
|
||||
|
||||
// we use ranged for loops which became available in gcc 4.6 and for
|
||||
// the gcc 4.6 as well as 4.8 requires '-std=c++11' too.
|
||||
//
|
||||
unsigned f (const std::vector<unsigned> & a) {
|
||||
unsigned res = 0;
|
||||
for (auto i : a) res += i;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int main () { MACRO ("%d", 42); return 0; }
|
||||
EOF
|
||||
if $CXX $CXXFLAGS $WERROR -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if [ "`$feature.exe 2>>configure.log|tr -d '\r'`" = 42 ]
|
||||
then
|
||||
msg "compiler supports all required C99/C++11 extensions"
|
||||
else
|
||||
die "checking compilation without '-std=c++11' failed"
|
||||
fi
|
||||
else
|
||||
CXXFLAGS="$CXXFLAGS -std=c++11"
|
||||
if $CXX $CXXFLAGS -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if [ "`$feature.exe 2>>configure.log|tr -d '\r'`" = 42 ]
|
||||
then
|
||||
msg "using '-std=c++11' for all required C99/C++11 extensions"
|
||||
else
|
||||
die "checking compilation with '-std=c++11' failed"
|
||||
fi
|
||||
else
|
||||
die "compiler does not support C99/C++11 even with '-std=c++11'"
|
||||
fi
|
||||
fi
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Unlocked IO is much faster but not necessarily supported.
|
||||
|
||||
if [ $unlocked = yes ]
|
||||
then
|
||||
feature=./configure-have-unlocked-io
|
||||
cat <<EOF > $feature.cpp
|
||||
#include <cstdio>
|
||||
int main () {
|
||||
const char * path = "$feature.log";
|
||||
FILE * file = fopen (path, "w");
|
||||
if (!file) return 1;
|
||||
if (putc_unlocked (42, file) != 42) return 1;
|
||||
if (fclose (file)) return 1;
|
||||
file = fopen (path, "r");
|
||||
if (!file) return 1;
|
||||
if (getc_unlocked (file) != 42) return 1;
|
||||
if (fclose (file)) return 1;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if $CXX $CXXFLAGS -o $feature.exe $feature.cpp 2>>configure.log
|
||||
then
|
||||
if $feature.exe
|
||||
then
|
||||
msg "unlocked IO with '{putc,getc}_unlocked' seems to work"
|
||||
else
|
||||
msg "not using unlocked IO (running '$feature.exe' failed)"
|
||||
unlocked=no
|
||||
fi
|
||||
else
|
||||
msg "not using unlocked IO (failed to compile '$feature.cpp')"
|
||||
unlocked=no
|
||||
fi
|
||||
else
|
||||
msg "not using unlocked IO (since '--no-unlocked' specified)"
|
||||
fi
|
||||
|
||||
[ $unlocked = no ] && CXXFLAGS="$CXXFLAGS -DNUNLOCKED"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
# Instantiate '../makefile.in' template to produce 'makefile' in 'build'.
|
||||
|
||||
msg "compiling with ${HILITE}'$CXX $CXXFLAGS'${NORMAL}"
|
||||
|
||||
rm -f makefile
|
||||
sed \
|
||||
-e "2c\\
|
||||
# This 'makefile' is generated from '../makefile.in'." \
|
||||
-e "s,@CXX@,$CXX," \
|
||||
-e "s#@CXXFLAGS@#$CXXFLAGS#" \
|
||||
../makefile.in > makefile
|
||||
|
||||
msg "generated '$build/makefile' from '../makefile.in'"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
build="`pwd`"
|
||||
makefile="`dirname "$build"`/makefile"
|
||||
cat <<EOF > "$makefile"
|
||||
CADICALBUILD=$build
|
||||
all:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)"
|
||||
clean:
|
||||
@if [ -d "\$(CADICALBUILD)" ]; \\
|
||||
then \\
|
||||
if [ -f "\$(CADICALBUILD)"/makefile ]; \\
|
||||
then \\
|
||||
touch "\$(CADICALBUILD)"/build.hpp; \\
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" clean; \\
|
||||
fi; \\
|
||||
rm -rf "\$(CADICALBUILD)"; \\
|
||||
fi
|
||||
rm -f "$src/makefile"
|
||||
rm -f "$makefile"
|
||||
test:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" test
|
||||
cadical:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" cadical
|
||||
mobical:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" mobical
|
||||
update:
|
||||
\$(MAKE) -C "\$(CADICALBUILD)" update
|
||||
.PHONY: all cadical clean mobical test update
|
||||
EOF
|
||||
|
||||
msg "generated '../makefile' as proxy to ..."
|
||||
msg "... '$build/makefile'"
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
if [ -f $src/makefile ]
|
||||
then
|
||||
msg "removing '$src/makefile'"
|
||||
rm -f $src/makefile
|
||||
fi
|
||||
|
||||
msg "linking '$root/makefile'"
|
||||
ln -s $root/makefile $src/makefile
|
||||
|
||||
#--------------------------------------------------------------------------#
|
||||
|
||||
msg "now run ${HILITE}'make'${NORMAL} to compile CaDiCaL"
|
||||
msg "optionally run 'make test'"
|
27
hCaD_V2/src/contract.cpp
Normal file
27
hCaD_V2/src/contract.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef NCONTRACTS
|
||||
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
void fatal_message_start ();
|
||||
|
||||
// See comments in 'contract.hpp'. Ugly hack we keep for now.
|
||||
|
||||
void require_solver_pointer_to_be_non_zero (const void * ptr,
|
||||
const char * function_name,
|
||||
const char * file_name)
|
||||
{
|
||||
if (ptr) return;
|
||||
fatal_message_start ();
|
||||
fprintf (stderr,
|
||||
"invalid API usage of '%s' in '%s': "
|
||||
"solver 'this' pointer zero (not initialized)\n",
|
||||
function_name, file_name);
|
||||
fflush (stderr);
|
||||
abort ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
112
hCaD_V2/src/contract.hpp
Normal file
112
hCaD_V2/src/contract.hpp
Normal file
@ -0,0 +1,112 @@
|
||||
#ifndef _contract_hpp_INCLUDED
|
||||
#define _contract_hpp_INCLUDED
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifndef NCONTRACTS
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// If the user violates API contracts while calling functions declared in
|
||||
// 'cadical.hpp' and implemented in 'solver.cpp' then an error is reported.
|
||||
// Currently we also force aborting the program. In the future it might be
|
||||
// better to allow the user to provide a call back function, which then can
|
||||
// for instance throw a C++ exception or execute a 'longjmp' in 'C' etc.
|
||||
|
||||
#define CONTRACT_VIOLATED(...) \
|
||||
do { \
|
||||
fatal_message_start (); \
|
||||
fprintf (stderr, \
|
||||
"invalid API usage of '%s' in '%s': ", \
|
||||
__PRETTY_FUNCTION__, __FILE__); \
|
||||
fprintf (stderr, __VA_ARGS__); \
|
||||
fputc ('\n', stderr); \
|
||||
fflush (stderr); \
|
||||
abort (); \
|
||||
} while (0)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// It would be much easier to just write 'REQUIRE (this, "not initialized")'
|
||||
// which however produces warnings due to the '-Wnonnull' check. Note, that
|
||||
// 'this' is always assumed to be non zero in modern C++. Much worse, if we
|
||||
// use instead 'this != 0' or something similar like 'this != nullptr' then
|
||||
// optimization silently removes this check ('gcc-7.4.0' at least) even
|
||||
// though of course a zero pointer might be used as 'this' if the user did
|
||||
// not initialize it. The only solution I found is to disable optimization
|
||||
// for this check. It does not seem to be necessary for 'clang++' though
|
||||
// ('clang++-6.0.0' at least). The alternative is to not check that the
|
||||
// user forgot to initialize the solver pointer, but as long this works we
|
||||
// keep this ugly hack. It also forces the function not to be inlined.
|
||||
// The actual code I is in 'contract.cpp'.
|
||||
//
|
||||
void require_solver_pointer_to_be_non_zero (const void *ptr,
|
||||
const char * function_name,
|
||||
const char * file_name);
|
||||
#define REQUIRE_NON_ZERO_THIS() \
|
||||
do { \
|
||||
require_solver_pointer_to_be_non_zero (this, \
|
||||
__PRETTY_FUNCTION__, __FILE__); \
|
||||
} while (0)
|
||||
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// These are common shortcuts for 'Solver' API contracts (requirements).
|
||||
|
||||
#define REQUIRE(COND,...) \
|
||||
do { \
|
||||
if ((COND)) break; \
|
||||
CONTRACT_VIOLATED (__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_INITIALIZED() \
|
||||
do { \
|
||||
REQUIRE_NON_ZERO_THIS (); \
|
||||
REQUIRE(external, "external solver not initialized"); \
|
||||
REQUIRE(internal, "internal solver not initialized"); \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_VALID_STATE() \
|
||||
do { \
|
||||
REQUIRE_INITIALIZED (); \
|
||||
REQUIRE(this->state () & VALID, "solver in invalid state"); \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_READY_STATE() \
|
||||
do { \
|
||||
REQUIRE_VALID_STATE (); \
|
||||
REQUIRE (state () != ADDING, \
|
||||
"clause incomplete (terminating zero not added)"); \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_VALID_OR_SOLVING_STATE() \
|
||||
do { \
|
||||
REQUIRE_INITIALIZED (); \
|
||||
REQUIRE(this->state () & (VALID | SOLVING), \
|
||||
"solver neither in valid nor solving state"); \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_VALID_LIT(LIT) \
|
||||
do { \
|
||||
REQUIRE ((int)(LIT) && ((int) (LIT)) != INT_MIN, \
|
||||
"invalid literal '%d'", (int)(LIT)); \
|
||||
} while (0)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#else // NCONTRACTS
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#define REQUIRE(...) do { } while (0)
|
||||
#define REQUIRE_INITIALIZED() do { } while (0)
|
||||
#define REQUIRE_VALID_STATE() do { } while (0)
|
||||
#define REQUIRE_READY_STATE() do { } while (0)
|
||||
#define REQUIRE_VALID_OR_SOLVING_STATE() do { } while (0)
|
||||
#define REQUIRE_VALID_LIT(...) do { } while (0)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#endif
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
588
hCaD_V2/src/cover.cpp
Normal file
588
hCaD_V2/src/cover.cpp
Normal file
@ -0,0 +1,588 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Covered clause elimination (CCE) is described in our short LPAR-10 paper
|
||||
// and later in more detail in our JAIR'15 article. Actually implement
|
||||
// the asymmetric version which adds asymmetric literals too but still call
|
||||
// it 'CCE' in the following (and not 'ACCE'). This implementation provides
|
||||
// a simplified and cleaner version of the one implemented before in
|
||||
// Lingeling. We still follow quite closely the original description in the
|
||||
// literature, which is based on asymmetric literal addition (ALA) and
|
||||
// covered literal addition (CLA). Both can be seen as kind of propagation,
|
||||
// where the literals in the original and then extended clause are assigned
|
||||
// to false, and the literals on the trail (actually we use our own 'added'
|
||||
// stack for that) make up the extended clause. The ALA steps can be
|
||||
// implemented by simple propagation (copied from 'propagate.cpp') using
|
||||
// watches, while the CLA steps need full occurrence lists to determine the
|
||||
// resolution candidate clauses. The CCE is successful if a conflict is
|
||||
// found during ALA steps or if during a CLA step all resolution candidates
|
||||
// of a literal on the trail are satisfied (the extended clause is blocked).
|
||||
|
||||
struct Coveror
|
||||
{
|
||||
std::vector<int> added; // acts as trail
|
||||
std::vector<int> extend; // extension stack for witness
|
||||
std::vector<int> covered; // clause literals or added through CLA
|
||||
std::vector<int> intersection; // of literals in resolution candidates
|
||||
|
||||
size_t alas, clas; // actual number of ALAs and CLAs
|
||||
|
||||
struct { size_t added, covered; } next; // propagate next ...
|
||||
|
||||
Coveror () : alas (0), clas (0) { }
|
||||
};
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Push on the extension stack a clause made up of the given literal, the
|
||||
// original clause (initially copied to 'covered') and all the added covered
|
||||
// literals so far. The given literal will act as blocking literal for that
|
||||
// clause, if CCE is successful. Only in this case, this private extension
|
||||
// stack is copied to the actual extension stack of the solver. Note, that
|
||||
// even though all 'added' clauses correspond to the extended clause, we
|
||||
// only need to save the original and added covered literals.
|
||||
|
||||
inline void
|
||||
Internal::cover_push_extension (int lit, Coveror & coveror) {
|
||||
coveror.extend.push_back (0);
|
||||
coveror.extend.push_back (lit); // blocking literal comes first
|
||||
bool found = false;
|
||||
for (const auto & other : coveror.covered)
|
||||
if (lit == other) assert (!found), found = true;
|
||||
else coveror.extend.push_back (other);
|
||||
assert (found);
|
||||
(void) found;
|
||||
}
|
||||
|
||||
// Successful covered literal addition (CLA) step.
|
||||
|
||||
inline void
|
||||
Internal::covered_literal_addition (int lit, Coveror & coveror)
|
||||
{
|
||||
require_mode (COVER);
|
||||
assert (level == 1);
|
||||
cover_push_extension (lit, coveror);
|
||||
for (const auto & other : coveror.intersection) {
|
||||
LOG ("covered literal addition %d", other);
|
||||
assert (!vals[other]), assert (!vals[-other]);
|
||||
vals[other] = -1, vals[-other] = 1;
|
||||
coveror.covered.push_back (other);
|
||||
coveror.added.push_back (other);
|
||||
coveror.clas++;
|
||||
}
|
||||
coveror.next.covered = 0;
|
||||
}
|
||||
|
||||
// Successful asymmetric literal addition (ALA) step.
|
||||
|
||||
inline void
|
||||
Internal::asymmetric_literal_addition (int lit, Coveror & coveror)
|
||||
{
|
||||
require_mode (COVER);
|
||||
assert (level == 1);
|
||||
LOG ("initial asymmetric literal addition %d", lit);
|
||||
assert (!vals[lit]), assert (!vals[-lit]);
|
||||
vals[lit] = -1, vals[-lit] = 1;
|
||||
coveror.added.push_back (lit);
|
||||
coveror.alas++;
|
||||
coveror.next.covered = 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// In essence copied and adapted from 'propagate' in 'propagate.cpp'. Since
|
||||
// this function is also a hot-spot here in 'cover' we specialize it in the
|
||||
// same spirit as 'probe_propagate' and 'vivify_propagate'. Please refer to
|
||||
// the detailed comments for 'propagate' in 'propagate.cpp' for details.
|
||||
|
||||
bool
|
||||
Internal::cover_propagate_asymmetric (int lit,
|
||||
Clause * ignore,
|
||||
Coveror & coveror)
|
||||
{
|
||||
require_mode (COVER);
|
||||
stats.propagations.cover++;
|
||||
assert (val (lit) < 0);
|
||||
bool subsumed = false;
|
||||
LOG ("asymmetric literal propagation of %d", lit);
|
||||
Watches & ws = watches (lit);
|
||||
const const_watch_iterator eow = ws.end ();
|
||||
watch_iterator j = ws.begin ();
|
||||
const_watch_iterator i = j;
|
||||
while (!subsumed && i != eow) {
|
||||
const Watch w = *j++ = *i++;
|
||||
if (w.clause == ignore) continue; // costly but necessary here ...
|
||||
const signed char b = val (w.blit);
|
||||
if (b > 0) continue;
|
||||
if (w.clause->garbage) j--;
|
||||
else if (w.binary ()) {
|
||||
if (b < 0) {
|
||||
LOG (w.clause, "found subsuming");
|
||||
subsumed = true;
|
||||
} else asymmetric_literal_addition (-w.blit, coveror);
|
||||
} else {
|
||||
literal_iterator lits = w.clause->begin ();
|
||||
const int other = lits[0]^lits[1]^lit;
|
||||
lits[0] = other, lits[1] = lit;
|
||||
const signed char u = val (other);
|
||||
if (u > 0) j[-1].blit = other;
|
||||
else {
|
||||
const int size = w.clause->size;
|
||||
const const_literal_iterator end = lits + size;
|
||||
const literal_iterator middle = lits + w.clause->pos;
|
||||
literal_iterator k = middle;
|
||||
signed char v = -1;
|
||||
int r = 0;
|
||||
while (k != end && (v = val (r = *k)) < 0)
|
||||
k++;
|
||||
if (v < 0) {
|
||||
k = lits + 2;
|
||||
assert (w.clause->pos <= size);
|
||||
while (k != middle && (v = val (r = *k)) < 0)
|
||||
k++;
|
||||
}
|
||||
w.clause->pos = k - lits;
|
||||
assert (lits + 2 <= k), assert (k <= w.clause->end ());
|
||||
if (v > 0) j[-1].blit = r;
|
||||
else if (!v) {
|
||||
LOG (w.clause, "unwatch %d in", lit);
|
||||
lits[1] = r;
|
||||
*k = lit;
|
||||
watch_literal (r, lit, w.clause);
|
||||
j--;
|
||||
} else if (!u) {
|
||||
assert (v < 0);
|
||||
asymmetric_literal_addition (-other, coveror);
|
||||
} else {
|
||||
assert (u < 0), assert (v < 0);
|
||||
LOG (w.clause, "found subsuming");
|
||||
subsumed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j != i) {
|
||||
while (i != eow) *j++ = *i++;
|
||||
ws.resize (j - ws.begin ());
|
||||
}
|
||||
return subsumed;
|
||||
}
|
||||
|
||||
// Covered literal addition (which needs full occurrence lists). The
|
||||
// function returns 'true' if the extended clause is blocked on 'lit.'
|
||||
|
||||
bool
|
||||
Internal::cover_propagate_covered (int lit, Coveror & coveror)
|
||||
{
|
||||
require_mode (COVER);
|
||||
|
||||
assert (val (lit) < 0);
|
||||
if (frozen (lit)) {
|
||||
LOG ("no covered propagation on frozen literal %d", lit);
|
||||
return false;
|
||||
}
|
||||
|
||||
stats.propagations.cover++;
|
||||
|
||||
LOG ("covered propagation of %d", lit);
|
||||
assert (coveror.intersection.empty ());
|
||||
|
||||
Occs & os = occs (-lit);
|
||||
const auto end = os.end ();
|
||||
bool first = true;
|
||||
|
||||
// Compute the intersection of the literals in all the clauses with
|
||||
// '-lit'. If all these clauses are double satisfied then we know that
|
||||
// the extended clauses (in 'added') is blocked. All literals in the
|
||||
// intersection can be added as covered literal. As soon the intersection
|
||||
// becomes empty (during traversal of clauses with '-lit') we abort.
|
||||
|
||||
for (auto i = os.begin (); i != end; i++) {
|
||||
|
||||
Clause * c = *i;
|
||||
if (c->garbage) continue;
|
||||
|
||||
// First check whether clause is 'blocked', i.e., is double satisfied.
|
||||
|
||||
bool blocked = false;
|
||||
for (const auto & other : *c) {
|
||||
if (other == -lit) continue;
|
||||
const signed char tmp = val (other);
|
||||
if (tmp < 0) continue;
|
||||
if (tmp > 0) { blocked = true; break; }
|
||||
}
|
||||
if (blocked) { // ... if 'c' is double satisfied.
|
||||
LOG (c, "blocked");
|
||||
continue; // with next clause with '-lit'.
|
||||
}
|
||||
|
||||
if (first) {
|
||||
|
||||
// Copy and mark literals of first clause.
|
||||
|
||||
for (const auto & other : *c) {
|
||||
if (other == -lit) continue;
|
||||
const signed char tmp = val (other);
|
||||
if (tmp < 0) continue;
|
||||
assert (!tmp);
|
||||
coveror.intersection.push_back (other);
|
||||
mark (other);
|
||||
}
|
||||
|
||||
first = false;
|
||||
|
||||
} else {
|
||||
|
||||
// Unmark all literals in current clause.
|
||||
|
||||
for (const auto & other : *c) {
|
||||
if (other == -lit) continue;
|
||||
signed char tmp = val (other);
|
||||
if (tmp < 0) continue;
|
||||
assert (!tmp);
|
||||
tmp = marked (other);
|
||||
if (tmp > 0) unmark (other);
|
||||
}
|
||||
|
||||
// Then remove from intersection all marked literals.
|
||||
|
||||
const auto end = coveror.intersection.end ();
|
||||
auto j = coveror.intersection.begin ();
|
||||
for (auto k = j; k != end; k++) {
|
||||
const int other = *j++ = *k;
|
||||
const int tmp = marked (other);
|
||||
assert (tmp >= 0);
|
||||
if (tmp) j--, unmark (other); // remove marked and unmark it
|
||||
else mark (other); // keep unmarked and mark it
|
||||
}
|
||||
const size_t new_size = j - coveror.intersection.begin ();
|
||||
coveror.intersection.resize (new_size);
|
||||
|
||||
if (!coveror.intersection.empty ()) continue;
|
||||
|
||||
// No covered literal addition candidates in the intersection left!
|
||||
// Move this clause triggering early abort to the beginning.
|
||||
// This is a common move to front strategy to minimize effort.
|
||||
|
||||
auto begin = os.begin ();
|
||||
while (i != begin) {
|
||||
auto prev = i - 1;
|
||||
*i = *prev;
|
||||
i = prev;
|
||||
}
|
||||
*begin = c;
|
||||
|
||||
break; // early abort ...
|
||||
}
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
if (first) {
|
||||
LOG ("all resolution candidates with %d blocked", -lit);
|
||||
assert (coveror.intersection.empty ());
|
||||
cover_push_extension (lit, coveror);
|
||||
res = true;
|
||||
} else if (coveror.intersection.empty ()) {
|
||||
LOG ("empty intersection of resolution candidate literals");
|
||||
} else {
|
||||
LOG (coveror.intersection,
|
||||
"non-empty intersection of resolution candidate literals");
|
||||
covered_literal_addition (lit, coveror);
|
||||
unmark (coveror.intersection);
|
||||
coveror.intersection.clear ();
|
||||
coveror.next.covered = 0; // Restart covering.
|
||||
}
|
||||
|
||||
unmark (coveror.intersection);
|
||||
coveror.intersection.clear ();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::cover_clause (Clause * c, Coveror & coveror) {
|
||||
|
||||
require_mode (COVER);
|
||||
assert (!c->garbage);
|
||||
|
||||
LOG (c, "trying covered clauses elimination on");
|
||||
bool satisfied = false;
|
||||
for (const auto & lit : *c)
|
||||
if (val (lit) > 0)
|
||||
satisfied = true;
|
||||
|
||||
if (satisfied) {
|
||||
LOG (c, "clause already satisfied");
|
||||
mark_garbage (c);
|
||||
return false;
|
||||
}
|
||||
|
||||
assert (coveror.added.empty ());
|
||||
assert (coveror.extend.empty ());
|
||||
assert (coveror.covered.empty ());
|
||||
|
||||
assert (!level);
|
||||
level = 1;
|
||||
LOG ("assuming literals of candidate clause");
|
||||
for (const auto & lit : *c) {
|
||||
if (val (lit)) continue;
|
||||
asymmetric_literal_addition (lit, coveror);
|
||||
coveror.covered.push_back (lit);
|
||||
}
|
||||
|
||||
bool tautological = false;
|
||||
|
||||
coveror.next.added = coveror.next.covered = 0;
|
||||
|
||||
while (!tautological) {
|
||||
if (coveror.next.added < coveror.added.size ()) {
|
||||
const int lit = coveror.added[coveror.next.added++];
|
||||
tautological = cover_propagate_asymmetric (lit, c, coveror);
|
||||
} else if (coveror.next.covered < coveror.covered.size ()) {
|
||||
const int lit = coveror.covered[coveror.next.covered++];
|
||||
tautological = cover_propagate_covered (lit, coveror);
|
||||
} else break;
|
||||
}
|
||||
|
||||
if (tautological) {
|
||||
if (coveror.extend.empty ()) {
|
||||
stats.cover.asymmetric++;
|
||||
stats.cover.total++;
|
||||
LOG (c, "asymmetric tautological");
|
||||
mark_garbage (c);
|
||||
} else {
|
||||
stats.cover.blocked++;
|
||||
stats.cover.total++;
|
||||
LOG (c, "covered tautological");
|
||||
mark_garbage (c);
|
||||
// Only copy extension stack if successful.
|
||||
int prev = INT_MIN;
|
||||
for (const auto & other : coveror.extend) {
|
||||
if (!prev) {
|
||||
external->push_zero_on_extension_stack ();
|
||||
external->push_witness_literal_on_extension_stack (other);
|
||||
external->push_zero_on_extension_stack ();
|
||||
}
|
||||
if (other)
|
||||
external->push_clause_literal_on_extension_stack (other);
|
||||
prev = other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Backtrack and 'unassign' all literals.
|
||||
|
||||
assert (level == 1);
|
||||
for (const auto & lit : coveror.added)
|
||||
vals[lit] = vals[-lit] = 0;
|
||||
level = 0;
|
||||
|
||||
coveror.covered.clear ();
|
||||
coveror.extend.clear ();
|
||||
coveror.added.clear ();
|
||||
|
||||
return tautological;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Not yet tried and larger clauses are tried first.
|
||||
|
||||
struct clause_covered_or_smaller {
|
||||
bool operator () (const Clause * a, const Clause * b) {
|
||||
if (a->covered && !b->covered) return true;
|
||||
if (!a->covered && b->covered) return false;
|
||||
return a->size < b->size;
|
||||
}
|
||||
};
|
||||
|
||||
int64_t Internal::cover_round () {
|
||||
|
||||
if (unsat) return 0;
|
||||
|
||||
init_watches ();
|
||||
connect_watches (true); // irredundant watches only is enough
|
||||
|
||||
int64_t delta = stats.propagations.search;
|
||||
delta *= 1e-3 * opts.coverreleff;
|
||||
if (delta < opts.covermineff) delta = opts.covermineff;
|
||||
if (delta > opts.covermaxeff) delta = opts.covermaxeff;
|
||||
delta = max (delta, ((int64_t) 2) * active ());
|
||||
|
||||
PHASE ("cover", stats.cover.count,
|
||||
"covered clause elimination limit of %" PRId64 " propagations", delta);
|
||||
|
||||
int64_t limit = stats.propagations.cover + delta;
|
||||
|
||||
init_occs ();
|
||||
|
||||
vector<Clause *> schedule;
|
||||
Coveror coveror;
|
||||
|
||||
// First connect all clauses and find all not yet tried clauses.
|
||||
//
|
||||
int64_t untried = 0;
|
||||
//
|
||||
for (auto c : clauses) {
|
||||
assert (!c->frozen);
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
bool satisfied = false, allfrozen = true;
|
||||
for (const auto & lit : *c)
|
||||
if (val (lit) > 0) { satisfied = true; break; }
|
||||
else if (allfrozen && !frozen (lit)) allfrozen = false;
|
||||
if (satisfied) { mark_garbage (c); continue; }
|
||||
if (allfrozen) { c->frozen = true; continue; }
|
||||
for (const auto & lit : *c)
|
||||
occs (lit).push_back (c);
|
||||
if (c->size < opts.coverminclslim) continue;
|
||||
if (c->size > opts.covermaxclslim) continue;
|
||||
if (c->covered) continue;
|
||||
schedule.push_back (c);
|
||||
untried++;
|
||||
}
|
||||
|
||||
if (schedule.empty ()) {
|
||||
|
||||
PHASE ("cover", stats.cover.count,
|
||||
"no previously untried clause left");
|
||||
|
||||
for (auto c : clauses) {
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
if (c->frozen) { c->frozen = false; continue; }
|
||||
if (c->size < opts.coverminclslim) continue;
|
||||
if (c->size > opts.covermaxclslim) continue;
|
||||
assert (c->covered);
|
||||
c->covered = false;
|
||||
schedule.push_back (c);
|
||||
}
|
||||
} else { // Mix of tried and not tried clauses ....
|
||||
|
||||
for (auto c : clauses) {
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
if (c->frozen) { c->frozen = false; continue; }
|
||||
if (c->size < opts.coverminclslim) continue;
|
||||
if (c->size > opts.covermaxclslim) continue;
|
||||
if (!c->covered) continue;
|
||||
schedule.push_back (c);
|
||||
}
|
||||
}
|
||||
|
||||
stable_sort (schedule.begin (), schedule.end (),
|
||||
clause_covered_or_smaller ());
|
||||
|
||||
#ifndef QUIET
|
||||
const size_t scheduled = schedule.size ();
|
||||
PHASE ("cover", stats.cover.count,
|
||||
"scheduled %zd clauses %.0f%% with %" PRId64 " untried %.0f%%",
|
||||
scheduled, percent (scheduled, stats.current.irredundant),
|
||||
untried, percent (untried, scheduled));
|
||||
#endif
|
||||
|
||||
// Heuristically it should be beneficial to intersect with smaller clauses
|
||||
// first, since then the chances are higher that the intersection of
|
||||
// resolution candidates becomes emptier earlier.
|
||||
|
||||
for (auto lit : lits) {
|
||||
if (!active (lit)) continue;
|
||||
Occs & os = occs (lit);
|
||||
stable_sort (os.begin (), os.end (), clause_smaller_size ());
|
||||
}
|
||||
|
||||
// This is the main loop of trying to do CCE of candidate clauses.
|
||||
//
|
||||
int64_t covered = 0;
|
||||
//
|
||||
while (!terminated_asynchronously () &&
|
||||
!schedule.empty () &&
|
||||
stats.propagations.cover < limit) {
|
||||
Clause * c = schedule.back ();
|
||||
schedule.pop_back ();
|
||||
c->covered = true;
|
||||
if (cover_clause (c, coveror)) covered++;
|
||||
}
|
||||
|
||||
#ifndef QUIET
|
||||
const size_t remain = schedule.size ();
|
||||
const size_t tried = scheduled - remain;
|
||||
PHASE ("cover", stats.cover.count,
|
||||
"eliminated %" PRId64 " covered clauses out of %zd tried %.0f%%",
|
||||
covered, tried, percent (covered, tried));
|
||||
if (remain)
|
||||
PHASE ("cover", stats.cover.count,
|
||||
"remaining %zu clauses %.0f%% untried",
|
||||
remain, percent (remain, scheduled));
|
||||
else
|
||||
PHASE ("cover", stats.cover.count,
|
||||
"all scheduled clauses tried");
|
||||
#endif
|
||||
reset_occs ();
|
||||
reset_watches ();
|
||||
|
||||
return covered;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::cover () {
|
||||
|
||||
if (!opts.cover) return false;
|
||||
if (unsat) return false;
|
||||
if (terminated_asynchronously ()) return false;
|
||||
if (!stats.current.irredundant) return false;
|
||||
|
||||
// TODO: Our current algorithm for producing the necessary clauses on the
|
||||
// reconstruction stack for extending the witness requires a covered
|
||||
// literal addition step which (empirically) conflicts with flushing
|
||||
// during restoring clauses (see 'regr00{48,51}.trace') even though
|
||||
// flushing during restore is disabled by default (as is covered clause
|
||||
// elimination). The consequence of combining these two options
|
||||
// ('opts.cover' and 'opts.restoreflush') can thus produce incorrect
|
||||
// witness reconstruction and thus invalid witnesses. This is quite
|
||||
// infrequent (one out of half billion mobical test cases) but as the two
|
||||
// regression traces show, does happen. Thus we disable the combination.
|
||||
//
|
||||
if (opts.restoreflush) return false;
|
||||
|
||||
START_SIMPLIFIER (cover, COVER);
|
||||
|
||||
stats.cover.count++;
|
||||
|
||||
// During variable elimination unit clauses can be generated which need to
|
||||
// be propagated properly over redundant clauses too. Since variable
|
||||
// elimination avoids to have occurrence lists and watches at the same
|
||||
// time this propagation is delayed until the end of variable elimination.
|
||||
// Since we want to interleave CCE with it, we have to propagate here.
|
||||
// Otherwise this triggers inconsistencies.
|
||||
//
|
||||
if (propagated < trail.size ()) {
|
||||
init_watches ();
|
||||
connect_watches (); // need to propagated over all clauses!
|
||||
LOG ("elimination produced %zd units",
|
||||
(size_t)(trail.size () - propagated));
|
||||
if (!propagate ()) {
|
||||
LOG ("propagating units before covered clause elimination "
|
||||
"results in empty clause");
|
||||
learn_empty_clause ();
|
||||
assert (unsat);
|
||||
}
|
||||
reset_watches ();
|
||||
}
|
||||
assert (unsat || propagated == trail.size ());
|
||||
|
||||
int64_t covered = cover_round ();
|
||||
|
||||
STOP_SIMPLIFIER (cover, COVER);
|
||||
report ('c', !opts.reportall && !covered);
|
||||
|
||||
return covered;
|
||||
}
|
||||
|
||||
}
|
33
hCaD_V2/src/cover.hpp
Normal file
33
hCaD_V2/src/cover.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef _cover_hpp_INCLUDED
|
||||
#define _cover_hpp_INCLUDED
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This header only provides the 'COVER' macro for testing. It is unrelated
|
||||
// to 'cover.cpp' which implements covered clause elimination (CCE), but we
|
||||
// wanted to use the name base name in both cases. More explanation on CCE
|
||||
// is provided in 'cover.cpp'.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Coverage goal, used similar to 'assert' (but with flipped condition) and
|
||||
// also included even if 'NDEBUG' is defined (in optimizing compilation).
|
||||
//
|
||||
// This should in essence not be used in production code.
|
||||
//
|
||||
// There seems to be no problem overloading the name 'COVER' of this macro
|
||||
// with the constant 'COVER' of 'Internal::Mode' (surprisingly).
|
||||
|
||||
#define COVER(COND) \
|
||||
do { \
|
||||
if (!(COND)) break; \
|
||||
fprintf (stderr, \
|
||||
"%scadical%s: %s:%d: %s: Coverage goal %s`%s'%s reached.\n", \
|
||||
terr.bold_code (), terr.normal_code (), \
|
||||
__FUNCTION__, __LINE__, __FILE__, \
|
||||
terr.green_code (), # COND, terr.normal_code ()); \
|
||||
fflush (stderr); \
|
||||
abort (); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
123
hCaD_V2/src/decide.cpp
Normal file
123
hCaD_V2/src/decide.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// This function determines the next decision variable on the queue, without
|
||||
// actually removing it from the decision queue, e.g., calling it multiple
|
||||
// times without any assignment will return the same result. This is of
|
||||
// course used below in 'decide' but also in 'reuse_trail' to determine the
|
||||
// largest decision level to backtrack to during 'restart' without changing
|
||||
// the assigned variables (if 'opts.restartreusetrail' is non-zero).
|
||||
|
||||
int Internal::next_decision_variable_on_queue () {
|
||||
int64_t searched = 0;
|
||||
int res = queue.unassigned;
|
||||
while (val (res))
|
||||
res = link (res).prev, searched++;
|
||||
if (searched) {
|
||||
stats.searched += searched;
|
||||
update_queue_unassigned (res);
|
||||
}
|
||||
LOG ("next queue decision variable %d bumped %" PRId64 "", res, bumped (res));
|
||||
return res;
|
||||
}
|
||||
|
||||
// This function determines the best decision with respect to score.
|
||||
//
|
||||
int Internal::next_decision_variable_with_best_score () {
|
||||
int res = 0;
|
||||
for (;;) {
|
||||
res = scores.front ();
|
||||
if (!val (res)) break;
|
||||
(void) scores.pop_front ();
|
||||
}
|
||||
LOG ("next decision variable %d with score %g", res, score (res));
|
||||
return res;
|
||||
}
|
||||
|
||||
int Internal::next_decision_variable () {
|
||||
if (use_scores ()) return next_decision_variable_with_best_score ();
|
||||
else return next_decision_variable_on_queue ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Implements phase saving as well using a target phase during
|
||||
// stabilization unless decision phase is forced to the initial value
|
||||
// of a phase is forced through the 'phase' option.
|
||||
|
||||
int Internal::decide_phase (int idx, bool target) {
|
||||
const int initial_phase = opts.phase ? 1 : -1;
|
||||
int phase = 0;
|
||||
if (force_saved_phase) phase = phases.saved[idx];
|
||||
if (!phase && opts.forcephase) phase = initial_phase;
|
||||
if (!phase) phase = phases.forced[idx]; // TODO swap?
|
||||
if (!phase && target) phase = phases.target[idx];
|
||||
if (!phase) phase = phases.saved[idx];
|
||||
|
||||
if(opts.psids && phases.act[vlit(idx)] != phases.act[vlit(-idx)])
|
||||
phase = phases.act[vlit(idx)] > phases.act[vlit(-idx)] ? 1: -1;
|
||||
// The following should no be necessary and in some version we had even
|
||||
// a hard 'COVER' assertion here to check for this. Unfortunately it
|
||||
// triggered for some users and we could not get to the root cause of
|
||||
// 'phase' still not being set here. The logic for phase and target
|
||||
// saving is pretty complex, particularly in combination with local
|
||||
// search, and to avoid running in such an issue in the future again, we
|
||||
// now use this 'defensive' code here, even though such defensive code is
|
||||
// considered bad programming practice.
|
||||
//
|
||||
if (!phase) phase = initial_phase;
|
||||
|
||||
return phase * idx;
|
||||
}
|
||||
|
||||
// The likely phase of an variable used in 'collect' for optimizing
|
||||
// co-location of clauses likely accessed together during search.
|
||||
|
||||
int Internal::likely_phase (int idx) { return decide_phase (idx, false); }
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::satisfied () {
|
||||
size_t assigned = trail.size ();
|
||||
if (propagated < assigned) return false;
|
||||
if ((size_t) level < assumptions.size ()) return false;
|
||||
return (assigned == (size_t) max_var);
|
||||
}
|
||||
|
||||
// Search for the next decision and assign it to the saved phase. Requires
|
||||
// that not all variables are assigned.
|
||||
|
||||
int Internal::decide () {
|
||||
assert (!satisfied ());
|
||||
START (decide);
|
||||
int res = 0;
|
||||
if ((size_t) level < assumptions.size ()) {
|
||||
const int lit = assumptions[level];
|
||||
assert (assumed (lit));
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp < 0) {
|
||||
LOG ("assumption %d falsified", lit);
|
||||
failing ();
|
||||
res = 20;
|
||||
} else if (tmp > 0) {
|
||||
LOG ("assumption %d already satisfied", lit);
|
||||
level++;
|
||||
control.push_back (Level (0, trail.size ()));
|
||||
LOG ("added pseudo decision level");
|
||||
} else {
|
||||
LOG ("deciding assumption %d", lit);
|
||||
search_assume_decision (lit);
|
||||
}
|
||||
} else {
|
||||
stats.decisions++;
|
||||
int idx = next_decision_variable ();
|
||||
const bool target = (opts.target > 1 || (stable && opts.target));
|
||||
int decision = decide_phase (idx, target);
|
||||
search_assume_decision (decision);
|
||||
}
|
||||
STOP (decide);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
361
hCaD_V2/src/decompose.cpp
Normal file
361
hCaD_V2/src/decompose.cpp
Normal file
@ -0,0 +1,361 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// This implements Tarjan's algorithm for decomposing the binary implication
|
||||
// graph intro strongly connected components (SCCs). Literals in one SCC
|
||||
// are equivalent and we replace them all by the literal with the smallest
|
||||
// index in the SCC. These variables are marked 'substituted' and will be
|
||||
// removed from all clauses. Their value will be fixed during 'extend'.
|
||||
|
||||
#define TRAVERSED UINT_MAX // mark completely traversed
|
||||
|
||||
struct DFS {
|
||||
unsigned idx; // depth first search index
|
||||
unsigned min; // minimum reachable index
|
||||
DFS () : idx (0), min (0) { }
|
||||
};
|
||||
|
||||
// This performs one round of Tarjan's algorithm, e.g., equivalent literal
|
||||
// detection and substitution, on the whole formula. We might want to
|
||||
// repeat it since its application might produce new binary clauses or
|
||||
// units. Such units might even result in an empty clause.
|
||||
|
||||
bool Internal::decompose_round () {
|
||||
|
||||
if (!opts.decompose) return false;
|
||||
if (unsat) return false;
|
||||
if (terminated_asynchronously ()) return false;
|
||||
|
||||
assert (!level);
|
||||
|
||||
START_SIMPLIFIER (decompose, DECOMP);
|
||||
|
||||
stats.decompositions++;
|
||||
|
||||
const size_t size_dfs = 2*(1 + (size_t) max_var);
|
||||
DFS * dfs = new DFS[size_dfs];
|
||||
int * reprs = new int[size_dfs];
|
||||
clear_n (reprs, size_dfs);
|
||||
|
||||
int non_trivial_sccs = 0, substituted = 0;
|
||||
#ifndef QUIET
|
||||
int before = active ();
|
||||
#endif
|
||||
unsigned dfs_idx = 0;
|
||||
|
||||
vector<int> work; // depth first search working stack
|
||||
vector<int> scc; // collects members of one SCC
|
||||
|
||||
// The binary implication graph might have disconnected components and
|
||||
// thus we have in general to start several depth first searches.
|
||||
|
||||
for (auto root_idx : vars) {
|
||||
if (unsat) break;
|
||||
if (!active (root_idx)) continue;
|
||||
for (int root_sign = -1; !unsat && root_sign <= 1; root_sign += 2) {
|
||||
int root = root_sign * root_idx;
|
||||
if (dfs[vlit (root)].min == TRAVERSED) continue; // skip traversed
|
||||
LOG ("new dfs search starting at root %d", root);
|
||||
assert (work.empty ());
|
||||
assert (scc.empty ());
|
||||
work.push_back (root);
|
||||
while (!unsat && !work.empty ()) {
|
||||
int parent = work.back ();
|
||||
DFS & parent_dfs = dfs[vlit (parent)];
|
||||
if (parent_dfs.min == TRAVERSED) { // skip traversed
|
||||
assert (reprs [vlit (parent)]);
|
||||
work.pop_back ();
|
||||
} else {
|
||||
assert (!reprs [vlit (parent)]);
|
||||
|
||||
// Go over all implied literals, thus need to iterate over all
|
||||
// binary watched clauses with the negation of 'parent'.
|
||||
|
||||
Watches & ws = watches (-parent);
|
||||
|
||||
// Two cases: Either the node has never been visited before, i.e.,
|
||||
// it's depth first search index is zero, then perform the
|
||||
// 'pre-fix' work before visiting it's children. Otherwise all
|
||||
// it's children and nodes reachable from those children have been
|
||||
// visited and their minimum reachable depth first search index
|
||||
// has been computed. This second case is the 'post-fix' work.
|
||||
|
||||
if (parent_dfs.idx) { // post-fix
|
||||
|
||||
work.pop_back (); // 'parent' done
|
||||
|
||||
// Get the minimum reachable depth first search index reachable
|
||||
// from the children of 'parent'.
|
||||
|
||||
unsigned new_min = parent_dfs.min;
|
||||
|
||||
for (const auto & w : ws) {
|
||||
if (!w.binary ()) continue;
|
||||
const int child = w.blit;
|
||||
if (!active (child)) continue;
|
||||
const DFS & child_dfs = dfs[vlit (child)];
|
||||
if (new_min > child_dfs.min) new_min = child_dfs.min;
|
||||
}
|
||||
|
||||
LOG ("post-fix work dfs search %d index %u reaches minimum %u",
|
||||
parent, parent_dfs.idx, new_min);
|
||||
|
||||
if (parent_dfs.idx == new_min) { // entry to SCC
|
||||
|
||||
// All nodes on the 'scc' stack after and including 'parent'
|
||||
// are in the same SCC. Their representative is computed as
|
||||
// the smallest literal (index-wise) in the SCC. If the SCC
|
||||
// contains both a literal and its negation, then the formula
|
||||
// becomes unsatisfiable.
|
||||
|
||||
int other, size = 0, repr = parent;
|
||||
assert (!scc.empty ());
|
||||
size_t j = scc.size ();
|
||||
do {
|
||||
assert (j > 0);
|
||||
other = scc[--j];
|
||||
if (other == -parent) {
|
||||
LOG ("both %d and %d in one SCC", parent, -parent);
|
||||
assign_unit (parent);
|
||||
learn_empty_clause ();
|
||||
} else {
|
||||
if (abs (other) < abs (repr)) repr = other;
|
||||
size++;
|
||||
}
|
||||
} while (!unsat && other != parent);
|
||||
|
||||
if (!unsat) {
|
||||
|
||||
LOG ("SCC of representative %d of size %d", repr, size);
|
||||
|
||||
do {
|
||||
assert (!scc.empty ());
|
||||
other = scc.back ();
|
||||
scc.pop_back ();
|
||||
dfs[vlit (other)].min = TRAVERSED;
|
||||
if (frozen (other)) {
|
||||
reprs[vlit (other) ] = other;
|
||||
} else {
|
||||
reprs[vlit (other)] = repr;
|
||||
if (other != repr) {
|
||||
substituted++;
|
||||
LOG ("literal %d in SCC of %d", other, repr);
|
||||
}
|
||||
}
|
||||
} while (other != parent);
|
||||
|
||||
if (size > 1) non_trivial_sccs++;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Current node 'parent' is in a non-trivial SCC but is not
|
||||
// the entry point of the SCC in this depth first search, so
|
||||
// keep it on the SCC stack until the entry point is reached.
|
||||
|
||||
parent_dfs.min = new_min;
|
||||
}
|
||||
|
||||
} else { // pre-fix
|
||||
|
||||
dfs_idx++;
|
||||
assert (dfs_idx < TRAVERSED);
|
||||
parent_dfs.idx = parent_dfs.min = dfs_idx;
|
||||
scc.push_back (parent);
|
||||
|
||||
LOG ("pre-fix work dfs search %d index %u", parent, dfs_idx);
|
||||
|
||||
// Now traverse all the children in the binary implication
|
||||
// graph but keep 'parent' on the stack for 'post-fix' work.
|
||||
|
||||
for (const auto & w : ws) {
|
||||
if (!w.binary ()) continue;
|
||||
const int child = w.blit;
|
||||
if (!active (child)) continue;
|
||||
const DFS & child_dfs = dfs[vlit (child)];
|
||||
if (child_dfs.idx) continue;
|
||||
work.push_back (child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
erase_vector (work);
|
||||
erase_vector (scc);
|
||||
delete [] dfs;
|
||||
|
||||
// Only keep the representatives 'repr' mapping.
|
||||
|
||||
PHASE ("decompose",
|
||||
stats.decompositions,
|
||||
"%d non-trivial sccs, %d substituted %.2f%%",
|
||||
non_trivial_sccs, substituted, percent (substituted, before));
|
||||
|
||||
bool new_unit = false, new_binary_clause = false;
|
||||
|
||||
vector<Clause*> postponed_garbage;
|
||||
|
||||
// Now go over all clauses and find clause which contain literals that
|
||||
// should be substituted by their representative.
|
||||
|
||||
size_t clauses_size = clauses.size (), garbage = 0, replaced = 0;
|
||||
for (size_t i = 0; substituted && !unsat && i < clauses_size; i++) {
|
||||
Clause * c = clauses[i];
|
||||
if (c->garbage) continue;
|
||||
int j, size = c->size;
|
||||
for (j = 0; j < size; j++) {
|
||||
const int lit = c->literals[j];
|
||||
if (reprs [ vlit (lit) ] != lit) break;
|
||||
}
|
||||
|
||||
if (j == size) continue;
|
||||
|
||||
replaced++;
|
||||
LOG (c, "first substituted literal %d in", substituted);
|
||||
|
||||
// Now copy the result to 'clause'. Substitute literals if they have a
|
||||
// different representative. Skip duplicates and false literals. If a
|
||||
// literal occurs in both phases or is assigned to true the clause is
|
||||
// satisfied and can be marked as garbage.
|
||||
|
||||
assert (clause.empty ());
|
||||
bool satisfied = false;
|
||||
|
||||
for (int k = 0; !satisfied && k < size; k++) {
|
||||
const int lit = c->literals[k];
|
||||
signed char tmp = val (lit);
|
||||
if (tmp > 0) satisfied = true;
|
||||
else if (tmp < 0) continue;
|
||||
else {
|
||||
const int other = reprs [vlit (lit)];
|
||||
tmp = val (other);
|
||||
if (tmp < 0) continue;
|
||||
else if (tmp > 0) satisfied = true;
|
||||
else {
|
||||
tmp = marked (other);
|
||||
if (tmp < 0) satisfied = true;
|
||||
else if (!tmp) {
|
||||
mark (other);
|
||||
clause.push_back (other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (satisfied) {
|
||||
LOG (c, "satisfied after substitution (postponed)");
|
||||
postponed_garbage.push_back (c);
|
||||
garbage++;
|
||||
} else if (!clause.size ()) {
|
||||
LOG ("learned empty clause during decompose");
|
||||
learn_empty_clause ();
|
||||
} else if (clause.size () == 1) {
|
||||
LOG (c, "unit %d after substitution", clause[0]);
|
||||
assign_unit (clause[0]);
|
||||
mark_garbage (c);
|
||||
new_unit = true;
|
||||
garbage++;
|
||||
} else if (c->literals[0] != clause[0] ||
|
||||
c->literals[1] != clause[1]) {
|
||||
LOG ("need new clause since at least one watched literal changed");
|
||||
if (clause.size () == 2) new_binary_clause = true;
|
||||
size_t d_clause_idx = clauses.size ();
|
||||
Clause * d = new_clause_as (c);
|
||||
assert (clauses[d_clause_idx] == d);
|
||||
clauses[d_clause_idx] = c;
|
||||
clauses[i] = d;
|
||||
mark_garbage (c);
|
||||
garbage++;
|
||||
} else {
|
||||
LOG ("simply shrinking clause since watches did not change");
|
||||
assert (c->size > 2);
|
||||
if (!c->redundant) mark_removed (c);
|
||||
if (proof) {
|
||||
proof->add_derived_clause (clause);
|
||||
proof->delete_clause (c);
|
||||
}
|
||||
size_t l;
|
||||
for (l = 2; l < clause.size (); l++)
|
||||
c->literals[l] = clause[l];
|
||||
int flushed = c->size - (int) l;
|
||||
if (flushed) {
|
||||
if (l == 2) new_binary_clause = true;
|
||||
LOG ("flushed %d literals", flushed);
|
||||
(void) shrink_clause (c, l);
|
||||
} else if (likely_to_be_kept_clause (c)) mark_added (c);
|
||||
LOG (c, "substituted");
|
||||
}
|
||||
while (!clause.empty ()) {
|
||||
int lit = clause.back ();
|
||||
clause.pop_back ();
|
||||
assert (marked (lit) > 0);
|
||||
unmark (lit);
|
||||
}
|
||||
}
|
||||
|
||||
if (!unsat && !postponed_garbage.empty ()) {
|
||||
LOG ("now marking %zd postponed garbage clauses",
|
||||
postponed_garbage.size ());
|
||||
for (const auto & c : postponed_garbage)
|
||||
mark_garbage (c);
|
||||
}
|
||||
erase_vector (postponed_garbage);
|
||||
|
||||
PHASE ("decompose",
|
||||
stats.decompositions,
|
||||
"%zd clauses replaced %.2f%% producing %zd garbage clauses %.2f%%",
|
||||
replaced, percent (replaced, clauses_size),
|
||||
garbage, percent (garbage, replaced));
|
||||
|
||||
erase_vector (scc);
|
||||
|
||||
// Propagate found units.
|
||||
|
||||
if (!unsat && propagated < trail.size () && !propagate ()) {
|
||||
LOG ("empty clause after propagating units from substitution");
|
||||
learn_empty_clause ();
|
||||
}
|
||||
|
||||
// Finally, mark substituted literals as such and push the equivalences of
|
||||
// the substituted literals to their representative on the extension
|
||||
// stack to fix an assignment during 'extend'.
|
||||
//
|
||||
// TODO instead of adding the clauses to the extension stack one could
|
||||
// also just simply use the 'e2i' map as a union find data structure.
|
||||
// This would avoid the need to restore these clauses.
|
||||
|
||||
for (auto idx : vars) {
|
||||
if (unsat) break;
|
||||
if (!active (idx)) continue;
|
||||
int other = reprs [ vlit (idx) ];
|
||||
if (other == idx) continue;
|
||||
assert (!flags (other).eliminated ());
|
||||
assert (!flags (other).substituted ());
|
||||
if (!flags (other).fixed ()) mark_substituted (idx);
|
||||
external->push_binary_clause_on_extension_stack (-idx, other);
|
||||
external->push_binary_clause_on_extension_stack (idx, -other);
|
||||
}
|
||||
|
||||
delete [] reprs;
|
||||
|
||||
flush_all_occs_and_watches (); // particularly the 'blit's
|
||||
|
||||
bool success = unsat ||
|
||||
(substituted > 0 && (new_unit || new_binary_clause));
|
||||
report ('d', !opts.reportall && !success);
|
||||
|
||||
STOP_SIMPLIFIER (decompose, DECOMP);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void Internal::decompose () {
|
||||
for (int round = 1; round <= opts.decomposerounds; round++)
|
||||
if (!decompose_round ())
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
139
hCaD_V2/src/deduplicate.cpp
Normal file
139
hCaD_V2/src/deduplicate.cpp
Normal file
@ -0,0 +1,139 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// Equivalent literal substitution in 'decompose' and shrinking in 'subsume'
|
||||
// or 'vivify' might produce duplicated binary clauses. They can not be
|
||||
// found in 'subsume' nor 'vivify' since we explicitly do not consider
|
||||
// binary clauses as candidates to be shrunken or subsumed. They are
|
||||
// detected here by a simple scan of watch lists and then marked as garbage.
|
||||
// This is actually also quite fast.
|
||||
|
||||
// Further it might also be possible that two binary clauses can be resolved
|
||||
// to produce a unit (we call it 'hyper unary resolution'). For example
|
||||
// resolving the binary clauses '1 -2' and '1 2' produces the unit '1'.
|
||||
// This could be found by probing in 'probe' unless '-1' also occurs in a
|
||||
// binary clause (add the clause '-1 2' to those two clauses) in which case
|
||||
// '1' as well as '2' both occur positively as well as negatively and none
|
||||
// of them nor their negation is considered as probe
|
||||
|
||||
void Internal::mark_duplicated_binary_clauses_as_garbage () {
|
||||
|
||||
if (!opts.deduplicate) return;
|
||||
if (unsat) return;
|
||||
if (terminated_asynchronously ()) return;
|
||||
|
||||
START_SIMPLIFIER (deduplicate, DEDUP);
|
||||
stats.deduplications++;
|
||||
|
||||
assert (!level);
|
||||
assert (watching ());
|
||||
|
||||
vector<int> stack; // To save marked literals and unmark them later.
|
||||
|
||||
int64_t subsumed = 0;
|
||||
int64_t units = 0;
|
||||
|
||||
for (auto idx : vars) {
|
||||
|
||||
if (unsat) break;
|
||||
if (!active (idx)) continue;
|
||||
int unit = 0;
|
||||
|
||||
for (int sign = -1; !unit && sign <= 1; sign += 2) {
|
||||
|
||||
const int lit = sign * idx; // Consider all literals.
|
||||
|
||||
assert (stack.empty ());
|
||||
Watches & ws = watches (lit);
|
||||
|
||||
// We are removing references to garbage clause. Thus no 'auto'.
|
||||
|
||||
const const_watch_iterator end = ws.end ();
|
||||
watch_iterator j = ws.begin ();
|
||||
const_watch_iterator i;
|
||||
|
||||
for (i = j; !unit && i != end; i++) {
|
||||
Watch w = *j++ = *i;
|
||||
if (!w.binary ()) continue;
|
||||
int other = w.blit;
|
||||
const int tmp = marked (other);
|
||||
Clause * c = w.clause;
|
||||
|
||||
if (tmp > 0) { // Found duplicated binary clause.
|
||||
|
||||
if (c->garbage) { j--; continue; }
|
||||
LOG (c, "found duplicated");
|
||||
|
||||
// The previous identical clause 'd' might be redundant and if the
|
||||
// second clause 'c' is not (so irredundant), then we have to keep
|
||||
// 'c' instead of 'd', thus we search for it and replace it.
|
||||
|
||||
if (!c->redundant) {
|
||||
watch_iterator k;
|
||||
for (k = ws.begin ();;k++) {
|
||||
assert (k != i);
|
||||
if (!k->binary ()) continue;
|
||||
if (k->blit != other) continue;
|
||||
Clause * d = k->clause;
|
||||
if (d->garbage) continue;
|
||||
c = d;
|
||||
break;
|
||||
}
|
||||
*k = w;
|
||||
}
|
||||
|
||||
LOG (c, "mark garbage duplicated");
|
||||
stats.subsumed++;
|
||||
stats.deduplicated++;
|
||||
subsumed++;
|
||||
mark_garbage (c);
|
||||
j--;
|
||||
|
||||
} else if (tmp < 0) { // Hyper unary resolution.
|
||||
|
||||
LOG ("found %d %d and %d %d which produces unit %d",
|
||||
lit, -other, lit, other, lit);
|
||||
unit = lit;
|
||||
j = ws.begin (); // Flush 'ws'.
|
||||
units++;
|
||||
|
||||
} else {
|
||||
if (c->garbage) continue;
|
||||
mark (other);
|
||||
stack.push_back (other);
|
||||
}
|
||||
}
|
||||
|
||||
if (j == ws.begin ()) erase_vector (ws);
|
||||
else if (j != end)
|
||||
ws.resize (j - ws.begin ()); // Shrink watchers.
|
||||
|
||||
for (const auto & other : stack)
|
||||
unmark (other);
|
||||
|
||||
stack.clear ();
|
||||
}
|
||||
|
||||
// Propagation potentially messes up the watches and thus we can not
|
||||
// propagate the unit immediately after finding it. Instead we break
|
||||
// out of both loops and assign and propagate the unit here.
|
||||
|
||||
if (unit) {
|
||||
|
||||
stats.failed++;
|
||||
stats.hyperunary++;
|
||||
assign_unit (unit);
|
||||
|
||||
if (!propagate ()) {
|
||||
LOG ("empty clause after propagating unit");
|
||||
learn_empty_clause ();
|
||||
}
|
||||
}
|
||||
}
|
||||
STOP_SIMPLIFIER (deduplicate, DEDUP);
|
||||
|
||||
report ('2', !opts.reportall && !(subsumed + units));
|
||||
}
|
||||
|
||||
}
|
960
hCaD_V2/src/elim.cpp
Normal file
960
hCaD_V2/src/elim.cpp
Normal file
@ -0,0 +1,960 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Implements a variant of bounded variable elimination as originally
|
||||
// described in our SAT'05 paper introducing 'SATeLite'. This is an
|
||||
// inprocessing version, i.e., it is interleaved with search and triggers
|
||||
// subsumption and strengthening, blocked and covered clause elimination
|
||||
// during elimination rounds. It focuses only those variables which
|
||||
// occurred in removed irredundant clauses since the last time an
|
||||
// elimination round was run. By bounding the maximum resolvent size we can
|
||||
// run each elimination round until completion. See the code of 'elim' for
|
||||
// how elimination rounds are interleaved with blocked clause elimination
|
||||
// and subsumption (which in turn also calls vivification and transitive
|
||||
// reduction of the binary implication graph).
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
inline double Internal::compute_elim_score (unsigned lit) {
|
||||
assert (1 <= lit), assert (lit <= (unsigned) max_var);
|
||||
const unsigned uidx = 2*lit;
|
||||
const double pos = internal->ntab [uidx];
|
||||
const double neg = internal->ntab [uidx + 1];
|
||||
if (!pos) return -neg;
|
||||
if (!neg) return -pos;
|
||||
double sum = 0, prod = 0;
|
||||
if (opts.elimsum) sum = opts.elimsum * (pos + neg);
|
||||
if (opts.elimprod) prod = opts.elimprod * (pos * neg);
|
||||
return prod + sum;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
inline bool elim_more::operator () (unsigned a, unsigned b) {
|
||||
const auto s = internal->compute_elim_score (a);
|
||||
const auto t = internal->compute_elim_score (b);
|
||||
if (s > t) return true;
|
||||
if (s < t) return false;
|
||||
return a > b;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Note that the new fast subsumption algorithm implemented in 'subsume'
|
||||
// does not distinguish between irredundant and redundant clauses and is
|
||||
// also run during search to strengthen and remove 'sticky' redundant
|
||||
// clauses but also irredundant ones. So beside learned units during search
|
||||
// or as consequence of other preprocessors, these subsumption rounds during
|
||||
// search can remove (irredundant) clauses (and literals), which in turn
|
||||
// might make new bounded variable elimination possible. This is tested
|
||||
// in the 'bool eliminating ()' guard.
|
||||
|
||||
bool Internal::eliminating () {
|
||||
|
||||
if (!opts.elim) return false;
|
||||
if (!preprocessing && !opts.inprocessing) return false;
|
||||
if (preprocessing) assert (lim.preprocessing);
|
||||
|
||||
// Respect (increasing) conflict limit.
|
||||
//
|
||||
if (lim.elim >= stats.conflicts) return false;
|
||||
|
||||
// Wait until there are new units or new removed variables
|
||||
// (in removed or shrunken irredundant clauses and thus marked).
|
||||
//
|
||||
if (last.elim.fixed < stats.all.fixed) return true;
|
||||
if (last.elim.marked < stats.mark.elim) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Update the global elimination schedule after adding or removing a clause.
|
||||
|
||||
void
|
||||
Internal::elim_update_added_clause (Eliminator & eliminator, Clause * c) {
|
||||
assert (!c->redundant);
|
||||
ElimSchedule & schedule = eliminator.schedule;
|
||||
for (const auto & lit : *c) {
|
||||
if (!active (lit)) continue;
|
||||
occs (lit).push_back (c);
|
||||
if (frozen (lit)) continue;
|
||||
noccs (lit)++;
|
||||
const int idx = abs (lit);
|
||||
if (schedule.contains (idx)) schedule.update (idx);
|
||||
}
|
||||
}
|
||||
|
||||
void Internal::elim_update_removed_lit (Eliminator & eliminator, int lit) {
|
||||
if (!active (lit)) return;
|
||||
if (frozen (lit)) return;
|
||||
int64_t & score = noccs (lit);
|
||||
assert (score > 0);
|
||||
score--;
|
||||
const int idx = abs (lit);
|
||||
ElimSchedule & schedule = eliminator.schedule;
|
||||
if (schedule.contains (idx)) schedule.update (idx);
|
||||
else {
|
||||
LOG ("rescheduling %d for elimination after removing clause", idx);
|
||||
schedule.push_back (idx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Internal::elim_update_removed_clause (Eliminator & eliminator,
|
||||
Clause * c, int except)
|
||||
{
|
||||
assert (!c->redundant);
|
||||
for (const auto & lit : *c) {
|
||||
if (lit == except) continue;
|
||||
assert (lit != -except);
|
||||
elim_update_removed_lit (eliminator, lit);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Since we do not have watches we have to do our own unit propagation
|
||||
// during elimination as soon we find a unit clause. This finds new units
|
||||
// and also marks clauses satisfied by those units as garbage immediately.
|
||||
|
||||
void Internal::elim_propagate (Eliminator & eliminator, int root) {
|
||||
assert (val (root) > 0);
|
||||
vector<int> work;
|
||||
size_t i = 0;
|
||||
work.push_back (root);
|
||||
while (i < work.size ()) {
|
||||
int lit = work[i++];
|
||||
LOG ("elimination propagation of %d", lit);
|
||||
assert (val (lit) > 0);
|
||||
const Occs & ns = occs (-lit);
|
||||
for (const auto & c : ns) {
|
||||
if (c->garbage) continue;
|
||||
int unit = 0, satisfied = 0;
|
||||
for (const auto & other : *c) {
|
||||
const signed char tmp = val (other);
|
||||
if (tmp < 0) continue;
|
||||
if (tmp > 0) { satisfied = other; break; }
|
||||
if (unit) unit = INT_MIN;
|
||||
else unit = other;
|
||||
}
|
||||
if (satisfied) {
|
||||
LOG (c, "elimination propagation of %d finds %d satisfied",
|
||||
lit, satisfied);
|
||||
elim_update_removed_clause (eliminator, c, satisfied);
|
||||
mark_garbage (c);
|
||||
} else if (!unit) {
|
||||
LOG ("empty clause during elimination propagation of %d", lit);
|
||||
learn_empty_clause ();
|
||||
break;
|
||||
} else if (unit != INT_MIN) {
|
||||
LOG ("new unit %d during elimination propagation of %d", unit, lit);
|
||||
assign_unit (unit);
|
||||
work.push_back (unit);
|
||||
}
|
||||
}
|
||||
if (unsat) break;
|
||||
const Occs & ps = occs (lit);
|
||||
for (const auto & c : ps) {
|
||||
if (c->garbage) continue;
|
||||
LOG (c, "elimination propagation of %d produces satisfied", lit);
|
||||
elim_update_removed_clause (eliminator, c, lit);
|
||||
mark_garbage (c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// On-the-fly self-subsuming resolution during variable elimination is due
|
||||
// to HyoJung Han, Fabio Somenzi, SAT'09. Basically while resolving two
|
||||
// clauses we test the resolvent to be smaller than one of the antecedents.
|
||||
// If this is the case the pivot can be removed from the antecedent
|
||||
// on-the-fly and the resolution can be skipped during elimination.
|
||||
|
||||
void Internal::elim_on_the_fly_self_subsumption (Eliminator & eliminator,
|
||||
Clause * c, int pivot)
|
||||
{
|
||||
LOG (c, "pivot %d on-the-fly self-subsuming resolution", pivot);
|
||||
stats.elimotfstr++;
|
||||
stats.strengthened++;
|
||||
assert (clause.empty ());
|
||||
for (const auto & lit : *c) {
|
||||
if (lit == pivot) continue;
|
||||
const signed char tmp = val (lit);
|
||||
assert (tmp <= 0);
|
||||
if (tmp < 0) continue;
|
||||
clause.push_back (lit);
|
||||
}
|
||||
Clause * r = new_resolved_irredundant_clause ();
|
||||
elim_update_added_clause (eliminator, r);
|
||||
clause.clear ();
|
||||
elim_update_removed_clause (eliminator, c, pivot);
|
||||
mark_garbage (c);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Resolve two clauses on the pivot literal 'pivot', which is assumed to
|
||||
// occur in opposite phases in 'c' and 'd'. The actual resolvent is stored
|
||||
// in the temporary global 'clause' if it is not redundant. It is
|
||||
// considered redundant if one of the clauses is already marked as garbage
|
||||
// it is root level satisfied, the resolvent is empty, a unit, or produces a
|
||||
// self-subsuming resolution, which results in the pivot to be removed from
|
||||
// at least one of the antecedents.
|
||||
|
||||
// Note that current root level assignments are taken into account, i.e., by
|
||||
// removing root level falsified literals. The function returns true if the
|
||||
// resolvent is not redundant and for instance has to be taken into account
|
||||
// during bounded variable elimination.
|
||||
|
||||
// Detected units are immediately assigned and in case the last argument is
|
||||
// true also propagated eagerly in a elimination specific propagation
|
||||
// routine, which not only finds units but also updates the schedule.
|
||||
|
||||
// When this function is called during computation of the number of
|
||||
// non-trivial (or non-satisfied) resolvents we can eagerly propagate units.
|
||||
// But during actually adding the resolvents this results in problems as we
|
||||
// found one rare test case '../test/trace/reg0056.trace' (out of billions),
|
||||
// where the pivot itself was assigned during such a propagation while
|
||||
// adding resolvents and lead to pushing a clause to the reconstruction
|
||||
// stack that later flipped the value of the pivot (while all other literals
|
||||
// in that clause were unit implied too). Not pushing the pivot clauses to
|
||||
// the reconstruction stack produced a wrong model too. Our fix is to only
|
||||
// eagerly propagate during computation of the number of resolvents and
|
||||
// otherwise delay propagation until the end of elimination (which is less
|
||||
// precise regarding scheduling but very rarely happens).
|
||||
|
||||
bool Internal::resolve_clauses (Eliminator & eliminator,
|
||||
Clause * c, int pivot, Clause * d,
|
||||
const bool propagate_eagerly) {
|
||||
|
||||
assert (!c->redundant);
|
||||
assert (!d->redundant);
|
||||
|
||||
stats.elimres++;
|
||||
|
||||
if (c->garbage || d->garbage) return false;
|
||||
if (c->size > d->size) { pivot = -pivot; swap (c, d); }
|
||||
|
||||
assert (!level);
|
||||
assert (clause.empty ());
|
||||
|
||||
int satisfied = 0; // Contains this satisfying literal.
|
||||
int tautological = 0; // Clashing literal if tautological.
|
||||
|
||||
int s = 0; // Actual literals from 'c'.
|
||||
int t = 0; // Actual literals from 'd'.
|
||||
|
||||
// First determine whether the first antecedent is satisfied, add its
|
||||
// literals to 'clause' and mark them (except for 'pivot').
|
||||
//
|
||||
for (const auto & lit : *c) {
|
||||
if (lit == pivot) { s++; continue; }
|
||||
assert (lit != -pivot);
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = lit; break; }
|
||||
else if (tmp < 0) continue;
|
||||
else mark (lit), clause.push_back (lit), s++;
|
||||
}
|
||||
if (satisfied) {
|
||||
LOG (c, "satisfied by %d antecedent", satisfied);
|
||||
elim_update_removed_clause (eliminator, c, satisfied);
|
||||
mark_garbage (c);
|
||||
clause.clear ();
|
||||
unmark (c);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then determine whether the second antecedent is satisfied, add its
|
||||
// literal to 'clause' and check whether a clashing literal is found, such
|
||||
// that the resolvent would be tautological.
|
||||
//
|
||||
for (const auto & lit : *d) {
|
||||
if (lit == -pivot) { t++; continue; }
|
||||
assert (lit != pivot);
|
||||
signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = lit; break; }
|
||||
else if (tmp < 0) continue;
|
||||
else if ((tmp = marked (lit)) < 0) { tautological = lit; break; }
|
||||
else if (!tmp) clause.push_back (lit), t++;
|
||||
else assert (tmp > 0), t++;
|
||||
}
|
||||
|
||||
unmark (c);
|
||||
const int64_t size = clause.size ();
|
||||
|
||||
if (satisfied) {
|
||||
LOG (d, "satisfied by %d antecedent", satisfied);
|
||||
elim_update_removed_clause (eliminator, d, satisfied);
|
||||
mark_garbage (d);
|
||||
clause.clear ();
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG (c, "first antecedent");
|
||||
LOG (d, "second antecedent");
|
||||
|
||||
if (tautological) {
|
||||
clause.clear ();
|
||||
LOG ("resolvent tautological on %d", tautological);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!size) {
|
||||
clause.clear ();
|
||||
LOG ("empty resolvent");
|
||||
learn_empty_clause ();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
int unit = clause[0];
|
||||
LOG ("unit resolvent %d", unit);
|
||||
clause.clear ();
|
||||
assign_unit (unit);
|
||||
if (propagate_eagerly)
|
||||
elim_propagate (eliminator, unit);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG (clause, "resolvent");
|
||||
|
||||
// Double self-subsuming resolution. The clauses 'c' and 'd' are
|
||||
// identical except for the pivot which occurs in different phase. The
|
||||
// resolvent subsumes both antecedents.
|
||||
|
||||
if (s > size && t > size) {
|
||||
assert (s == size + 1);
|
||||
assert (t == size + 1);
|
||||
clause.clear ();
|
||||
elim_on_the_fly_self_subsumption (eliminator, c, pivot);
|
||||
LOG (d, "double pivot %d on-the-fly self-subsuming resolution", -pivot);
|
||||
stats.elimotfsub++;
|
||||
stats.subsumed++;
|
||||
elim_update_removed_clause (eliminator, d, -pivot);
|
||||
mark_garbage (d);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Single self-subsuming resolution: The pivot can be removed from 'c',
|
||||
// which is implemented by adding a clause which is the same as 'c' but
|
||||
// with 'pivot' removed and then marking 'c' as garbage.
|
||||
|
||||
if (s > size) {
|
||||
assert (s == size + 1);
|
||||
clause.clear ();
|
||||
elim_on_the_fly_self_subsumption (eliminator, c, pivot);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Same single self-subsuming resolution situation, but only for 'd'.
|
||||
|
||||
if (t > size) {
|
||||
assert (t == size + 1);
|
||||
clause.clear ();
|
||||
elim_on_the_fly_self_subsumption (eliminator, d, -pivot);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Check whether the number of non-tautological resolvents on 'pivot' is
|
||||
// smaller or equal to the number of clauses with 'pivot' or '-pivot'. This
|
||||
// is the main criteria of bounded variable elimination. As a side effect
|
||||
// it flushes garbage clauses with that variable, sorts its occurrence lists
|
||||
// (smallest clauses first) and also negates pivot if it has more positive
|
||||
// than negative occurrences.
|
||||
|
||||
bool
|
||||
Internal::elim_resolvents_are_bounded (Eliminator & eliminator, int pivot)
|
||||
{
|
||||
const bool substitute = !eliminator.gates.empty ();
|
||||
if (substitute) LOG ("trying to substitute %d", pivot);
|
||||
|
||||
stats.elimtried++;
|
||||
|
||||
assert (!unsat);
|
||||
assert (active (pivot));
|
||||
|
||||
const Occs & ps = occs (pivot);
|
||||
const Occs & ns = occs (-pivot);
|
||||
const int64_t pos = ps.size ();
|
||||
const int64_t neg = ns.size ();
|
||||
if (!pos || !neg) return lim.elimbound >= 0;
|
||||
const int64_t bound = pos + neg + lim.elimbound;
|
||||
|
||||
LOG ("checking number resolvents on %d bounded by "
|
||||
"%" PRId64 " = %" PRId64 " + %" PRId64 " + %" PRId64,
|
||||
pivot, bound, pos, neg, lim.elimbound);
|
||||
|
||||
// Try all resolutions between a positive occurrence (outer loop) of
|
||||
// 'pivot' and a negative occurrence of 'pivot' (inner loop) as long the
|
||||
// bound on non-tautological resolvents is not hit and the size of the
|
||||
// generated resolvents does not exceed the resolvent clause size limit.
|
||||
|
||||
int64_t resolvents = 0; // Non-tautological resolvents.
|
||||
|
||||
for (const auto & c : ps) {
|
||||
assert (!c->redundant);
|
||||
if (c->garbage) continue;
|
||||
for (const auto & d : ns) {
|
||||
assert (!d->redundant);
|
||||
if (d->garbage) continue;
|
||||
if (substitute && c->gate == d->gate) continue;
|
||||
stats.elimrestried++;
|
||||
if (resolve_clauses (eliminator, c, pivot, d, true)) {
|
||||
resolvents++;
|
||||
int size = clause.size ();
|
||||
clause.clear ();
|
||||
LOG ("now at least %" PRId64
|
||||
" non-tautological resolvents on pivot %d",
|
||||
resolvents, pivot);
|
||||
if (size > opts.elimclslim) {
|
||||
LOG ("resolvent size %d too big after %" PRId64
|
||||
" resolvents on %d",
|
||||
size, resolvents, pivot);
|
||||
return false;
|
||||
}
|
||||
if (resolvents > bound) {
|
||||
LOG ("too many non-tautological resolvents on %d", pivot);
|
||||
return false;
|
||||
}
|
||||
} else if (unsat) return false;
|
||||
else if (val (pivot)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG ("need %" PRId64 " <= %" PRId64 " non-tautological resolvents",
|
||||
resolvents, bound);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
// Add all resolvents on 'pivot' and connect them.
|
||||
|
||||
inline void
|
||||
Internal::elim_add_resolvents (Eliminator & eliminator, int pivot) {
|
||||
|
||||
const bool substitute = !eliminator.gates.empty ();
|
||||
if (substitute) {
|
||||
LOG ("substituting pivot %d by resolving with %zd gate clauses",
|
||||
pivot, eliminator.gates.size ());
|
||||
stats.elimsubst++;
|
||||
}
|
||||
|
||||
LOG ("adding all resolvents on %d", pivot);
|
||||
|
||||
assert (!val (pivot));
|
||||
assert (!flags (pivot).eliminated ());
|
||||
|
||||
const Occs & ps = occs (pivot);
|
||||
const Occs & ns = occs (-pivot);
|
||||
|
||||
int64_t resolvents = 0;
|
||||
|
||||
for (auto & c : ps) {
|
||||
if (unsat) break;
|
||||
if (c->garbage) continue;
|
||||
for (auto & d : ns) {
|
||||
if (unsat) break;
|
||||
if (d->garbage) continue;
|
||||
if (substitute && c->gate == d->gate) continue;
|
||||
if (!resolve_clauses (eliminator, c, pivot, d, false)) continue;
|
||||
Clause * r = new_resolved_irredundant_clause ();
|
||||
elim_update_added_clause (eliminator, r);
|
||||
eliminator.enqueue (r);
|
||||
clause.clear ();
|
||||
resolvents++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
LOG ("added %" PRId64 " resolvents to eliminate %d", resolvents, pivot);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Remove clauses with 'pivot' and '-pivot' by marking them as garbage and
|
||||
// push them on the extension stack.
|
||||
|
||||
void
|
||||
Internal::mark_eliminated_clauses_as_garbage (Eliminator & eliminator,
|
||||
int pivot)
|
||||
{
|
||||
assert (!unsat);
|
||||
|
||||
LOG ("marking irredundant clauses with %d as garbage", pivot);
|
||||
|
||||
const int64_t substitute = eliminator.gates.size ();
|
||||
if (substitute)
|
||||
LOG ("pushing %" PRId64 " gate clauses on extension stack", substitute);
|
||||
|
||||
int64_t pushed = 0;
|
||||
|
||||
Occs & ps = occs (pivot);
|
||||
for (const auto & c : ps) {
|
||||
if (c->garbage) continue;
|
||||
mark_garbage (c);
|
||||
assert (!c->redundant);
|
||||
if (!substitute || c->gate) {
|
||||
external->push_clause_on_extension_stack (c, pivot);
|
||||
pushed++;
|
||||
}
|
||||
elim_update_removed_clause (eliminator, c, pivot);
|
||||
}
|
||||
erase_occs (ps);
|
||||
|
||||
LOG ("marking irredundant clauses with %d as garbage", -pivot);
|
||||
|
||||
Occs & ns = occs (-pivot);
|
||||
for (const auto & d : ns) {
|
||||
if (d->garbage) continue;
|
||||
mark_garbage (d);
|
||||
assert (!d->redundant);
|
||||
if (!substitute || d->gate) {
|
||||
external->push_clause_on_extension_stack (d, -pivot);
|
||||
pushed++;
|
||||
}
|
||||
elim_update_removed_clause (eliminator, d, -pivot);
|
||||
}
|
||||
erase_occs (ns);
|
||||
|
||||
if (substitute) assert (pushed <= substitute);
|
||||
|
||||
// Unfortunately, we can not use the trick by Niklas Soerensson anymore,
|
||||
// which avoids saving all clauses on the extension stack. This would
|
||||
// break our new incremental 'restore' logic.
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Try to eliminate 'pivot' by bounded variable elimination.
|
||||
|
||||
void
|
||||
Internal::try_to_eliminate_variable (Eliminator & eliminator, int pivot) {
|
||||
|
||||
if (!active (pivot)) return;
|
||||
assert (!frozen (pivot));
|
||||
|
||||
// First flush garbage clauses.
|
||||
//
|
||||
int64_t pos = flush_occs (pivot);
|
||||
int64_t neg = flush_occs (-pivot);
|
||||
|
||||
if (pos > neg) { pivot = -pivot; swap (pos, neg); }
|
||||
LOG ("pivot %d occurs positively %" PRId64 " times and negatively %" PRId64 " times",
|
||||
pivot, pos, neg);
|
||||
assert (!eliminator.schedule.contains (abs (pivot)));
|
||||
assert (pos <= neg);
|
||||
|
||||
if (pos && neg > opts.elimocclim) {
|
||||
LOG ("too many occurrences thus not eliminated %d", pivot);
|
||||
assert (!eliminator.schedule.contains (abs (pivot)));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG ("trying to eliminate %d", pivot);
|
||||
assert (!flags (pivot).eliminated ());
|
||||
|
||||
// Sort occurrence lists, such that shorter clauses come first.
|
||||
Occs & ps = occs (pivot);
|
||||
stable_sort (ps.begin (), ps.end (), clause_smaller_size ());
|
||||
Occs & ns = occs (-pivot);
|
||||
stable_sort (ns.begin (), ns.end (), clause_smaller_size ());
|
||||
|
||||
if (pos) find_gate_clauses (eliminator, pivot);
|
||||
|
||||
if (!unsat && !val (pivot)) {
|
||||
if (elim_resolvents_are_bounded (eliminator, pivot)) {
|
||||
LOG ("number of resolvents on %d are bounded", pivot);
|
||||
elim_add_resolvents (eliminator, pivot);
|
||||
if (!unsat) mark_eliminated_clauses_as_garbage (eliminator, pivot);
|
||||
if (active (pivot)) mark_eliminated (pivot);
|
||||
} else LOG ("too many resolvents on %d so not eliminated", pivot);
|
||||
}
|
||||
|
||||
unmark_gate_clauses (eliminator);
|
||||
elim_backward_clauses (eliminator);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void
|
||||
Internal::mark_redundant_clauses_with_eliminated_variables_as_garbage () {
|
||||
for (const auto & c : clauses) {
|
||||
if (c->garbage || !c->redundant) continue;
|
||||
bool clean = true;
|
||||
for (const auto & lit : *c) {
|
||||
Flags & f = flags (lit);
|
||||
if (f.eliminated ()) { clean = false; break; }
|
||||
if (f.pure ()) { clean = false; break; }
|
||||
}
|
||||
if (!clean) mark_garbage (c);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This function performs one round of bounded variable elimination and
|
||||
// returns the number of eliminated variables. The additional result
|
||||
// 'completed' is true if this elimination round ran to completion (all
|
||||
// variables have been tried). Otherwise it was asynchronously terminated
|
||||
// or the resolution limit was hit.
|
||||
|
||||
int Internal::elim_round (bool & completed) {
|
||||
|
||||
assert (opts.elim);
|
||||
assert (!unsat);
|
||||
|
||||
START_SIMPLIFIER (elim, ELIM);
|
||||
stats.elimrounds++;
|
||||
|
||||
last.elim.marked = stats.mark.elim;
|
||||
assert (!level);
|
||||
|
||||
int64_t resolution_limit;
|
||||
|
||||
if (opts.elimlimited) {
|
||||
int64_t delta = stats.propagations.search;
|
||||
delta *= 1e-3 * opts.elimreleff;
|
||||
if (delta < opts.elimineff) delta = opts.elimineff;
|
||||
if (delta > opts.elimaxeff) delta = opts.elimaxeff;
|
||||
delta = max (delta, (int64_t) 2l * active ());
|
||||
|
||||
PHASE ("elim-round", stats.elimrounds,
|
||||
"limit of %" PRId64 " resolutions", delta);
|
||||
|
||||
resolution_limit = stats.elimres + delta;
|
||||
} else {
|
||||
PHASE ("elim-round", stats.elimrounds, "resolutions unlimited");
|
||||
resolution_limit = LONG_MAX;
|
||||
}
|
||||
|
||||
init_noccs ();
|
||||
|
||||
// First compute the number of occurrences of each literal and at the same
|
||||
// time mark satisfied clauses and update 'elim' flags of variables in
|
||||
// clauses with root level assigned literals (both false and true).
|
||||
//
|
||||
for (const auto & c : clauses) {
|
||||
if (c->garbage || c->redundant) continue;
|
||||
bool satisfied = false, falsified = false;
|
||||
for (const auto & lit : *c) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) satisfied = true;
|
||||
else if (tmp < 0) falsified = true;
|
||||
else assert (active (lit));
|
||||
}
|
||||
if (satisfied) mark_garbage (c); // forces more precise counts
|
||||
else {
|
||||
for (const auto & lit : *c) {
|
||||
if (!active (lit)) continue;
|
||||
if (falsified) mark_elim (lit); // simulate unit propagation
|
||||
noccs (lit)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_occs ();
|
||||
|
||||
Eliminator eliminator (this);
|
||||
ElimSchedule & schedule = eliminator.schedule;
|
||||
|
||||
// Now find elimination candidates which occurred in clauses removed since
|
||||
// the last time we ran bounded variable elimination, which in turned
|
||||
// triggered their 'elim' bit to be set.
|
||||
//
|
||||
for (auto idx : vars) {
|
||||
if (!active (idx)) continue;
|
||||
if (frozen (idx)) continue;
|
||||
if (!flags (idx).elim) continue;
|
||||
flags (idx).elim = false;
|
||||
LOG ("scheduling %d for elimination initially", idx);
|
||||
schedule.push_back (idx);
|
||||
}
|
||||
|
||||
schedule.shrink ();
|
||||
|
||||
#ifndef QUIET
|
||||
int64_t scheduled = schedule.size ();
|
||||
#endif
|
||||
|
||||
PHASE ("elim-round", stats.elimrounds,
|
||||
"scheduled %" PRId64 " variables %.0f%% for elimination",
|
||||
scheduled, percent (scheduled, active ()));
|
||||
|
||||
// Connect irredundant clauses.
|
||||
//
|
||||
for (const auto & c : clauses)
|
||||
if (!c->garbage && !c->redundant)
|
||||
for (const auto & lit : *c)
|
||||
if (active (lit))
|
||||
occs (lit).push_back (c);
|
||||
|
||||
#ifndef QUIET
|
||||
const int64_t old_resolutions = stats.elimres;
|
||||
#endif
|
||||
const int old_eliminated = stats.all.eliminated;
|
||||
const int old_fixed = stats.all.fixed;
|
||||
|
||||
// Limit on garbage bytes during variable elimination. If the limit is hit
|
||||
// a garbage collection is performed.
|
||||
//
|
||||
const int64_t garbage_limit = (2*stats.irrbytes/3) + (1<<20);
|
||||
|
||||
// Main loops tries to eliminate variables according to the schedule. The
|
||||
// schedule is updated dynamically and variables are potentially
|
||||
// rescheduled to be tried again if they occur in a removed clause.
|
||||
//
|
||||
#ifndef QUIET
|
||||
int64_t tried = 0;
|
||||
#endif
|
||||
while (!unsat &&
|
||||
!terminated_asynchronously () &&
|
||||
stats.elimres <= resolution_limit &&
|
||||
!schedule.empty ()) {
|
||||
int idx = schedule.front ();
|
||||
schedule.pop_front ();
|
||||
flags (idx).elim = false;
|
||||
try_to_eliminate_variable (eliminator, idx);
|
||||
#ifndef QUIET
|
||||
tried++;
|
||||
#endif
|
||||
if (stats.garbage <= garbage_limit) continue;
|
||||
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
|
||||
garbage_collection ();
|
||||
}
|
||||
|
||||
// If the schedule is empty all variables have been tried (even
|
||||
// rescheduled ones). Otherwise asynchronous termination happened or we
|
||||
// ran into the resolution limit (or derived unsatisfiability).
|
||||
//
|
||||
completed = !schedule.size ();
|
||||
|
||||
PHASE ("elim-round", stats.elimrounds,
|
||||
"tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)",
|
||||
tried, percent (tried, scheduled), schedule.size ());
|
||||
|
||||
schedule.erase ();
|
||||
|
||||
// Collect potential literal clause instantiation pairs, which needs full
|
||||
// occurrence lists and thus we have it here before resetting them.
|
||||
//
|
||||
Instantiator instantiator;
|
||||
if (!unsat &&
|
||||
!terminated_asynchronously () &&
|
||||
opts.instantiate)
|
||||
collect_instantiation_candidates (instantiator);
|
||||
|
||||
reset_occs ();
|
||||
reset_noccs ();
|
||||
|
||||
// Mark all redundant clauses with eliminated variables as garbage.
|
||||
//
|
||||
if (!unsat)
|
||||
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
|
||||
|
||||
int eliminated = stats.all.eliminated - old_eliminated;
|
||||
#ifndef QUIET
|
||||
int64_t resolutions = stats.elimres - old_resolutions;
|
||||
PHASE ("elim-round", stats.elimrounds,
|
||||
"eliminated %d variables %.0f%% in %" PRId64 " resolutions",
|
||||
eliminated, percent (eliminated, scheduled), resolutions);
|
||||
#endif
|
||||
|
||||
last.elim.subsumephases = stats.subsumephases;
|
||||
const int units = stats.all.fixed - old_fixed;
|
||||
report ('e', !opts.reportall && !(eliminated + units));
|
||||
STOP_SIMPLIFIER (elim, ELIM);
|
||||
|
||||
if (!unsat &&
|
||||
!terminated_asynchronously () &&
|
||||
instantiator) // Do we have candidate pairs?
|
||||
instantiate (instantiator);
|
||||
|
||||
return eliminated; // non-zero if successful
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Increase elimination bound (additional clauses allowed during variable
|
||||
// elimination), which is triggered if elimination with last bound completed
|
||||
// (including no new subsumptions). This was pioneered by GlueMiniSAT in
|
||||
// the SAT Race 2015 and then picked up Chanseok Oh in his COMinisatPS
|
||||
// solver, which in turn is used in the Maple series of SAT solvers.
|
||||
// The bound is no increased if the maximum bound is reached.
|
||||
|
||||
void Internal::increase_elimination_bound () {
|
||||
|
||||
if (lim.elimbound >= opts.elimboundmax) return;
|
||||
|
||||
if (lim.elimbound < 0) lim.elimbound = 0;
|
||||
else if (!lim.elimbound) lim.elimbound = 1;
|
||||
else lim.elimbound *= 2;
|
||||
|
||||
if (lim.elimbound > opts.elimboundmax)
|
||||
lim.elimbound = opts.elimboundmax;
|
||||
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"new elimination bound %" PRId64 "", lim.elimbound);
|
||||
|
||||
// Now reschedule all active variables for elimination again.
|
||||
//
|
||||
int count = 0;
|
||||
for (auto idx : vars) {
|
||||
if (!active (idx)) continue;
|
||||
if (flags (idx).elim) continue;
|
||||
mark_elim (idx);
|
||||
count++;
|
||||
}
|
||||
LOG ("marked %d variables as elimination candidates", count);
|
||||
|
||||
report ('^');
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::elim (bool update_limits) {
|
||||
|
||||
if (unsat) return;
|
||||
if (level) backtrack ();
|
||||
if (!propagate ()) { learn_empty_clause (); return; }
|
||||
|
||||
stats.elimphases++;
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"starting at most %d elimination rounds",
|
||||
opts.elimrounds);
|
||||
|
||||
#ifndef QUIET
|
||||
int old_active_variables = active ();
|
||||
int old_eliminated = stats.all.eliminated;
|
||||
#endif
|
||||
|
||||
// Make sure there was a complete subsumption phase since last
|
||||
// elimination including vivification etc.
|
||||
//
|
||||
if (last.elim.subsumephases == stats.subsumephases)
|
||||
subsume (update_limits);
|
||||
|
||||
reset_watches (); // saves lots of memory
|
||||
|
||||
// Alternate one round of bounded variable elimination ('elim_round') and
|
||||
// subsumption ('subsume_round'), blocked ('block') and covered clause
|
||||
// elimination ('cover') until nothing changes, or the round limit is hit.
|
||||
// The loop also aborts early if no variable could be eliminated, the
|
||||
// empty clause is resolved, it is asynchronously terminated or a
|
||||
// resolution limit is hit.
|
||||
|
||||
// This variable determines whether the whole loop of this bounded
|
||||
// variable elimination phase ('elim') ran until completion. This
|
||||
// potentially triggers an incremental increase of the elimination bound.
|
||||
//
|
||||
bool phase_complete = false;
|
||||
|
||||
int round = 1;
|
||||
|
||||
while (!unsat &&
|
||||
!phase_complete &&
|
||||
!terminated_asynchronously ()) {
|
||||
|
||||
bool round_complete;
|
||||
|
||||
#ifndef QUIET
|
||||
int eliminated =
|
||||
#endif
|
||||
elim_round (round_complete);
|
||||
|
||||
if (!round_complete) {
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"last round %d incomplete %s",
|
||||
round, eliminated ? "but successful" : "and unsuccessful");
|
||||
assert (!phase_complete);
|
||||
break;
|
||||
}
|
||||
|
||||
if (round++ >= opts.elimrounds) {
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"round limit %d hit (%s)", round-1,
|
||||
eliminated ? "though last round successful" :
|
||||
"last round unsuccessful anyhow");
|
||||
assert (!phase_complete);
|
||||
break;
|
||||
}
|
||||
|
||||
// Prioritize 'subsumption' over blocked and covered clause elimination.
|
||||
|
||||
if (subsume_round ()) continue;
|
||||
if (block ()) continue;
|
||||
if (cover ()) continue;
|
||||
|
||||
// Was not able to generate new variable elimination candidates after
|
||||
// variable elimination round, neither through subsumption, nor blocked,
|
||||
// nor covered clause elimination.
|
||||
//
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"no new variable elimination candidates");
|
||||
|
||||
assert (round_complete);
|
||||
phase_complete = true;
|
||||
}
|
||||
|
||||
if (phase_complete) {
|
||||
stats.elimcompleted++;
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"fully completed elimination %" PRId64
|
||||
" at elimination bound %" PRId64 "",
|
||||
stats.elimcompleted, lim.elimbound);
|
||||
} else {
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"incomplete elimination %" PRId64
|
||||
" at elimination bound %" PRId64 "",
|
||||
stats.elimcompleted + 1, lim.elimbound);
|
||||
}
|
||||
|
||||
init_watches ();
|
||||
connect_watches ();
|
||||
|
||||
if (unsat) LOG ("elimination derived empty clause");
|
||||
else if (propagated < trail.size ()) {
|
||||
LOG ("elimination produced %zd units",
|
||||
(size_t)(trail.size () - propagated));
|
||||
if (!propagate ()) {
|
||||
LOG ("propagating units after elimination results in empty clause");
|
||||
learn_empty_clause ();
|
||||
}
|
||||
}
|
||||
|
||||
// If we ran variable elimination until completion we increase the
|
||||
// variable elimination bound and reschedule elimination of all variables.
|
||||
//
|
||||
if (phase_complete) increase_elimination_bound ();
|
||||
|
||||
#ifndef QUIET
|
||||
int eliminated = stats.all.eliminated - old_eliminated;
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"eliminated %d variables %.2f%%",
|
||||
eliminated, percent (eliminated, old_active_variables));
|
||||
#endif
|
||||
|
||||
if (!update_limits) return;
|
||||
|
||||
int64_t delta = scale (opts.elimint * (stats.elimphases + 1));
|
||||
lim.elim = stats.conflicts + delta;
|
||||
|
||||
PHASE ("elim-phase", stats.elimphases,
|
||||
"new limit at %" PRId64 " conflicts after %" PRId64 " conflicts",
|
||||
lim.elim, delta);
|
||||
|
||||
last.elim.fixed = stats.all.fixed;
|
||||
}
|
||||
|
||||
}
|
37
hCaD_V2/src/elim.hpp
Normal file
37
hCaD_V2/src/elim.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef _elim_hpp_INCLUDED
|
||||
#define _elim_hpp_INCLUDED
|
||||
|
||||
#include "heap.hpp" // Alphabetically after 'elim.hpp'.
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct Internal;
|
||||
|
||||
struct elim_more {
|
||||
Internal * internal;
|
||||
elim_more (Internal * i) : internal (i) { }
|
||||
bool operator () (unsigned a, unsigned b);
|
||||
};
|
||||
|
||||
typedef heap<elim_more> ElimSchedule;
|
||||
|
||||
struct Eliminator {
|
||||
|
||||
Internal * internal;
|
||||
ElimSchedule schedule;
|
||||
|
||||
Eliminator (Internal * i) : internal (i), schedule (elim_more (i)) { }
|
||||
~Eliminator ();
|
||||
|
||||
queue<Clause*> backward;
|
||||
|
||||
Clause * dequeue ();
|
||||
void enqueue (Clause *);
|
||||
|
||||
vector<Clause *> gates;
|
||||
vector<int> marked;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
95
hCaD_V2/src/ema.cpp
Normal file
95
hCaD_V2/src/ema.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// Updating an exponential moving average is placed here since we want to
|
||||
// log both updates and phases of initialization, thus need 'LOG'.
|
||||
//
|
||||
// We now use initialization bias correction as in the ADAM method
|
||||
// [KingmaBa-ICLR'15] instead of our ad-hoc initialization method used
|
||||
// before. Our old variant used exponentially decreasing alphas:
|
||||
//
|
||||
// 1,
|
||||
// 1/2, 1/2,
|
||||
// 1/4, 1/4, 1/4, 1/4
|
||||
// 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8,
|
||||
// ...
|
||||
// 2^-n, ..., 2^-n 'n' times
|
||||
// alpha, alpha, ... now 'alpha' forever.
|
||||
//
|
||||
// where 2^-n is the smallest negative power of two above 'alpha'
|
||||
//
|
||||
// This old method is better than the initializations described in our
|
||||
// [BiereFroehlich-POS'15] paper and actually faster than the ADAM method,
|
||||
// but less precise. This old method, which we consider obsolete but it
|
||||
// could still be useful for implementations relying on integers instead of
|
||||
// floating points thus only needs shifts and integer arithmetic.
|
||||
//
|
||||
// Our new method for unbiased initialization of the exponential averages
|
||||
// works as follows. First the biased moving average is computed as usual.
|
||||
// Note that (as already before) we use the simpler equation
|
||||
//
|
||||
// new_biased = old_biased + alpha * (y - old_biased);
|
||||
//
|
||||
// which in principle (and thus easy to remember) can be implemented as
|
||||
//
|
||||
// biased += alpha * (y - biased);
|
||||
//
|
||||
// The original formulation in the ADAM paper (with 'alpha = 1 - beta') is
|
||||
//
|
||||
// new_biased = beta * old_biased + (1 - beta) * y
|
||||
//
|
||||
// To show that these are equivalent (modulo floating point issues)
|
||||
// consider the following equivalent expressions:
|
||||
//
|
||||
// old_biased + alpha * (y - old_biased)
|
||||
// old_biased + alpha * y - alpha * old_biased
|
||||
// (1 - alpha) * old_biased + alpha * y
|
||||
// beta * old_biased + (1 - beta) * y
|
||||
//
|
||||
// The real new idea taken from the ADAM paper is however to fix the biased
|
||||
// moving average with a correction term '1.0 / (1.0 - pow (beta, updated))'
|
||||
// by multiplication to obtain an unbiased moving average (called simply
|
||||
// 'value' in our 'code'). In order to avoid computing 'pow' every time, we
|
||||
// use 'exp' which is multiplied in every update with 'beta'.
|
||||
|
||||
void EMA::update (Internal * internal, double y, const char * name) {
|
||||
#ifdef LOGGING
|
||||
updated++;
|
||||
const double old_value = value;
|
||||
#endif
|
||||
const double old_biased = biased;
|
||||
const double delta = y - old_biased;
|
||||
const double scaled_delta = alpha * delta;
|
||||
const double new_biased = old_biased + scaled_delta;
|
||||
LOG ("update %" PRIu64 " of biased %s EMA %g with %g (delta %g) "
|
||||
"yields %g (scaled delta %g)",
|
||||
updated, name, old_biased, y, delta, new_biased, scaled_delta);
|
||||
biased = new_biased;
|
||||
const double old_exp = exp;
|
||||
double new_exp, div, new_value;
|
||||
if (old_exp) {
|
||||
new_exp = old_exp * beta;
|
||||
assert (new_exp < 1);
|
||||
exp = new_exp;
|
||||
div = 1 - new_exp;
|
||||
assert (div > 0);
|
||||
new_value = new_biased / div;
|
||||
} else {
|
||||
new_value = new_biased;
|
||||
#ifdef LOGGING
|
||||
new_exp = 0;
|
||||
div = 1;
|
||||
#endif
|
||||
}
|
||||
value = new_value;
|
||||
LOG ("update %" PRIu64 " of corrected %s EMA %g with %g (delta %g) "
|
||||
"yields %g (exponent %g, divisor %g)" ,
|
||||
updated, name, old_value, y, delta, new_value, new_exp, div);
|
||||
#ifndef LOGGING
|
||||
(void) internal;
|
||||
(void) name;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
61
hCaD_V2/src/ema.hpp
Normal file
61
hCaD_V2/src/ema.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef _ema_hpp_INCLUDED
|
||||
#define _ema_hpp_INCLUDED
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct Internal;
|
||||
|
||||
// This is a more complex generic exponential moving average class to
|
||||
// support more robust initialization (see comments in the 'update'
|
||||
// implementation).
|
||||
|
||||
struct EMA {
|
||||
|
||||
#ifdef LOGGING
|
||||
uint64_t updated;
|
||||
#endif
|
||||
double value; // unbiased (corrected) moving average
|
||||
double biased; // biased initialized moving average
|
||||
double alpha; // input scaling with 'alpha = 1 - beta'
|
||||
double beta; // decay of 'biased' with 'beta = 1 - alpha'
|
||||
double exp; // 'exp = pow (beta, updated)'
|
||||
|
||||
EMA () :
|
||||
#ifdef LOGGING
|
||||
updated (0),
|
||||
#endif
|
||||
value (0), biased (0), alpha (0), beta (0), exp (0) { }
|
||||
|
||||
EMA (double a) :
|
||||
#ifdef LOGGING
|
||||
updated (0),
|
||||
#endif
|
||||
value (0), biased (0), alpha (a), beta (1 - a), exp (!!beta)
|
||||
{
|
||||
assert (beta >= 0);
|
||||
}
|
||||
|
||||
operator double () const { return value; }
|
||||
void update (Internal *, double y, const char * name);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Compact average update and initialization macros for better logging.
|
||||
|
||||
#define UPDATE_AVERAGE(A,Y) \
|
||||
do { A.update (internal, (Y), #A); } while (0)
|
||||
|
||||
#define INIT_EMA(E,WINDOW) \
|
||||
do { \
|
||||
assert ((WINDOW) >= 1); \
|
||||
double ALPHA = 1.0 / (double)(WINDOW); \
|
||||
E = EMA (ALPHA); \
|
||||
LOG ("init " #E " EMA target alpha %g window %d", ALPHA, (int)WINDOW); \
|
||||
} while (0)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
208
hCaD_V2/src/extend.cpp
Normal file
208
hCaD_V2/src/extend.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
void External::push_zero_on_extension_stack () {
|
||||
extension.push_back (0);
|
||||
LOG ("pushing 0 on extension stack");
|
||||
}
|
||||
|
||||
void External::push_clause_literal_on_extension_stack (int ilit) {
|
||||
assert (ilit);
|
||||
const int elit = internal->externalize (ilit);
|
||||
assert (elit);
|
||||
extension.push_back (elit);
|
||||
LOG ("pushing clause literal %d on extension stack (internal %d)",
|
||||
elit, ilit);
|
||||
}
|
||||
|
||||
void External::push_witness_literal_on_extension_stack (int ilit) {
|
||||
assert (ilit);
|
||||
const int elit = internal->externalize (ilit);
|
||||
assert (elit);
|
||||
extension.push_back (elit);
|
||||
LOG ("pushing witness literal %d on extension stack (internal %d)",
|
||||
elit, ilit);
|
||||
if (marked (witness, elit)) return;
|
||||
LOG ("marking witness %d", elit);
|
||||
mark (witness, elit);
|
||||
}
|
||||
|
||||
// The extension stack allows to reconstruct a satisfying assignment for the
|
||||
// original formula after removing eliminated clauses. This was pioneered
|
||||
// by Niklas Soerensson in MiniSAT and for instance is described in our
|
||||
// inprocessing paper, published at IJCAR'12. This first function adds a
|
||||
// clause to this stack. First the blocking or eliminated literal is added,
|
||||
// and then the rest of the clause.
|
||||
|
||||
void External::push_clause_on_extension_stack (Clause * c) {
|
||||
internal->stats.weakened++;
|
||||
internal->stats.weakenedlen += c->size;
|
||||
push_zero_on_extension_stack ();
|
||||
for (const auto & lit : *c)
|
||||
push_clause_literal_on_extension_stack (lit);
|
||||
}
|
||||
|
||||
void External::push_clause_on_extension_stack (Clause * c, int pivot) {
|
||||
push_zero_on_extension_stack ();
|
||||
push_witness_literal_on_extension_stack (pivot);
|
||||
push_clause_on_extension_stack (c);
|
||||
}
|
||||
|
||||
void
|
||||
External::push_binary_clause_on_extension_stack (int pivot, int other) {
|
||||
internal->stats.weakened++;
|
||||
internal->stats.weakenedlen += 2;
|
||||
push_zero_on_extension_stack ();
|
||||
push_witness_literal_on_extension_stack (pivot);
|
||||
push_zero_on_extension_stack ();
|
||||
push_clause_literal_on_extension_stack (pivot);
|
||||
push_clause_literal_on_extension_stack (other);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void External::push_external_clause_and_witness_on_extension_stack (
|
||||
const vector<int> & c, const vector<int> & w) {
|
||||
extension.push_back (0);
|
||||
for (const auto & elit : w) {
|
||||
assert (elit != INT_MIN);
|
||||
init (abs (elit));
|
||||
extension.push_back (elit);
|
||||
mark (witness, elit);
|
||||
}
|
||||
extension.push_back (0);
|
||||
for (const auto & elit : c) {
|
||||
assert (elit != INT_MIN);
|
||||
init (abs (elit));
|
||||
extension.push_back (elit);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is the actual extension process. It goes backward over the clauses
|
||||
// on the extension stack and flips the assignment of one of the blocking
|
||||
// literals in the conditional autarky stored before the clause. In the
|
||||
// original algorithm for witness construction for variable elimination and
|
||||
// blocked clause removal the conditional autarky consists of a single
|
||||
// literal from the removed clause, while in general the autarky witness can
|
||||
// contain an arbitrary set of literals. We are using the more general
|
||||
// witness reconstruction here which for instance would also work for
|
||||
// super-blocked or set-blocked clauses.
|
||||
|
||||
void External::extend () {
|
||||
|
||||
assert (!extended);
|
||||
START (extend);
|
||||
internal->stats.extensions++;
|
||||
|
||||
PHASE ("extend", internal->stats.extensions,
|
||||
"mapping internal %d assignments to %d assignments",
|
||||
internal->max_var, max_var);
|
||||
|
||||
int64_t updated = 0;
|
||||
for (unsigned i = 1; i <= (unsigned) max_var; i++) {
|
||||
const int ilit = e2i[i];
|
||||
if (!ilit) continue;
|
||||
while (i >= vals.size ())
|
||||
vals.push_back (false);
|
||||
vals[i] = (internal->val (ilit) > 0);
|
||||
updated++;
|
||||
}
|
||||
PHASE ("extend", internal->stats.extensions,
|
||||
"updated %" PRId64 " external assignments", updated);
|
||||
PHASE ("extend", internal->stats.extensions,
|
||||
"extending through extension stack of size %zd",
|
||||
extension.size ());
|
||||
const auto begin = extension.begin ();
|
||||
auto i = extension.end ();
|
||||
int64_t flipped = 0;
|
||||
while (i != begin) {
|
||||
bool satisfied = false;
|
||||
int lit;
|
||||
assert (i != begin);
|
||||
while ((lit = *--i)) {
|
||||
if (satisfied) continue;
|
||||
if (ival (lit) > 0) satisfied = true;
|
||||
assert (i != begin);
|
||||
}
|
||||
assert (i != begin);
|
||||
if (satisfied)
|
||||
while (*--i)
|
||||
assert (i != begin);
|
||||
else {
|
||||
while ((lit = *--i)) {
|
||||
const int tmp = ival (lit); // not 'signed char'!!!
|
||||
if (tmp < 0) {
|
||||
LOG ("flipping blocking literal %d", lit);
|
||||
assert (lit);
|
||||
assert (lit != INT_MIN);
|
||||
int idx = abs (lit);
|
||||
while ((size_t) idx >= vals.size ())
|
||||
vals.push_back (false);
|
||||
vals[idx] = !vals[idx];
|
||||
internal->stats.extended++;
|
||||
flipped++;
|
||||
}
|
||||
assert (i != begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
PHASE ("extend", internal->stats.extensions,
|
||||
"flipped %" PRId64 " literals during extension", flipped);
|
||||
extended = true;
|
||||
LOG ("extended");
|
||||
STOP (extend);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool External::traverse_witnesses_backward (WitnessIterator & it) {
|
||||
if (internal->unsat) return true;
|
||||
vector<int> clause, witness;
|
||||
const auto begin = extension.begin ();
|
||||
auto i = extension.end ();
|
||||
while (i != begin) {
|
||||
int lit;
|
||||
while ((lit = *--i))
|
||||
clause.push_back (lit);
|
||||
while ((lit = *--i))
|
||||
witness.push_back (lit);
|
||||
reverse (clause.begin (), clause.end ());
|
||||
reverse (witness.begin (), witness.end ());
|
||||
if (!it.witness (clause, witness))
|
||||
return false;
|
||||
clause.clear ();
|
||||
witness.clear ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool External::traverse_witnesses_forward (WitnessIterator & it) {
|
||||
if (internal->unsat) return true;
|
||||
vector<int> clause, witness;
|
||||
const auto end = extension.end ();
|
||||
auto i = extension.begin ();
|
||||
if (i != end) {
|
||||
int lit = *i++;
|
||||
do {
|
||||
assert (!lit), (void) lit;
|
||||
while ((lit = *i++))
|
||||
witness.push_back (lit);
|
||||
assert (!lit);
|
||||
assert (i != end);
|
||||
while (i != end && (lit = *i++))
|
||||
clause.push_back (lit);
|
||||
if (!it.witness (clause, witness))
|
||||
return false;
|
||||
clause.clear ();
|
||||
witness.clear ();
|
||||
} while (i != end);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
}
|
541
hCaD_V2/src/external.cpp
Normal file
541
hCaD_V2/src/external.cpp
Normal file
@ -0,0 +1,541 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
External::External (Internal * i)
|
||||
:
|
||||
internal (i),
|
||||
max_var (0),
|
||||
vsize (0),
|
||||
extended (false),
|
||||
terminator (0),
|
||||
learner (0),
|
||||
solution (0),
|
||||
vars (max_var)
|
||||
{
|
||||
assert (internal);
|
||||
assert (!internal->external);
|
||||
internal->external = this;
|
||||
}
|
||||
|
||||
External::~External () {
|
||||
if (solution) delete [] solution;
|
||||
}
|
||||
|
||||
void External::enlarge (int new_max_var) {
|
||||
|
||||
assert (!extended);
|
||||
|
||||
size_t new_vsize = vsize ? 2*vsize : 1 + (size_t) new_max_var;
|
||||
while (new_vsize <= (size_t) new_max_var) new_vsize *= 2;
|
||||
LOG ("enlarge external size from %zd to new size %zd", vsize, new_vsize);
|
||||
vsize = new_vsize;
|
||||
}
|
||||
|
||||
void External::init (int new_max_var) {
|
||||
assert (!extended);
|
||||
if (new_max_var <= max_var) return;
|
||||
int new_vars = new_max_var - max_var;
|
||||
int old_internal_max_var = internal->max_var;
|
||||
int new_internal_max_var = old_internal_max_var + new_vars;
|
||||
internal->init_vars (new_internal_max_var);
|
||||
if ((size_t) new_max_var >= vsize) enlarge (new_max_var);
|
||||
LOG ("initialized %d external variables", new_vars);
|
||||
if (!max_var) {
|
||||
assert (e2i.empty ());
|
||||
e2i.push_back (0);
|
||||
assert (internal->i2e.empty ());
|
||||
internal->i2e.push_back (0);
|
||||
} else {
|
||||
assert (e2i.size () == (size_t) max_var + 1);
|
||||
assert (internal->i2e.size () == (size_t) old_internal_max_var + 1);
|
||||
}
|
||||
unsigned iidx = old_internal_max_var + 1, eidx;
|
||||
for (eidx = max_var + 1u;
|
||||
eidx <= (unsigned) new_max_var;
|
||||
eidx++, iidx++) {
|
||||
LOG ("mapping external %u to internal %u", eidx, iidx);
|
||||
assert (e2i.size () == eidx);
|
||||
e2i.push_back (iidx);
|
||||
internal->i2e.push_back (eidx);
|
||||
assert (internal->i2e[iidx] == (int) eidx);
|
||||
assert (e2i[eidx] == (int) iidx);
|
||||
}
|
||||
if (internal->opts.checkfrozen)
|
||||
while (new_max_var >= (int64_t) moltentab.size ())
|
||||
moltentab.push_back (false);
|
||||
assert (iidx == (size_t) new_internal_max_var + 1);
|
||||
assert (eidx == (size_t) new_max_var + 1);
|
||||
max_var = new_max_var;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void External::reset_assumptions () {
|
||||
assumptions.clear ();
|
||||
internal->reset_assumptions ();
|
||||
}
|
||||
|
||||
void External::reset_extended () {
|
||||
if (!extended) return;
|
||||
LOG ("reset extended");
|
||||
extended = false;
|
||||
}
|
||||
|
||||
void External::reset_limits () {
|
||||
internal->reset_limits ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int External::internalize (int elit) {
|
||||
int ilit;
|
||||
if (elit) {
|
||||
assert (elit != INT_MIN);
|
||||
const int eidx = abs (elit);
|
||||
if (eidx > max_var) init (eidx);
|
||||
ilit = e2i [eidx];
|
||||
if (elit < 0) ilit = -ilit;
|
||||
if (!ilit) {
|
||||
assert (internal->max_var < INT_MAX);
|
||||
ilit = internal->max_var + 1u;
|
||||
internal->init_vars (ilit);
|
||||
e2i[eidx] = ilit;
|
||||
LOG ("mapping external %d to internal %d", eidx, ilit);
|
||||
e2i[eidx] = ilit;
|
||||
internal->i2e.push_back (eidx);
|
||||
assert (internal->i2e[ilit] == eidx);
|
||||
assert (e2i[eidx] == ilit);
|
||||
if (elit < 0) ilit = -ilit;
|
||||
}
|
||||
if (internal->opts.checkfrozen) {
|
||||
assert (eidx < (int64_t) moltentab.size ());
|
||||
if (moltentab[eidx])
|
||||
FATAL ("can not reuse molten literal %d", eidx);
|
||||
}
|
||||
Flags & f = internal->flags (ilit);
|
||||
if (f.status == Flags::UNUSED) internal->mark_active (ilit);
|
||||
else if (f.status != Flags::ACTIVE &&
|
||||
f.status != Flags::FIXED) internal->reactivate (ilit);
|
||||
if (!marked (tainted, elit) && marked (witness, -elit)) {
|
||||
assert (!internal->opts.checkfrozen);
|
||||
LOG ("marking tainted %d", elit);
|
||||
mark (tainted, elit);
|
||||
}
|
||||
} else ilit = 0;
|
||||
return ilit;
|
||||
}
|
||||
|
||||
void External::add (int elit) {
|
||||
assert (elit != INT_MIN);
|
||||
reset_extended ();
|
||||
if (internal->opts.check &&
|
||||
(internal->opts.checkwitness || internal->opts.checkfailed))
|
||||
original.push_back (elit);
|
||||
const int ilit = internalize (elit);
|
||||
assert (!elit == !ilit);
|
||||
if (elit) LOG ("adding external %d as internal %d", elit, ilit);
|
||||
internal->add_original_lit (ilit);
|
||||
}
|
||||
|
||||
void External::assume (int elit) {
|
||||
assert (elit);
|
||||
reset_extended ();
|
||||
assumptions.push_back (elit);
|
||||
const int ilit = internalize (elit);
|
||||
assert (ilit);
|
||||
LOG ("assuming external %d as internal %d", elit, ilit);
|
||||
internal->assume (ilit);
|
||||
}
|
||||
|
||||
bool External::failed (int elit) {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
int eidx = abs (elit);
|
||||
if (eidx > max_var) return 0;
|
||||
int ilit = e2i[eidx];
|
||||
if (!ilit) return 0;
|
||||
if (elit < 0) ilit = -ilit;
|
||||
return internal->failed (ilit);
|
||||
}
|
||||
|
||||
void External::phase (int elit) {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
int eidx = abs (elit);
|
||||
if (eidx > max_var) return;
|
||||
int ilit = e2i[eidx];
|
||||
if (!ilit) return;
|
||||
if (elit < 0) ilit = -ilit;
|
||||
internal->phase (ilit);
|
||||
}
|
||||
|
||||
void External::unphase (int elit) {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
int eidx = abs (elit);
|
||||
if (eidx > max_var) return;
|
||||
int ilit = e2i[eidx];
|
||||
if (!ilit) return;
|
||||
if (elit < 0) ilit = -ilit;
|
||||
internal->unphase (ilit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Internal checker if 'solve' claims the formula to be satisfiable.
|
||||
|
||||
void External::check_satisfiable () {
|
||||
LOG ("checking satisfiable");
|
||||
if (internal->opts.checkwitness)
|
||||
check_assignment (&External::ival);
|
||||
if (internal->opts.checkassumptions && !assumptions.empty ())
|
||||
check_assumptions_satisfied ();
|
||||
}
|
||||
|
||||
// Internal checker if 'solve' claims formula to be unsatisfiable.
|
||||
|
||||
void External::check_unsatisfiable () {
|
||||
LOG ("checking unsatisfiable");
|
||||
if (internal->opts.checkfailed && !assumptions.empty ())
|
||||
check_assumptions_failing ();
|
||||
}
|
||||
|
||||
// Check result of 'solve' to be correct.
|
||||
|
||||
void External::check_solve_result (int res) {
|
||||
if (!internal->opts.check) return;
|
||||
if (res == 10) check_satisfiable ();
|
||||
if (res == 20) check_unsatisfiable ();
|
||||
}
|
||||
|
||||
// Prepare checking that completely molten literals are not used as argument
|
||||
// of 'add' or 'assume', which is invalid under freezing semantics. This
|
||||
// case would be caught by our 'restore' implementation so is only needed
|
||||
// for checking the deprecated 'freeze' semantics.
|
||||
|
||||
void External::update_molten_literals () {
|
||||
if (!internal->opts.checkfrozen) return;
|
||||
assert ((size_t) max_var + 1 == moltentab.size ());
|
||||
int registered = 0, molten = 0;
|
||||
for (auto lit : vars) {
|
||||
if (moltentab[lit]) {
|
||||
LOG ("skipping already molten literal %d", lit);
|
||||
molten++;
|
||||
} else if (frozen (lit))
|
||||
LOG ("skipping currently frozen literal %d", lit);
|
||||
else {
|
||||
LOG ("new molten literal %d", lit);
|
||||
moltentab[lit] = true;
|
||||
registered++;
|
||||
molten++;
|
||||
}
|
||||
}
|
||||
LOG ("registered %d new molten literals", registered);
|
||||
LOG ("reached in total %d molten literals", molten);
|
||||
}
|
||||
|
||||
int External::solve (bool preprocess_only) {
|
||||
reset_extended ();
|
||||
update_molten_literals ();
|
||||
int res = internal->solve (preprocess_only);
|
||||
if (res == 10) extend ();
|
||||
check_solve_result (res);
|
||||
reset_limits ();
|
||||
return res;
|
||||
}
|
||||
|
||||
void External::terminate () { internal->terminate (); }
|
||||
|
||||
int External::lookahead () {
|
||||
reset_extended ();
|
||||
update_molten_literals ();
|
||||
int ilit = internal->lookahead ();
|
||||
const int elit = (ilit && ilit != INT_MIN) ? internal->externalize (ilit) : 0;
|
||||
LOG ("lookahead internal %d external %d", ilit, elit);
|
||||
return elit;
|
||||
}
|
||||
|
||||
CaDiCaL::CubesWithStatus External::generate_cubes (int depth, int min_depth = 0) {
|
||||
reset_extended ();
|
||||
update_molten_literals ();
|
||||
reset_limits ();
|
||||
auto cubes = internal->generate_cubes (depth, min_depth);
|
||||
auto externalize = [this](int ilit) {
|
||||
const int elit = ilit ? internal->externalize (ilit) : 0;
|
||||
MSG ("lookahead internal %d external %d", ilit, elit);
|
||||
return elit;
|
||||
};
|
||||
auto externalize_map = [this, externalize](std::vector<int> cube) {
|
||||
(void) this;
|
||||
MSG("Cube : ");
|
||||
std::for_each(begin(cube), end(cube), externalize);
|
||||
};
|
||||
std::for_each(begin(cubes.cubes), end(cubes.cubes), externalize_map);
|
||||
|
||||
return cubes;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void External::freeze (int elit) {
|
||||
reset_extended ();
|
||||
int ilit = internalize (elit);
|
||||
unsigned eidx = vidx (elit);
|
||||
while (eidx >= frozentab.size ())
|
||||
frozentab.push_back (0);
|
||||
unsigned & ref = frozentab[eidx];
|
||||
if (ref < UINT_MAX) {
|
||||
ref++;
|
||||
LOG ("external variable %d frozen once and now frozen %u times",
|
||||
eidx, ref);
|
||||
} else
|
||||
LOG ("external variable %d frozen but remains frozen forever", eidx);
|
||||
internal->freeze (ilit);
|
||||
}
|
||||
|
||||
void External::melt (int elit) {
|
||||
reset_extended ();
|
||||
int ilit = internalize (elit);
|
||||
unsigned eidx = vidx (elit);
|
||||
assert (eidx < frozentab.size ());
|
||||
unsigned & ref = frozentab[eidx];
|
||||
assert (ref > 0);
|
||||
if (ref < UINT_MAX) {
|
||||
if (!--ref)
|
||||
LOG ("external variable %d melted once and now completely melted",
|
||||
eidx);
|
||||
else
|
||||
LOG ("external variable %d melted once but remains frozen %u times",
|
||||
eidx, ref);
|
||||
} else
|
||||
LOG ("external variable %d melted but remains frozen forever", eidx);
|
||||
internal->melt (ilit);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void External::check_assignment (int (External::*a)(int) const) {
|
||||
|
||||
// First check all assigned and consistent.
|
||||
//
|
||||
for (auto idx : vars) {
|
||||
if (!(this->*a) (idx)) FATAL ("unassigned variable: %d", idx);
|
||||
if ((this->*a) (idx) != -(this->*a)(-idx))
|
||||
FATAL ("inconsistently assigned literals %d and %d", idx, -idx);
|
||||
}
|
||||
|
||||
// Then check that all (saved) original clauses are satisfied.
|
||||
//
|
||||
bool satisfied = false;
|
||||
const auto end = original.end ();
|
||||
auto start = original.begin (), i = start;
|
||||
int64_t count = 0;
|
||||
for (; i != end; i++) {
|
||||
int lit = *i;
|
||||
if (!lit) {
|
||||
if (!satisfied) {
|
||||
fatal_message_start ();
|
||||
fputs ("unsatisfied clause:\n", stderr);
|
||||
for (auto j = start; j != i; j++)
|
||||
fprintf (stderr, "%d ", *j);
|
||||
fputc ('0', stderr);
|
||||
fatal_message_end ();
|
||||
}
|
||||
satisfied = false;
|
||||
start = i + 1;
|
||||
count++;
|
||||
} else if (!satisfied && (this->*a) (lit) > 0) satisfied = true;
|
||||
}
|
||||
VERBOSE (1,
|
||||
"satisfying assignment checked on %" PRId64 " clauses",
|
||||
count);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void External::check_assumptions_satisfied () {
|
||||
for (const auto & lit : assumptions) {
|
||||
// Not 'signed char' !!!!
|
||||
const int tmp = ival (lit);
|
||||
if (tmp < 0) FATAL ("assumption %d falsified", lit);
|
||||
if (!tmp) FATAL ("assumption %d unassigned", lit);
|
||||
}
|
||||
VERBOSE (1,
|
||||
"checked that %zd assumptions are satisfied",
|
||||
assumptions.size ());
|
||||
}
|
||||
|
||||
void External::check_assumptions_failing () {
|
||||
Solver * checker = new Solver ();
|
||||
checker->prefix ("checker ");
|
||||
#ifdef LOGGING
|
||||
if (internal->opts.log) checker->set ("log", true);
|
||||
#endif
|
||||
for (const auto & lit : original)
|
||||
checker->add (lit);
|
||||
for (const auto & lit : assumptions) {
|
||||
if (!failed (lit)) continue;
|
||||
LOG ("checking failed literal %d in core", lit);
|
||||
checker->add (lit);
|
||||
checker->add (0);
|
||||
}
|
||||
int res = checker->solve ();
|
||||
if (res != 20) FATAL ("failed assumptions do not form a core");
|
||||
delete checker;
|
||||
VERBOSE (1,
|
||||
"checked that %zd failing assumptions form a core",
|
||||
assumptions.size ());
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Traversal of unit clauses is implemented here.
|
||||
|
||||
// In principle we want to traverse the clauses of the simplified formula
|
||||
// only, particularly eliminated variables should be completely removed.
|
||||
// This poses the question what to do with unit clauses. Should they be
|
||||
// considered part of the simplified formula or of the witness to construct
|
||||
// solutions for the original formula? Ideally they should be considered
|
||||
// to be part of the witness only, i.e., as they have been simplified away.
|
||||
|
||||
// Therefore we distinguish frozen and non-frozen units during clause
|
||||
// traversal. Frozen units are treated as unit clauses while non-frozen
|
||||
// units are treated as if they were already eliminated and put on the
|
||||
// extension stack as witness clauses.
|
||||
|
||||
// Furthermore, eliminating units during 'compact' could be interpreted as
|
||||
// variable elimination, i.e., just add the resolvents (remove falsified
|
||||
// literals), then drop the clauses with the unit, and push the unit on the
|
||||
// extension stack. This is of course only OK if the user did not freeze
|
||||
// that variable (even implicitly during assumptions).
|
||||
|
||||
// Thanks go to Fahiem Bacchus for asking why there is a necessity to
|
||||
// distinguish these two cases (frozen and non-frozen units). The answer is
|
||||
// that it is not strictly necessary, and this distinction could be avoided
|
||||
// by always treating units as remaining unit clauses, thus only using the
|
||||
// first of the two following functions and dropping the 'if (!frozen (idx))
|
||||
// continue;' check in it. This has however the down-side that those units
|
||||
// are still in the simplified formula and only as units. I would not
|
||||
// consider such a formula as really being 'simplified'. On the other hand
|
||||
// if the user explicitly freezes a literal, then it should continue to be in
|
||||
// the simplified formula during traversal. So also only using the second
|
||||
// function is not ideal.
|
||||
|
||||
// There is however a catch where this solution breaks down (in the sense of
|
||||
// producing less optimal results - that is keeping units in the formula
|
||||
// which better would be witness clauses). The problem is with compact
|
||||
// preprocessing which removes eliminated but also fixed internal variables.
|
||||
// One internal unit (fixed) variable is kept and all the other external
|
||||
// literals which became unit are mapped to that internal literal (negated
|
||||
// or positively). Compact is called non-deterministically from the point
|
||||
// of the user and thus there is no control on when this happens. If
|
||||
// compact happens those external units are mapped to a single internal
|
||||
// literal now and then all share the same 'frozen' counter. So if the
|
||||
// user freezes one of them all in essence get frozen, which in turn then
|
||||
// makes a difference in terms of traversing such a unit as unit clause or
|
||||
// as unit witness.
|
||||
|
||||
bool
|
||||
External::traverse_all_frozen_units_as_clauses (ClauseIterator & it)
|
||||
{
|
||||
if (internal->unsat) return true;
|
||||
|
||||
vector<int> clause;
|
||||
|
||||
for (auto idx : vars) {
|
||||
if (!frozen (idx)) continue;
|
||||
const int tmp = fixed (idx);
|
||||
if (!tmp) continue;
|
||||
int unit = tmp < 0 ? -idx : idx;
|
||||
clause.push_back (unit);
|
||||
if (!it.clause (clause))
|
||||
return false;
|
||||
clause.clear ();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
External::traverse_all_non_frozen_units_as_witnesses (WitnessIterator & it)
|
||||
{
|
||||
if (internal->unsat) return true;
|
||||
|
||||
vector<int> clause_and_witness;
|
||||
|
||||
for (auto idx : vars) {
|
||||
if (frozen (idx)) continue;
|
||||
const int tmp = fixed (idx);
|
||||
if (!tmp) continue;
|
||||
int unit = tmp < 0 ? -idx : idx;
|
||||
clause_and_witness.push_back (unit);
|
||||
if (!it.witness (clause_and_witness, clause_and_witness))
|
||||
return false;
|
||||
clause_and_witness.clear ();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void External::copy_flags (External & other) const {
|
||||
const vector<Flags> & this_ftab = internal->ftab;
|
||||
vector<Flags> & other_ftab = other.internal->ftab;
|
||||
const unsigned limit = min (max_var, other.max_var);
|
||||
for (unsigned eidx = 1; eidx <= limit; eidx++) {
|
||||
const int this_ilit = e2i[eidx];
|
||||
if (!this_ilit) continue;
|
||||
const int other_ilit = other.e2i[eidx];
|
||||
if (!other_ilit) continue;
|
||||
if (!internal->active (this_ilit)) continue;
|
||||
if (!other.internal->active (other_ilit)) continue;
|
||||
assert (this_ilit != INT_MIN);
|
||||
assert (other_ilit != INT_MIN);
|
||||
const Flags & this_flags = this_ftab[abs (this_ilit)];
|
||||
Flags & other_flags = other_ftab[abs (other_ilit)];
|
||||
this_flags.copy (other_flags);
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void External::export_learned_empty_clause () {
|
||||
assert (learner);
|
||||
if (learner->learning (0)) {
|
||||
LOG ("exporting learned empty clause");
|
||||
learner->learn (0);
|
||||
} else
|
||||
LOG ("not exporting learned empty clause");
|
||||
}
|
||||
|
||||
void External::export_learned_unit_clause (int ilit) {
|
||||
assert (learner);
|
||||
if (learner->learning (1)) {
|
||||
LOG ("exporting learned unit clause");
|
||||
const int elit = internal->externalize (ilit);
|
||||
assert (elit);
|
||||
learner->learn (elit);
|
||||
learner->learn (0);
|
||||
} else
|
||||
LOG ("not exporting learned unit clause");
|
||||
}
|
||||
|
||||
void External::export_learned_large_clause (const vector<int> & clause) {
|
||||
assert (learner);
|
||||
size_t size = clause.size ();
|
||||
assert (size <= (unsigned) INT_MAX);
|
||||
if (learner->learning ((int) size)) {
|
||||
LOG ("exporting learned clause of size %zu", size);
|
||||
for (auto ilit : clause) {
|
||||
const int elit = internal->externalize (ilit);
|
||||
assert (elit);
|
||||
learner->learn (elit);
|
||||
}
|
||||
learner->learn (0);
|
||||
} else
|
||||
LOG ("not exporting learned clause of size %zu", size);
|
||||
}
|
||||
|
||||
}
|
356
hCaD_V2/src/external.hpp
Normal file
356
hCaD_V2/src/external.hpp
Normal file
@ -0,0 +1,356 @@
|
||||
#ifndef _external_hpp_INCLUDED
|
||||
#define _external_hpp_INCLUDED
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#include "range.hpp"
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The CaDiCaL code is split into three layers:
|
||||
//
|
||||
// Solver: facade object providing the actual API of the solver
|
||||
// External: communication layer between 'Solver' and 'Internal'
|
||||
// Internal: the actual solver code
|
||||
//
|
||||
// Note, that 'Solver' is defined in 'cadical.hpp' and 'solver.cpp', while
|
||||
// 'External' and 'Internal' in '{external,internal}.{hpp,cpp}'.
|
||||
//
|
||||
// Also note, that any user should access the library only through the
|
||||
// 'Solver' API. For the library internal 'Parser' code we make an
|
||||
// exception and allow access to both 'External' and 'Internal'. The former
|
||||
// to enforce the same external to internal mapping of variables and the
|
||||
// latter for profiling and messages. The same applies to 'App'.
|
||||
//
|
||||
// The 'External' class provided here stores the information needed to map
|
||||
// external variable indices to internal variables (actually literals).
|
||||
// This is helpful for shrinking the working size of the internal solver
|
||||
// after many variables become inactive. It will also help to provide
|
||||
// support for extended resolution in the future, since it allows to
|
||||
// introduce only internally visible variables (even though we do not know
|
||||
// how to support generating incremental proofs in this situation yet).
|
||||
//
|
||||
// External literals are usually called 'elit' and internal 'ilit'.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
struct Clause;
|
||||
struct Internal;
|
||||
struct CubesWithStatus;
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
struct External {
|
||||
|
||||
/*==== start of state ==================================================*/
|
||||
|
||||
Internal * internal; // The actual internal solver.
|
||||
|
||||
int max_var; // External maximum variable index.
|
||||
size_t vsize; // Allocated external size.
|
||||
|
||||
vector<bool> vals; // Current external (extended) assignment.
|
||||
vector<int> e2i; // External 'idx' to internal 'lit'.
|
||||
|
||||
vector<int> assumptions; // External assumptions.
|
||||
|
||||
// The extension stack for reconstructing complete satisfying assignments
|
||||
// (models) of the original external formula is kept in this external
|
||||
// solver object. It keeps track of blocked clauses and clauses containing
|
||||
// eliminated variable. These irredundant clauses are stored in terms of
|
||||
// external literals on the 'extension' stack after mapping the
|
||||
// internal literals given as arguments with 'externalize'.
|
||||
|
||||
bool extended; // Have been extended.
|
||||
vector<int> extension; // Solution reconstruction extension stack.
|
||||
|
||||
vector<bool> witness; // Literal witness on extension stack.
|
||||
vector<bool> tainted; // Literal tainted in adding literals.
|
||||
|
||||
vector<unsigned> frozentab; // Reference counts for frozen variables.
|
||||
|
||||
// Regularly checked terminator if non-zero. The terminator is set from
|
||||
// 'Solver::set (Terminator *)' and checked by 'Internal::terminating ()'.
|
||||
|
||||
Terminator * terminator;
|
||||
|
||||
// If there is a learner export learned clauses.
|
||||
|
||||
Learner * learner;
|
||||
|
||||
void export_learned_empty_clause ();
|
||||
void export_learned_unit_clause (int ilit);
|
||||
void export_learned_large_clause (const vector<int> &);
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
|
||||
signed char * solution; // Given solution checking for debugging.
|
||||
vector<int> original; // Saved original formula for checking.
|
||||
|
||||
// If 'opts.checkfrozen' is set make sure that only literals are added
|
||||
// which were never completely molten before. These molten literals are
|
||||
// marked at the beginning of the 'solve' call. Note that variables
|
||||
// larger than 'max_var' are not molten and can thus always be used in the
|
||||
// future. Only needed to check and debug old style freeze semantics.
|
||||
//
|
||||
vector<bool> moltentab;
|
||||
|
||||
//----------------------------------------------------------------------//
|
||||
|
||||
const Range vars; // Provides safe variable iterations.
|
||||
|
||||
/*==== end of state ====================================================*/
|
||||
|
||||
// These two just factor out common sanity (assertion) checking code.
|
||||
|
||||
inline int vidx (int elit) const {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
int res = abs (elit);
|
||||
assert (res <= max_var);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline int vlit (int elit) const {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
assert (abs (elit) <= max_var);
|
||||
return elit;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// The following five functions push individual literals or clauses on the
|
||||
// extension stack. They all take internal literals as argument, and map
|
||||
// them back to external literals first, before pushing them on the stack.
|
||||
|
||||
void push_zero_on_extension_stack ();
|
||||
|
||||
// Our general version of extension stacks always pushes a set of witness
|
||||
// literals (for variable elimination the literal of the eliminated
|
||||
// literal and for blocked clauses the blocking literal) followed by all
|
||||
// the clause literals starting with and separated by zero.
|
||||
//
|
||||
void push_clause_literal_on_extension_stack (int ilit);
|
||||
void push_witness_literal_on_extension_stack (int ilit);
|
||||
|
||||
void push_clause_on_extension_stack (Clause *);
|
||||
void push_clause_on_extension_stack (Clause *, int witness);
|
||||
void push_binary_clause_on_extension_stack (int witness, int other);
|
||||
|
||||
// The main 'extend' function which extends an internal assignment to an
|
||||
// external assignment using the extension stack (and sets 'extended').
|
||||
//
|
||||
void extend ();
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Marking external literals.
|
||||
|
||||
unsigned elit2ulit (int elit) const {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
const int idx = abs (elit) - 1;
|
||||
assert (idx <= max_var);
|
||||
return 2u*idx + (elit < 0);
|
||||
}
|
||||
|
||||
bool marked (const vector<bool> & map, int elit) const {
|
||||
const unsigned ulit = elit2ulit (elit);
|
||||
return ulit < map.size () ? map[ulit] : false;
|
||||
}
|
||||
|
||||
void mark (vector<bool> & map, int elit) {
|
||||
const unsigned ulit = elit2ulit (elit);
|
||||
while (ulit >= map.size ()) map.push_back (false);
|
||||
map[ulit] = true;
|
||||
}
|
||||
|
||||
void unmark (vector<bool> & map, int elit) {
|
||||
const unsigned ulit = elit2ulit (elit);
|
||||
if (ulit < map.size ()) map[ulit] = false;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void push_external_clause_and_witness_on_extension_stack (
|
||||
const vector<int> & clause, const vector<int> & witness);
|
||||
|
||||
// Restore a clause, which was pushed on the extension stack.
|
||||
void restore_clause (
|
||||
const vector<int>::const_iterator & begin,
|
||||
const vector<int>::const_iterator & end);
|
||||
|
||||
void restore_clauses ();
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Explicitly freeze and melt literals (instead of just freezing
|
||||
// internally and implicitly assumed literals). Passes on freezing and
|
||||
// melting to the internal solver, which has separate frozen counters.
|
||||
|
||||
void freeze (int elit);
|
||||
void melt (int elit);
|
||||
|
||||
bool frozen (int elit) {
|
||||
assert (elit);
|
||||
assert (elit != INT_MIN);
|
||||
int eidx = abs (elit);
|
||||
if (eidx > max_var) return false;
|
||||
if (eidx >= (int) frozentab.size ()) return false;
|
||||
return frozentab[eidx] > 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
External (Internal *);
|
||||
~External ();
|
||||
|
||||
void enlarge (int new_max_var); // Enlarge allocated 'vsize'.
|
||||
void init (int new_max_var); // Initialize up-to 'new_max_var'.
|
||||
|
||||
int internalize (int); // Translate external to internal literal.
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// According to the CaDiCaL API contract (as well as IPASIR) we have to
|
||||
// forget about the previous assumptions after a 'solve' call. This
|
||||
// should however be delayed until we transition out of an 'UNSATISFIED'
|
||||
// state, i.e., after no more 'failed' calls are expected. Note that
|
||||
// 'failed' requires to know the failing assumptions, and the 'failed'
|
||||
// status of those should cleared before at start of the next 'solve'.
|
||||
// As a consequence 'reset_assumptions' is only called from
|
||||
// 'transition_to_unknown_state' in API calls in 'solver.cpp'.
|
||||
|
||||
void reset_assumptions ();
|
||||
|
||||
// Similarly a valid external assignment obtained through 'extend' has to
|
||||
// be reset at each point it risks to become invalid. This is done
|
||||
// in the external layer in 'external.cpp' functions..
|
||||
|
||||
void reset_extended ();
|
||||
|
||||
// Finally, the semantics of incremental solving also require that limits
|
||||
// are only valid for the next 'solve' call. Since the limits can not
|
||||
// really be queried, handling them is less complex and they are just
|
||||
// reset immediately at the end of 'External::solve'.
|
||||
|
||||
void reset_limits ();
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Proxies to IPASIR functions.
|
||||
|
||||
void add (int elit);
|
||||
void assume (int elit);
|
||||
int solve (bool preprocess_only);
|
||||
|
||||
// We call it 'ival' as abbreviation for 'val' with 'int' return type to
|
||||
// avoid bugs due to using 'signed char tmp = val (lit)', which might turn
|
||||
// a negative value into a positive one (happened in 'extend').
|
||||
//
|
||||
inline int ival (int elit) const {
|
||||
assert (elit != INT_MIN);
|
||||
int eidx = abs (elit), res;
|
||||
if (eidx > max_var) res = -1;
|
||||
else if ((size_t) eidx >= vals.size ()) res = -1;
|
||||
else res = vals[eidx] ? eidx : -eidx;
|
||||
if (elit < 0) res = -res;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool failed (int elit);
|
||||
|
||||
void terminate ();
|
||||
|
||||
// Other important non IPASIR functions.
|
||||
|
||||
int lookahead();
|
||||
CaDiCaL::CubesWithStatus generate_cubes(int, int);
|
||||
|
||||
int fixed (int elit) const; // Implemented in 'internal.hpp'.
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void phase (int elit);
|
||||
void unphase (int elit);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Traversal functions for the witness stack and units. The explanation
|
||||
// in 'external.cpp' for why we have to distinguish these cases.
|
||||
|
||||
bool traverse_all_frozen_units_as_clauses (ClauseIterator &);
|
||||
bool traverse_all_non_frozen_units_as_witnesses (WitnessIterator &);
|
||||
bool traverse_witnesses_backward (WitnessIterator &);
|
||||
bool traverse_witnesses_forward (WitnessIterator &);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Copy flags for determining preprocessing state.
|
||||
|
||||
void copy_flags (External & other) const;
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Check solver behaves as expected during testing and debugging.
|
||||
|
||||
void check_assumptions_satisfied ();
|
||||
void check_assumptions_failing ();
|
||||
|
||||
void check_solution_on_learned_clause ();
|
||||
void check_solution_on_shrunken_clause (Clause *);
|
||||
void check_solution_on_learned_unit_clause (int unit);
|
||||
void check_no_solution_after_learning_empty_clause ();
|
||||
|
||||
void check_learned_empty_clause () {
|
||||
if (solution) check_no_solution_after_learning_empty_clause ();
|
||||
}
|
||||
|
||||
void check_learned_unit_clause (int unit) {
|
||||
if (solution) check_solution_on_learned_unit_clause (unit);
|
||||
}
|
||||
|
||||
void check_learned_clause () {
|
||||
if (solution) check_solution_on_learned_clause ();
|
||||
}
|
||||
|
||||
void check_shrunken_clause (Clause * c) {
|
||||
if (solution) check_solution_on_shrunken_clause (c);
|
||||
}
|
||||
|
||||
void check_assignment (int (External::*assignment) (int) const);
|
||||
|
||||
void check_satisfiable ();
|
||||
void check_unsatisfiable ();
|
||||
|
||||
void check_solve_result (int res);
|
||||
|
||||
void update_molten_literals ();
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// For debugging and testing only. See 'solution.hpp' for more details.
|
||||
//
|
||||
inline int sol (int elit) const {
|
||||
assert (solution);
|
||||
assert (elit != INT_MIN);
|
||||
int eidx = abs (elit);
|
||||
if (eidx > max_var) return 0;
|
||||
int res = solution[eidx];
|
||||
if (elit < 0) res = -res;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
306
hCaD_V2/src/file.cpp
Normal file
306
hCaD_V2/src/file.cpp
Normal file
@ -0,0 +1,306 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Some more low-level 'C' headers.
|
||||
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Private constructor.
|
||||
|
||||
File::File (Internal *i, bool w, int c, FILE * f, const char * n)
|
||||
:
|
||||
#ifndef QUIET
|
||||
internal (i),
|
||||
#endif
|
||||
#if !defined(QUIET) || !defined(NDEBUG)
|
||||
writing (w),
|
||||
#endif
|
||||
close_file (c), file (f),
|
||||
_name (n), _lineno (1), _bytes (0)
|
||||
{
|
||||
(void) i, (void) w;
|
||||
assert (f), assert (n);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool File::exists (const char * path) {
|
||||
struct stat buf;
|
||||
if (stat (path, &buf)) return false;
|
||||
if (access (path, R_OK)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::writable (const char * path) {
|
||||
int res;
|
||||
if (!path) res = 1;
|
||||
else if (!strcmp (path, "/dev/null")) res = 0;
|
||||
else {
|
||||
if (!*path) res = 2;
|
||||
else {
|
||||
struct stat buf;
|
||||
const char * p = strrchr (path, '/');
|
||||
if (!p) {
|
||||
if (stat (path, &buf)) res = ((errno == ENOENT) ? 0 : -2);
|
||||
else if (S_ISDIR (buf.st_mode)) res = 3;
|
||||
else res = (access (path, W_OK) ? 4 : 0);
|
||||
} else if (!p[1]) res = 5;
|
||||
else {
|
||||
size_t len = p - path;
|
||||
char * dirname = new char[len + 1];
|
||||
strncpy (dirname, path, len);
|
||||
dirname[len] = 0;
|
||||
if (stat (dirname, &buf)) res = 6;
|
||||
else if (!S_ISDIR (buf.st_mode)) res = 7;
|
||||
else if (access (dirname, W_OK)) res = 8;
|
||||
else if (stat (path, &buf)) res = (errno == ENOENT) ? 0 : -3;
|
||||
else res = access (path, W_OK) ? 9 : 0;
|
||||
delete [] dirname;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !res;
|
||||
}
|
||||
|
||||
// These are signatures for supported compressed file types. In 2018 the
|
||||
// SAT Competition was running on StarExec and used internally 'bzip2'
|
||||
// compressed files, but gave them uncompressed to the solver using exactly
|
||||
// the same path (with '.bz2' suffix). Then 'CaDiCaL' tried to read that
|
||||
// actually uncompressed file through 'bzip2', which of course failed. Now
|
||||
// we double check and fall back to reading the file as is, if the signature
|
||||
// does not match after issuing a warning.
|
||||
|
||||
static int xzsig[] = { 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, 0x00, EOF };
|
||||
static int bz2sig[] = { 0x42, 0x5A, 0x68, EOF };
|
||||
static int gzsig[] = { 0x1F, 0x8B, EOF };
|
||||
static int sig7z[] = { 0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, EOF };
|
||||
static int lzmasig[] = { 0x5D, 0x00, 0x00, 0x80, 0x00, EOF };
|
||||
|
||||
bool File::match (Internal * internal,
|
||||
const char * path, const int * sig) {
|
||||
assert (path);
|
||||
FILE * tmp = fopen (path, "r");
|
||||
if (!tmp) {
|
||||
WARNING ("failed to open '%s' to check signature", path);
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
for (const int *p = sig; res && (*p != EOF); p++)
|
||||
res = (cadical_getc_unlocked (tmp) == *p);
|
||||
fclose (tmp);
|
||||
if (!res) WARNING ("file type signature check for '%s' failed", path);
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t File::size (const char * path) {
|
||||
struct stat buf;
|
||||
if (stat (path, &buf)) return 0;
|
||||
return (size_t) buf.st_size;
|
||||
}
|
||||
|
||||
// Check that 'prg' is in the 'PATH' and thus can be found if executed
|
||||
// through 'popen'.
|
||||
|
||||
char * File::find (const char * prg) {
|
||||
size_t prglen = strlen (prg);
|
||||
const char * c = getenv ("PATH");
|
||||
if (!c) return 0;;
|
||||
size_t len = strlen (c);
|
||||
char * e = new char[len + 1];
|
||||
strcpy (e, c);
|
||||
char * res = 0;
|
||||
for (char * p = e, * q; !res && p < e + len; p = q) {
|
||||
for (q = p; *q && *q != ':'; q++)
|
||||
;
|
||||
*q++ = 0;
|
||||
size_t pathlen = (q - p) + prglen;
|
||||
char * path = new char [pathlen + 1];
|
||||
sprintf (path, "%s/%s", p, prg);
|
||||
assert (strlen (path) == pathlen);
|
||||
if (exists (path)) res = path;
|
||||
else delete [] path;
|
||||
}
|
||||
delete [] e;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
FILE * File::open_file (Internal * internal, const char * path,
|
||||
const char * mode) {
|
||||
(void) internal;
|
||||
return fopen (path, mode);
|
||||
}
|
||||
|
||||
FILE * File::read_file (Internal * internal, const char * path) {
|
||||
MSG ("opening file to read '%s'", path);
|
||||
return open_file (internal, path, "r");
|
||||
}
|
||||
|
||||
FILE * File::write_file (Internal * internal, const char * path) {
|
||||
MSG ("opening file to write '%s'", path);
|
||||
return open_file (internal, path, "w");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
FILE * File::open_pipe (Internal * internal,
|
||||
const char * fmt, const char * path,
|
||||
const char * mode) {
|
||||
#ifdef QUIET
|
||||
(void) internal;
|
||||
#endif
|
||||
size_t prglen = 0;
|
||||
while (fmt[prglen] && fmt[prglen] != ' ') prglen++;
|
||||
char * prg = new char [prglen + 1];
|
||||
strncpy (prg, fmt, prglen);
|
||||
prg[prglen] = 0;
|
||||
char * found = find (prg);
|
||||
if (found) MSG ("found '%s' in path for '%s'", found, prg);
|
||||
if (!found) MSG ("did not find '%s' in path", prg);
|
||||
delete [] prg;
|
||||
if (!found) return 0;
|
||||
delete [] found;
|
||||
char * cmd = new char [strlen (fmt) + strlen (path)];
|
||||
sprintf (cmd, fmt, path);
|
||||
FILE * res = popen (cmd, mode);
|
||||
delete [] cmd;
|
||||
return res;
|
||||
}
|
||||
|
||||
FILE * File::read_pipe (Internal * internal,
|
||||
const char * fmt,
|
||||
const int * sig,
|
||||
const char * path) {
|
||||
if (!File::exists (path)) {
|
||||
LOG ("file '%s' does not exist", path);
|
||||
return 0;
|
||||
}
|
||||
LOG ("file '%s' exists", path);
|
||||
if (sig && !File::match (internal, path, sig)) return 0;
|
||||
LOG ("file '%s' matches signature for '%s'", path, fmt);
|
||||
MSG ("opening pipe to read '%s'", path);
|
||||
return open_pipe (internal, fmt, path, "r");
|
||||
}
|
||||
|
||||
FILE * File::write_pipe (Internal * internal,
|
||||
const char * fmt, const char * path) {
|
||||
MSG ("opening pipe to write '%s'", path);
|
||||
return open_pipe (internal, fmt, path, "w");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
File * File::read (Internal * internal, FILE * f, const char * n) {
|
||||
return new File (internal, false, 0, f, n);
|
||||
}
|
||||
|
||||
File * File::write (Internal * internal, FILE * f, const char * n) {
|
||||
return new File (internal, true, 0, f, n);
|
||||
}
|
||||
|
||||
File * File::read (Internal * internal, const char * path) {
|
||||
FILE * file;
|
||||
int close_input = 2;
|
||||
if (has_suffix (path, ".xz")) {
|
||||
file = read_pipe (internal, "xz -c -d %s", xzsig, path);
|
||||
if (!file) goto READ_FILE;
|
||||
} else if (has_suffix (path, ".lzma")) {
|
||||
file = read_pipe (internal, "lzma -c -d %s", lzmasig, path);
|
||||
if (!file) goto READ_FILE;
|
||||
} else if (has_suffix (path, ".bz2")) {
|
||||
file = read_pipe (internal, "bzip2 -c -d %s", bz2sig, path);
|
||||
if (!file) goto READ_FILE;
|
||||
} else if (has_suffix (path, ".gz")) {
|
||||
file = read_pipe (internal, "gzip -c -d %s", gzsig, path);
|
||||
if (!file) goto READ_FILE;
|
||||
} else if (has_suffix (path, ".7z")) {
|
||||
file = read_pipe (internal, "7z x -so %s 2>/dev/null", sig7z, path);
|
||||
if (!file) goto READ_FILE;
|
||||
} else {
|
||||
READ_FILE:
|
||||
file = read_file (internal, path);
|
||||
close_input = 1;
|
||||
}
|
||||
|
||||
return file ? new File (internal, false, close_input, file, path) : 0;
|
||||
}
|
||||
|
||||
File * File::write (Internal * internal, const char * path) {
|
||||
FILE * file;
|
||||
int close_input = 2;
|
||||
if (has_suffix (path, ".xz"))
|
||||
file = write_pipe (internal, "xz -c > %s", path);
|
||||
else if (has_suffix (path, ".bz2"))
|
||||
file = write_pipe (internal, "bzip2 -c > %s", path);
|
||||
else if (has_suffix (path, ".gz"))
|
||||
file = write_pipe (internal, "gzip -c > %s", path);
|
||||
else if (has_suffix (path, ".7z"))
|
||||
file = write_pipe (internal,
|
||||
"7z a -an -txz -si -so > %s 2>/dev/null", path);
|
||||
else
|
||||
file = write_file (internal, path), close_input = 1;
|
||||
|
||||
return file ? new File (internal, true, close_input, file, path) : 0;
|
||||
}
|
||||
|
||||
void File::close () {
|
||||
assert (file);
|
||||
if (close_file == 0) {
|
||||
MSG ("disconnecting from '%s'", name ());
|
||||
}
|
||||
if (close_file == 1) {
|
||||
MSG ("closing file '%s'", name ());
|
||||
fclose (file);
|
||||
}
|
||||
if (close_file == 2) {
|
||||
MSG ("closing pipe command on '%s'", name ());
|
||||
pclose (file);
|
||||
}
|
||||
|
||||
file = 0; // mark as closed
|
||||
|
||||
#ifndef QUIET
|
||||
if (internal->opts.verbose > 1) return;
|
||||
double mb = bytes () / (double) (1 << 20);
|
||||
if (writing)
|
||||
MSG ("after writing %" PRIu64 " bytes %.1f MB", bytes (), mb);
|
||||
else
|
||||
MSG ("after reading %" PRIu64 " bytes %.1f MB", bytes (), mb);
|
||||
if (close_file == 2) {
|
||||
int64_t s = size (name ());
|
||||
double mb = s / (double) (1<<20);
|
||||
if (writing)
|
||||
MSG ("deflated to %" PRId64 " bytes %.1f MB by factor %.2f "
|
||||
"(%.2f%% compression)",
|
||||
s, mb, relative (bytes (), s), percent (bytes () - s, bytes ()));
|
||||
else
|
||||
MSG ("inflated from %" PRId64 " bytes %.1f MB by factor %.2f "
|
||||
"(%.2f%% compression)",
|
||||
s, mb, relative (bytes (), s), percent (bytes () - s, bytes ()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void File::flush () {
|
||||
assert (file);
|
||||
fflush (file);
|
||||
}
|
||||
|
||||
File::~File () { if (file) close (); }
|
||||
|
||||
}
|
177
hCaD_V2/src/file.hpp
Normal file
177
hCaD_V2/src/file.hpp
Normal file
@ -0,0 +1,177 @@
|
||||
#ifndef _file_hpp_INCLUDED
|
||||
#define _file_hpp_INCLUDED
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <climits>
|
||||
#endif
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifndef NUNLOCKED
|
||||
#define cadical_putc_unlocked putc_unlocked
|
||||
#define cadical_getc_unlocked getc_unlocked
|
||||
#else
|
||||
#define cadical_putc_unlocked putc
|
||||
#define cadical_getc_unlocked getc
|
||||
#endif
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// Wraps a 'C' file 'FILE' with name and supports zipped reading and writing
|
||||
// through 'popen' using external helper tools. Reading has line numbers.
|
||||
// Compression and decompression relies on external utilities, e.g., 'gzip',
|
||||
// 'bzip2', 'xz', and '7z', which should be in the 'PATH'.
|
||||
|
||||
struct Internal;
|
||||
|
||||
class File {
|
||||
|
||||
#ifndef QUIET
|
||||
Internal * internal;
|
||||
#endif
|
||||
#if !defined(QUIET) || !defined(NDEBUG)
|
||||
bool writing;
|
||||
#endif
|
||||
|
||||
int close_file; // need to close file (1=fclose, 2=pclose)
|
||||
FILE * file;
|
||||
const char * _name;
|
||||
uint64_t _lineno;
|
||||
uint64_t _bytes;
|
||||
|
||||
File (Internal *, bool, int, FILE *, const char *);
|
||||
|
||||
static FILE * open_file (Internal *,
|
||||
const char * path, const char * mode);
|
||||
static FILE * read_file (Internal *, const char * path);
|
||||
static FILE * write_file (Internal *, const char * path);
|
||||
|
||||
static FILE * open_pipe (Internal *,
|
||||
const char * fmt,
|
||||
const char * path,
|
||||
const char * mode);
|
||||
static FILE * read_pipe (Internal *,
|
||||
const char * fmt,
|
||||
const int * sig,
|
||||
const char * path);
|
||||
static FILE * write_pipe (Internal *,
|
||||
const char * fmt, const char * path);
|
||||
public:
|
||||
|
||||
static char* find (const char * prg); // search in 'PATH'
|
||||
static bool exists (const char * path); // file exists?
|
||||
static bool writable (const char * path);// can write to that file?
|
||||
static size_t size (const char * path); // file size in bytes
|
||||
|
||||
// Does the file match the file type signature.
|
||||
//
|
||||
static bool match (Internal *, const char * path, const int * sig);
|
||||
|
||||
// Read from existing file. Assume given name.
|
||||
//
|
||||
static File * read (Internal *, FILE * f, const char * name);
|
||||
|
||||
// Open file from path name for reading (possibly through opening a pipe
|
||||
// to a decompression utility, based on the suffix).
|
||||
//
|
||||
static File * read (Internal *, const char * path);
|
||||
|
||||
// Same for writing as for reading above.
|
||||
//
|
||||
static File * write (Internal *, FILE *, const char * name);
|
||||
static File * write (Internal *, const char * path);
|
||||
|
||||
~File ();
|
||||
|
||||
// Using the 'unlocked' versions here is way faster but
|
||||
// not thread safe if the same file is used by different
|
||||
// threads, which on the other hand currently is impossible.
|
||||
|
||||
int get () {
|
||||
assert (!writing);
|
||||
int res = cadical_getc_unlocked (file);
|
||||
if (res == '\n') _lineno++;
|
||||
if (res != EOF) _bytes++;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool put (char ch) {
|
||||
assert (writing);
|
||||
if (cadical_putc_unlocked (ch, file) == EOF) return false;
|
||||
_bytes++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool put (unsigned char ch) {
|
||||
assert (writing);
|
||||
if (cadical_putc_unlocked (ch, file) == EOF) return false;
|
||||
_bytes++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool put (const char * s) {
|
||||
for (const char * p = s; *p; p++)
|
||||
if (!put (*p)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool put (int lit) {
|
||||
assert (writing);
|
||||
if (!lit) return put ('0');
|
||||
else if (lit == -2147483648) {
|
||||
assert (lit == INT_MIN);
|
||||
return put ("-2147483648");
|
||||
} else {
|
||||
char buffer[11];
|
||||
int i = sizeof buffer;
|
||||
buffer[--i] = 0;
|
||||
assert (lit != INT_MIN);
|
||||
unsigned idx = abs (lit);
|
||||
while (idx) {
|
||||
assert (i > 0);
|
||||
buffer[--i] = '0' + idx % 10;
|
||||
idx /= 10;
|
||||
}
|
||||
if (lit < 0 && !put ('-')) return false;
|
||||
return put (buffer + i);
|
||||
}
|
||||
}
|
||||
|
||||
bool put (int64_t l) {
|
||||
assert (writing);
|
||||
if (!l) return put ('0');
|
||||
else if (l == INT64_MIN) {
|
||||
assert (sizeof l == 8);
|
||||
return put ("-9223372036854775808");
|
||||
} else {
|
||||
char buffer[21];
|
||||
int i = sizeof buffer;
|
||||
buffer[--i] = 0;
|
||||
assert (l != INT64_MIN);
|
||||
uint64_t k = l < 0 ? -l : l;
|
||||
while (k) {
|
||||
assert (i > 0);
|
||||
buffer[--i] = '0' + k % 10;
|
||||
k /= 10;
|
||||
}
|
||||
if (l < 0 && !put ('-')) return false;
|
||||
return put (buffer + i);
|
||||
}
|
||||
}
|
||||
|
||||
const char * name () const { return _name; }
|
||||
uint64_t lineno () const { return _lineno; }
|
||||
uint64_t bytes () const { return _bytes; }
|
||||
|
||||
bool closed () { return !file; }
|
||||
void close ();
|
||||
void flush ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
119
hCaD_V2/src/flags.cpp
Normal file
119
hCaD_V2/src/flags.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
void Internal::mark_fixed (int lit) {
|
||||
Flags & f = flags (lit);
|
||||
assert (f.status == Flags::ACTIVE);
|
||||
f.status = Flags::FIXED;
|
||||
LOG ("fixed %d", abs (lit));
|
||||
stats.all.fixed++;
|
||||
stats.now.fixed++;
|
||||
stats.inactive++;
|
||||
assert (stats.active);
|
||||
stats.active--;
|
||||
assert (!active (lit));
|
||||
assert (f.fixed ());
|
||||
}
|
||||
|
||||
void Internal::mark_eliminated (int lit) {
|
||||
Flags & f = flags (lit);
|
||||
assert (f.status == Flags::ACTIVE);
|
||||
f.status = Flags::ELIMINATED;
|
||||
LOG ("eliminated %d", abs (lit));
|
||||
stats.all.eliminated++;
|
||||
stats.now.eliminated++;
|
||||
stats.inactive++;
|
||||
assert (stats.active);
|
||||
stats.active--;
|
||||
assert (!active (lit));
|
||||
assert (f.eliminated ());
|
||||
}
|
||||
|
||||
void Internal::mark_pure (int lit) {
|
||||
Flags & f = flags (lit);
|
||||
assert (f.status == Flags::ACTIVE);
|
||||
f.status = Flags::PURE;
|
||||
LOG ("pure %d", abs (lit));
|
||||
stats.all.pure++;
|
||||
stats.now.pure++;
|
||||
stats.inactive++;
|
||||
assert (stats.active);
|
||||
stats.active--;
|
||||
assert (!active (lit));
|
||||
assert (f.pure ());
|
||||
}
|
||||
|
||||
void Internal::mark_substituted (int lit) {
|
||||
Flags & f = flags (lit);
|
||||
assert (f.status == Flags::ACTIVE);
|
||||
f.status = Flags::SUBSTITUTED;
|
||||
LOG ("substituted %d", abs (lit));
|
||||
stats.all.substituted++;
|
||||
stats.now.substituted++;
|
||||
stats.inactive++;
|
||||
assert (stats.active);
|
||||
stats.active--;
|
||||
assert (!active (lit));
|
||||
assert (f.substituted ());
|
||||
}
|
||||
|
||||
void Internal::mark_active (int lit) {
|
||||
Flags & f = flags (lit);
|
||||
assert (f.status == Flags::UNUSED);
|
||||
f.status = Flags::ACTIVE;
|
||||
LOG ("activate %d previously unused", abs (lit));
|
||||
assert (stats.inactive);
|
||||
stats.inactive--;
|
||||
assert (stats.unused);
|
||||
stats.unused--;
|
||||
stats.active++;
|
||||
assert (active (lit));
|
||||
}
|
||||
|
||||
void Internal::reactivate (int lit) {
|
||||
assert (!active (lit));
|
||||
Flags & f = flags (lit);
|
||||
assert (f.status != Flags::FIXED);
|
||||
assert (f.status != Flags::UNUSED);
|
||||
#ifdef LOGGING
|
||||
const char * msg = 0;
|
||||
#endif
|
||||
switch (f.status) {
|
||||
default:
|
||||
case Flags::ELIMINATED:
|
||||
assert (f.status == Flags::ELIMINATED);
|
||||
assert (stats.now.eliminated > 0);
|
||||
stats.now.eliminated--;
|
||||
#ifdef LOGGING
|
||||
msg = "eliminated";
|
||||
#endif
|
||||
break;
|
||||
case Flags::SUBSTITUTED:
|
||||
#ifdef LOGGING
|
||||
msg = "substituted";
|
||||
#endif
|
||||
assert (stats.now.substituted > 0);
|
||||
stats.now.substituted--;
|
||||
break;
|
||||
case Flags::PURE:
|
||||
#ifdef LOGGING
|
||||
msg = "pure literal";
|
||||
#endif
|
||||
assert (stats.now.pure > 0);
|
||||
stats.now.pure--;
|
||||
break;
|
||||
}
|
||||
#ifdef LOGGING
|
||||
assert (msg);
|
||||
LOG ("reactivate previously %s %d", msg, abs (lit));
|
||||
#endif
|
||||
f.status = Flags::ACTIVE;
|
||||
assert (active (lit));
|
||||
stats.reactivated++;
|
||||
assert (stats.inactive > 0);
|
||||
stats.inactive--;
|
||||
stats.active++;
|
||||
}
|
||||
|
||||
}
|
76
hCaD_V2/src/flags.hpp
Normal file
76
hCaD_V2/src/flags.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef _flags_hpp_INCLUDED
|
||||
#define _flags_hpp_INCLUDED
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct Flags { // Variable flags.
|
||||
|
||||
// The first set of flags is related to 'analyze' and 'minimize'.
|
||||
//
|
||||
bool seen : 1; // seen in generating first UIP clause in 'analyze'
|
||||
bool keep : 1; // keep in learned clause in 'minimize'
|
||||
bool poison : 1; // can not be removed in 'minimize'
|
||||
bool removable : 1; // can be removed in 'minimize'
|
||||
bool shrinkable : 1; // can be removed in 'shrink'
|
||||
|
||||
// These three variable flags are used to schedule clauses in subsumption
|
||||
// ('subsume'), variables in bounded variable elimination ('elim') and in
|
||||
// hyper ternary resolution ('ternary').
|
||||
//
|
||||
bool elim : 1; // removed since last 'elim' round (*)
|
||||
bool subsume : 1; // added since last 'subsume' round (*)
|
||||
bool ternary : 1; // added in ternary clause since last 'ternary' (*)
|
||||
|
||||
// These literal flags are used by blocked clause elimination ('block').
|
||||
//
|
||||
unsigned char block : 2; // removed since last 'block' round (*)
|
||||
unsigned char skip : 2; // skip this literal as blocking literal
|
||||
|
||||
// Bits for handling assumptions.
|
||||
//
|
||||
unsigned char assumed : 2;
|
||||
unsigned char failed : 2;
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
ACTIVE = 1,
|
||||
FIXED = 2,
|
||||
ELIMINATED = 3,
|
||||
SUBSTITUTED = 4,
|
||||
PURE = 5
|
||||
};
|
||||
|
||||
unsigned char status : 3;
|
||||
|
||||
// Initialized explicitly in 'Internal::init' through this function.
|
||||
//
|
||||
Flags () {
|
||||
seen = keep = poison = removable = shrinkable = false;
|
||||
subsume = elim = ternary = true;
|
||||
block = 3u;
|
||||
skip = assumed = failed = 0;
|
||||
status = UNUSED;
|
||||
}
|
||||
|
||||
bool unused () const { return status == UNUSED; }
|
||||
bool active () const { return status == ACTIVE; }
|
||||
bool fixed () const { return status == FIXED; }
|
||||
bool eliminated () const { return status == ELIMINATED; }
|
||||
bool substituted () const { return status == SUBSTITUTED; }
|
||||
bool pure () const { return status == PURE; }
|
||||
|
||||
// The flags marked with '(*)' are copied during 'External::copy_flags',
|
||||
// which in essence means they are reset in the copy if they were clear.
|
||||
// This avoids the effort of fruitless preprocessing the copy.
|
||||
|
||||
void copy (Flags & dst) const {
|
||||
dst.elim = elim;
|
||||
dst.subsume = subsume;
|
||||
dst.ternary = ternary;
|
||||
dst.block = block;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
81
hCaD_V2/src/format.cpp
Normal file
81
hCaD_V2/src/format.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
void Format::enlarge () {
|
||||
char * old = buffer;
|
||||
buffer = new char[size = size ? 2*size : 1];
|
||||
memcpy (buffer, old, count);
|
||||
delete [] old;
|
||||
}
|
||||
|
||||
inline void Format::push_char (char ch) {
|
||||
if (size == count) enlarge ();
|
||||
buffer[count++] = ch;
|
||||
}
|
||||
|
||||
void Format::push_string (const char * s) {
|
||||
char ch;
|
||||
while ((ch = *s++)) push_char (ch);
|
||||
}
|
||||
|
||||
void Format::push_int (int d) {
|
||||
char tmp[12];
|
||||
sprintf (tmp, "%d", d);
|
||||
push_string (tmp);
|
||||
}
|
||||
|
||||
void Format::push_uint64 (uint64_t u) {
|
||||
char tmp[12];
|
||||
sprintf (tmp, "%" PRIu64, u);
|
||||
push_string (tmp);
|
||||
}
|
||||
|
||||
static bool
|
||||
match_format (const char * & str, const char * pattern)
|
||||
{
|
||||
assert (pattern);
|
||||
const char * p = str;
|
||||
const char * q = pattern;
|
||||
while (*q)
|
||||
if (*q++ != *p++)
|
||||
return false;
|
||||
str = p;
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * Format::add (const char * fmt, va_list & ap) {
|
||||
const char * p = fmt;
|
||||
char ch;
|
||||
while ((ch = *p++)) {
|
||||
if (ch != '%') push_char (ch);
|
||||
else if (*p == 'c') push_char (va_arg (ap, int)), p++;
|
||||
else if (*p == 'd') push_int (va_arg (ap, int)), p++;
|
||||
else if (*p == 's') push_string (va_arg (ap, const char*)), p++;
|
||||
else if (match_format (p, PRIu64))
|
||||
push_uint64 (va_arg (ap, uint64_t));
|
||||
else { push_char ('%'); push_char (*p); break; } // unsupported
|
||||
}
|
||||
push_char (0);
|
||||
count--; // thus automatic append in subsequent calls.
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char * Format::init (const char * fmt, ...) {
|
||||
count = 0;
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
const char * res = add (fmt, ap);
|
||||
va_end (ap);
|
||||
return res;
|
||||
}
|
||||
|
||||
const char * Format::append (const char * fmt, ...) {
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
const char * res = add (fmt, ap);
|
||||
va_end (ap);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
33
hCaD_V2/src/format.hpp
Normal file
33
hCaD_V2/src/format.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef _format_hpp_INCLUDED
|
||||
#define _format_hpp_INCLUDED
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// This class provides a 'printf' style formatting utility.
|
||||
// Only '%c', '%d', '%s' are supported at this point.
|
||||
// It is used to capture and save an error message.
|
||||
|
||||
class Format {
|
||||
char * buffer;
|
||||
int64_t count, size;
|
||||
void enlarge ();
|
||||
void push_char (char);
|
||||
void push_string (const char *);
|
||||
void push_int (int);
|
||||
void push_uint64 (uint64_t);
|
||||
const char * add (const char * fmt, va_list &);
|
||||
public:
|
||||
Format () : buffer (0), count (0), size (0) { }
|
||||
~Format () { if (buffer) delete [] buffer; }
|
||||
const char * init (const char * fmt, ...)
|
||||
CADICAL_ATTRIBUTE_FORMAT (2, 3);
|
||||
const char * append (const char * fmt, ...)
|
||||
CADICAL_ATTRIBUTE_FORMAT (2, 3);
|
||||
operator const char * () const { return count ? buffer : 0; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
527
hCaD_V2/src/gates.cpp
Normal file
527
hCaD_V2/src/gates.cpp
Normal file
@ -0,0 +1,527 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// As in our original SATeLite published at SAT'05 we are trying to find
|
||||
// gates in order to restrict the number of resolutions that need to be
|
||||
// tried. If there is such a gate, we only need to consider resolvents
|
||||
// among gate and one non-gate clauses. Resolvents between definitions will
|
||||
// be tautological anyhow and resolvents among non-gates can actually be
|
||||
// shown to be redundant too.
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The next function returns a non-zero if the clause 'c', which is assumed
|
||||
// to contain the literal 'first', after removing falsified literals is a
|
||||
// binary clause. Then the actual second literal is returned.
|
||||
|
||||
int
|
||||
Internal::second_literal_in_binary_clause (Eliminator & eliminator,
|
||||
Clause * c, int first)
|
||||
{
|
||||
assert (!c->garbage);
|
||||
int second = 0;
|
||||
for (const auto & lit : *c) {
|
||||
if (lit == first) continue;
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp < 0) continue;
|
||||
if (tmp > 0) {
|
||||
mark_garbage (c);
|
||||
elim_update_removed_clause (eliminator, c);
|
||||
return 0;
|
||||
}
|
||||
if (second) { second = INT_MIN; break; }
|
||||
second = lit;
|
||||
}
|
||||
if (!second) return 0;
|
||||
if (second == INT_MIN) return 0;
|
||||
assert (active (second));
|
||||
#ifdef LOGGING
|
||||
if (c->size == 2) LOG (c, "found binary");
|
||||
else LOG (c, "found actual binary %d %d", first, second);
|
||||
#endif
|
||||
return second;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Mark all other literals in binary clauses with 'first'. During this
|
||||
// marking we might also detect hyper unary resolvents producing a unit.
|
||||
// If such a unit is found we propagate it and return immediately.
|
||||
|
||||
void Internal::mark_binary_literals (Eliminator & eliminator, int first) {
|
||||
|
||||
if (unsat) return;
|
||||
if (val (first)) return;
|
||||
if (!eliminator.gates.empty ()) return;
|
||||
|
||||
assert (!marked (first));
|
||||
assert (eliminator.marked.empty ());
|
||||
|
||||
const Occs & os = occs (first);
|
||||
for (const auto & c : os) {
|
||||
if (c->garbage) continue;
|
||||
const int second =
|
||||
second_literal_in_binary_clause (eliminator, c, first);
|
||||
if (!second) continue;
|
||||
const int tmp = marked (second);
|
||||
if (tmp < 0) {
|
||||
LOG ("found binary resolved unit %d", first);
|
||||
assign_unit (first);
|
||||
elim_propagate (eliminator, first);
|
||||
return;
|
||||
}
|
||||
if (tmp > 0) {
|
||||
LOG (c, "duplicated actual binary clause");
|
||||
elim_update_removed_clause (eliminator, c);
|
||||
mark_garbage (c);
|
||||
continue;
|
||||
}
|
||||
eliminator.marked.push_back (second);
|
||||
mark (second);
|
||||
LOG ("marked second literal %d in binary clause %d %d",
|
||||
second, first, second);
|
||||
}
|
||||
}
|
||||
|
||||
// Unmark all literals saved on the 'marked' stack.
|
||||
|
||||
void Internal::unmark_binary_literals (Eliminator & eliminator) {
|
||||
LOG ("unmarking %zd literals", eliminator.marked.size ());
|
||||
for (const auto & lit : eliminator.marked)
|
||||
unmark (lit);
|
||||
eliminator.marked.clear ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Find equivalence for 'pivot'. Requires that all other literals in binary
|
||||
// clauses with 'pivot' are marked (through 'mark_binary_literals');
|
||||
|
||||
void Internal::find_equivalence (Eliminator & eliminator, int pivot) {
|
||||
|
||||
if (!opts.elimequivs) return;
|
||||
|
||||
assert (opts.elimsubst);
|
||||
|
||||
if (unsat) return;
|
||||
if (val (pivot)) return;
|
||||
if (!eliminator.gates.empty ()) return;
|
||||
|
||||
mark_binary_literals (eliminator, pivot);
|
||||
if (unsat || val (pivot)) goto DONE;
|
||||
|
||||
for (const auto & c : occs (-pivot)) {
|
||||
|
||||
if (c->garbage) continue;
|
||||
|
||||
const int second =
|
||||
second_literal_in_binary_clause (eliminator, c, -pivot);
|
||||
if (!second) continue;
|
||||
const int tmp = marked (second);
|
||||
if (tmp > 0) {
|
||||
LOG ("found binary resolved unit %d", second);
|
||||
assign_unit (second);
|
||||
elim_propagate (eliminator, second);
|
||||
if (val (pivot)) break;
|
||||
if (unsat) break;
|
||||
}
|
||||
if (tmp >= 0) continue;
|
||||
|
||||
LOG ("found equivalence %d = %d", pivot, -second);
|
||||
stats.elimequivs++;
|
||||
stats.elimgates++;
|
||||
|
||||
LOG (c, "first gate clause");
|
||||
assert (!c->gate);
|
||||
c->gate = true;
|
||||
eliminator.gates.push_back (c);
|
||||
|
||||
Clause * d = 0;
|
||||
const Occs & ps = occs (pivot);
|
||||
for (const auto & e : ps) {
|
||||
if (e->garbage) continue;
|
||||
const int other =
|
||||
second_literal_in_binary_clause (eliminator, e, pivot);
|
||||
if (other == -second) { d = e; break; }
|
||||
}
|
||||
assert (d);
|
||||
|
||||
LOG (d, "second gate clause");
|
||||
assert (!d->gate);
|
||||
d->gate = true;
|
||||
eliminator.gates.push_back (d);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
DONE:
|
||||
unmark_binary_literals (eliminator);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Find and gates for 'pivot' with a long clause, in which the pivot occurs
|
||||
// positively. Requires that all other literals in binary clauses with
|
||||
// 'pivot' are marked (through 'mark_binary_literals');
|
||||
|
||||
void Internal::find_and_gate (Eliminator & eliminator, int pivot) {
|
||||
|
||||
if (!opts.elimands) return;
|
||||
|
||||
assert (opts.elimsubst);
|
||||
|
||||
if (unsat) return;
|
||||
if (val (pivot)) return;
|
||||
if (!eliminator.gates.empty ()) return;
|
||||
|
||||
mark_binary_literals (eliminator, pivot);
|
||||
if (unsat || val (pivot)) goto DONE;
|
||||
|
||||
for (const auto & c : occs (-pivot)) {
|
||||
|
||||
if (c->garbage) continue;
|
||||
if (c->size < 3) continue;
|
||||
|
||||
bool all_literals_marked = true;
|
||||
unsigned arity = 0;
|
||||
for (const auto & lit : *c) {
|
||||
if (lit == -pivot) continue;
|
||||
assert (lit != pivot);
|
||||
signed char tmp = val (lit);
|
||||
if (tmp < 0) continue;
|
||||
assert (!tmp);
|
||||
tmp = marked (lit);
|
||||
if (tmp < 0) { arity++; continue; }
|
||||
all_literals_marked = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!all_literals_marked) continue;
|
||||
|
||||
#ifdef LOGGING
|
||||
if (opts.log) {
|
||||
Logger::print_log_prefix (this);
|
||||
tout.magenta ();
|
||||
printf ("found arity %u AND gate %d = ", arity, -pivot);
|
||||
bool first = true;
|
||||
for (const auto & lit : *c) {
|
||||
if (lit == -pivot) continue;
|
||||
assert (lit != pivot);
|
||||
if (!first) fputs (" & ", stdout);
|
||||
printf ("%d", -lit);
|
||||
first = false;
|
||||
}
|
||||
fputc ('\n', stdout);
|
||||
tout.normal ();
|
||||
fflush (stdout);
|
||||
}
|
||||
#endif
|
||||
stats.elimands++;
|
||||
stats.elimgates++;
|
||||
|
||||
(void) arity;
|
||||
assert (!c->gate);
|
||||
c->gate = true;
|
||||
eliminator.gates.push_back (c);
|
||||
for (const auto & lit : *c) {
|
||||
if (lit == -pivot) continue;
|
||||
assert (lit != pivot);
|
||||
signed char tmp = val (lit);
|
||||
if (tmp < 0) continue;
|
||||
assert (!tmp);
|
||||
assert (marked (lit) < 0);
|
||||
marks [vidx (lit)] *= 2;
|
||||
}
|
||||
|
||||
unsigned count = 0;
|
||||
for (const auto & d : occs (pivot)) {
|
||||
if (d->garbage) continue;
|
||||
const int other =
|
||||
second_literal_in_binary_clause (eliminator, d, pivot);
|
||||
if (!other) continue;
|
||||
const int tmp = marked (other);
|
||||
if (tmp != 2) continue;
|
||||
LOG (d, "AND gate binary side clause");
|
||||
assert (!d->gate);
|
||||
d->gate = true;
|
||||
eliminator.gates.push_back (d);
|
||||
count++;
|
||||
}
|
||||
assert (count >= arity);
|
||||
(void) count;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
DONE:
|
||||
unmark_binary_literals (eliminator);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Find and extract ternary clauses.
|
||||
|
||||
bool Internal::get_ternary_clause (Clause * d, int & a, int & b, int & c)
|
||||
{
|
||||
if (d->garbage) return false;
|
||||
if (d->size < 3) return false;
|
||||
int found = 0;
|
||||
a = b = c = 0;
|
||||
for (const auto & lit : *d) {
|
||||
if (val (lit)) continue;
|
||||
if (++found == 1) a = lit;
|
||||
else if (found == 2) b = lit;
|
||||
else if (found == 3) c = lit;
|
||||
else return false;
|
||||
}
|
||||
return found == 3;
|
||||
}
|
||||
|
||||
// This function checks whether 'd' exists as ternary clause.
|
||||
|
||||
bool Internal::match_ternary_clause (Clause * d, int a, int b, int c) {
|
||||
if (d->garbage) return false;
|
||||
int found = 0;
|
||||
for (const auto & lit : *d) {
|
||||
if (val (lit)) continue;
|
||||
if (a != lit && b != lit && c != lit) return false;
|
||||
found++;
|
||||
}
|
||||
return found == 3;
|
||||
}
|
||||
|
||||
Clause *
|
||||
Internal::find_ternary_clause (int a, int b, int c) {
|
||||
if (occs (b).size () > occs (c).size ()) swap (b, c);
|
||||
if (occs (a).size () > occs (b).size ()) swap (a, b);
|
||||
for (auto d : occs (a))
|
||||
if (match_ternary_clause (d, a, b, c))
|
||||
return d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Find if-then-else gate.
|
||||
|
||||
void Internal::find_if_then_else (Eliminator & eliminator, int pivot) {
|
||||
|
||||
if (!opts.elimites) return;
|
||||
|
||||
assert (opts.elimsubst);
|
||||
|
||||
if (unsat) return;
|
||||
if (val (pivot)) return;
|
||||
if (!eliminator.gates.empty ()) return;
|
||||
|
||||
const Occs & os = occs (pivot);
|
||||
const auto end = os.end ();
|
||||
for (auto i = os.begin (); i != end; i++) {
|
||||
Clause * di = *i;
|
||||
int ai, bi, ci;
|
||||
if (!get_ternary_clause (di, ai, bi, ci)) continue;
|
||||
if (bi == pivot) swap (ai, bi);
|
||||
if (ci == pivot) swap (ai, ci);
|
||||
assert (ai == pivot);
|
||||
for (auto j = i + 1; j != end; j++) {
|
||||
Clause * dj = *j;
|
||||
int aj, bj, cj;
|
||||
if (!get_ternary_clause (dj, aj, bj, cj)) continue;
|
||||
if (bj == pivot) swap (aj, bj);
|
||||
if (cj == pivot) swap (aj, cj);
|
||||
assert (aj == pivot);
|
||||
if (abs (bi) == abs (cj)) swap (bj, cj);
|
||||
if (abs (ci) == abs (cj)) continue;
|
||||
if (bi != -bj) continue;
|
||||
Clause * d1 = find_ternary_clause (-pivot, bi, -ci);
|
||||
if (!d1) continue;
|
||||
Clause * d2 = find_ternary_clause (-pivot, bj, -cj);
|
||||
if (!d2) continue;
|
||||
LOG (di, "1st if-then-else");
|
||||
LOG (dj, "2nd if-then-else");
|
||||
LOG (d1, "3rd if-then-else");
|
||||
LOG (d2, "4th if-then-else");
|
||||
LOG ("found ITE gate %d == (%d ? %d : %d)", pivot, -bi, -ci, -cj);
|
||||
assert (!di->gate);
|
||||
assert (!dj->gate);
|
||||
assert (!d1->gate);
|
||||
assert (!d2->gate);
|
||||
di->gate = true;
|
||||
dj->gate = true;
|
||||
d1->gate = true;
|
||||
d2->gate = true;
|
||||
eliminator.gates.push_back (di);
|
||||
eliminator.gates.push_back (dj);
|
||||
eliminator.gates.push_back (d1);
|
||||
eliminator.gates.push_back (d2);
|
||||
stats.elimgates++;
|
||||
stats.elimites++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Find and extract clause.
|
||||
|
||||
bool Internal::get_clause (Clause * c, vector<int> & l) {
|
||||
if (c->garbage) return false;
|
||||
l.clear ();
|
||||
for (const auto & lit : *c) {
|
||||
if (val (lit)) continue;
|
||||
l.push_back (lit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether 'c' contains only the literals in 'l'.
|
||||
|
||||
bool Internal::is_clause (Clause * c, const vector<int> & lits) {
|
||||
if (c->garbage) return false;
|
||||
int size = lits.size ();
|
||||
if (c->size < size) return false;
|
||||
int found = 0;
|
||||
for (const auto & lit : *c) {
|
||||
if (val (lit)) continue;
|
||||
const auto it = find (lits.begin (), lits.end (), lit);
|
||||
if (it == lits.end ()) return false;
|
||||
if (++found > size) return false;
|
||||
}
|
||||
return found == size;
|
||||
}
|
||||
|
||||
Clause * Internal::find_clause (const vector<int> & lits) {
|
||||
int best = 0;
|
||||
size_t len = 0;
|
||||
for (const auto & lit : lits) {
|
||||
size_t l = occs (lit).size ();
|
||||
if (best && l >= len) continue;
|
||||
len = l, best = lit;
|
||||
}
|
||||
for (auto c : occs (best))
|
||||
if (is_clause (c, lits))
|
||||
return c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Internal::find_xor_gate (Eliminator & eliminator, int pivot) {
|
||||
|
||||
if (!opts.elimxors) return;
|
||||
|
||||
assert (opts.elimsubst);
|
||||
|
||||
if (unsat) return;
|
||||
if (val (pivot)) return;
|
||||
if (!eliminator.gates.empty ()) return;
|
||||
|
||||
vector<int> lits;
|
||||
|
||||
for (auto d : occs (pivot)) {
|
||||
|
||||
if (!get_clause (d, lits)) continue;
|
||||
|
||||
const int size = lits.size (); // clause size
|
||||
const int arity = size - 1; // arity of XOR
|
||||
|
||||
if (size < 3) continue;
|
||||
if (arity > opts.elimxorlim) continue;
|
||||
|
||||
assert (eliminator.gates.empty ());
|
||||
|
||||
unsigned needed = (1u << arity) - 1; // additional clauses
|
||||
unsigned signs = 0; // literals to negate
|
||||
|
||||
do {
|
||||
const unsigned prev = signs;
|
||||
while (parity (++signs))
|
||||
;
|
||||
for (int j = 0; j < size; j++) {
|
||||
const unsigned bit = 1u << j;
|
||||
int lit = lits[j];
|
||||
if ((prev & bit) != (signs & bit))
|
||||
lits[j] = lit = -lit;
|
||||
}
|
||||
Clause * e = find_clause (lits);
|
||||
if (!e) break;
|
||||
eliminator.gates.push_back (e);
|
||||
} while (--needed);
|
||||
|
||||
if (needed) { eliminator.gates.clear (); continue; }
|
||||
|
||||
eliminator.gates.push_back (d);
|
||||
assert (eliminator.gates.size () == (1u << arity));
|
||||
|
||||
#ifdef LOGGING
|
||||
if (opts.log) {
|
||||
Logger::print_log_prefix (this);
|
||||
tout.magenta ();
|
||||
printf ("found arity %u XOR gate %d = ", arity, -pivot);
|
||||
bool first = true;
|
||||
for (const auto & lit : *d) {
|
||||
if (lit == pivot) continue;
|
||||
assert (lit != -pivot);
|
||||
if (!first) fputs (" ^ ", stdout);
|
||||
printf ("%d", lit);
|
||||
first = false;
|
||||
}
|
||||
fputc ('\n', stdout);
|
||||
tout.normal ();
|
||||
fflush (stdout);
|
||||
}
|
||||
#endif
|
||||
stats.elimgates++;
|
||||
stats.elimxors++;
|
||||
const auto end = eliminator.gates.end ();
|
||||
auto j = eliminator.gates.begin ();
|
||||
for (auto i = j; i != end; i++) {
|
||||
Clause * e = *i;
|
||||
if (e->gate) continue;
|
||||
e->gate = true;
|
||||
LOG (e, "contributing");
|
||||
*j++ = e;
|
||||
}
|
||||
eliminator.gates.resize (j - eliminator.gates.begin ());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Find a gate for 'pivot'. If such a gate is found, the gate clauses are
|
||||
// marked and pushed on the stack of gates. Further hyper unary resolution
|
||||
// might detect units, which are propagated. This might assign the pivot or
|
||||
// even produce the empty clause.
|
||||
|
||||
void Internal::find_gate_clauses (Eliminator & eliminator, int pivot)
|
||||
{
|
||||
if (!opts.elimsubst) return;
|
||||
|
||||
if (unsat) return;
|
||||
if (val (pivot)) return;
|
||||
|
||||
assert (eliminator.gates.empty ());
|
||||
|
||||
find_equivalence (eliminator, pivot);
|
||||
find_and_gate (eliminator, pivot);
|
||||
find_and_gate (eliminator, -pivot);
|
||||
find_if_then_else (eliminator, pivot);
|
||||
find_xor_gate (eliminator, pivot);
|
||||
}
|
||||
|
||||
void Internal::unmark_gate_clauses (Eliminator & eliminator) {
|
||||
LOG ("unmarking %zd gate clauses", eliminator.gates.size ());
|
||||
for (const auto & c : eliminator.gates) {
|
||||
assert (c->gate);
|
||||
c->gate = false;
|
||||
}
|
||||
eliminator.gates.clear ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
}
|
198
hCaD_V2/src/heap.hpp
Normal file
198
hCaD_V2/src/heap.hpp
Normal file
@ -0,0 +1,198 @@
|
||||
#ifndef _heap_hpp_INCLUDED
|
||||
#define _heap_hpp_INCLUDED
|
||||
|
||||
#include "util.hpp" // Alphabetically after 'heap.hpp'.
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
using namespace std;
|
||||
|
||||
// This is a priority queue with updates for unsigned integers implemented
|
||||
// as binary heap. We need to map integer elements added (through
|
||||
// 'push_back') to positions on the binary heap in 'array'. This map is
|
||||
// stored in the 'pos' array. This approach is really wasteful (at least in
|
||||
// terms of memory) if only few and a sparse set of integers is added. So
|
||||
// it should not be used in this situation. A generic priority queue would
|
||||
// implement the mapping externally provided by another template parameter.
|
||||
// Since we use 'UINT_MAX' as 'not contained' flag, we can only have
|
||||
// 'UINT_MAX - 1' elements in the heap.
|
||||
|
||||
const unsigned invalid_heap_position = UINT_MAX;
|
||||
|
||||
template<class C> class heap {
|
||||
|
||||
vector<unsigned> array; // actual binary heap
|
||||
vector<unsigned> pos; // positions of elements in array
|
||||
C less; // less-than for elements
|
||||
|
||||
// Map an element to its position entry in the 'pos' map.
|
||||
//
|
||||
unsigned & index (unsigned e) {
|
||||
while ((size_t) e >= pos.size ()) pos.push_back (invalid_heap_position);
|
||||
unsigned & res = pos[e];
|
||||
assert (res == invalid_heap_position || (size_t) res < array.size ());
|
||||
return res;
|
||||
}
|
||||
|
||||
bool has_parent (unsigned e) { return index (e) > 0; }
|
||||
bool has_left (unsigned e) { return (size_t) 2*index (e) + 1 < size (); }
|
||||
bool has_right (unsigned e) { return (size_t) 2*index (e) + 2 < size (); }
|
||||
|
||||
unsigned parent (unsigned e) {
|
||||
assert(has_parent (e));
|
||||
return array[(index(e)-1)/2];
|
||||
}
|
||||
|
||||
unsigned left (unsigned e) {
|
||||
assert(has_left (e));
|
||||
return array[2*index(e)+1];
|
||||
}
|
||||
|
||||
unsigned right (unsigned e) {
|
||||
assert(has_right (e));
|
||||
return array[2*index(e)+2];
|
||||
}
|
||||
|
||||
// Exchange elements 'a' and 'b' in 'array' and fix their positions.
|
||||
//
|
||||
void exchange (unsigned a, unsigned b) {
|
||||
unsigned & i = index (a), & j = index (b);
|
||||
swap (array[i], array[j]);
|
||||
swap (i, j);
|
||||
}
|
||||
|
||||
// Bubble up an element as far as necessary.
|
||||
//
|
||||
void up (unsigned e) {
|
||||
unsigned p;
|
||||
while (has_parent (e) && less ((p = parent (e)), e))
|
||||
exchange (p, e);
|
||||
}
|
||||
|
||||
// Bubble down an element as far as necessary.
|
||||
//
|
||||
void down (unsigned e) {
|
||||
while (has_left (e)) {
|
||||
unsigned c = left (e);
|
||||
if (has_right (e)) {
|
||||
unsigned r = right (e);
|
||||
if (less (c, r)) c = r;
|
||||
}
|
||||
if (!less (e, c)) break;
|
||||
exchange (e, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Very expensive checker for the main 'heap' invariant. Can be enabled
|
||||
// to find violations of antisymmetry in the client implementation of
|
||||
// 'less' and as well of course bugs in this heap implementation. It
|
||||
// should be enabled during testing applications of the heap.
|
||||
//
|
||||
void check () {
|
||||
#if 0 // EXPENSIVE HEAP CHECKING IF ENABLED
|
||||
#warning "expensive checking in heap enabled"
|
||||
assert (array.size () <= invalid_heap_position);
|
||||
for (size_t i = 0; i < array.size (); i++) {
|
||||
size_t l = 2*i + 1, r = 2*i + 2;
|
||||
if (l < array.size ()) assert (!less (array[i], array[l]));
|
||||
if (r < array.size ()) assert (!less (array[i], array[r]));
|
||||
assert (array[i] >= 0);
|
||||
{
|
||||
assert ((size_t) array[i] < pos.size ());
|
||||
assert (i == (size_t) pos[array[i]]);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < pos.size (); i++) {
|
||||
if (pos[i] == invalid_heap_position) continue;
|
||||
assert (pos[i] < array.size ());
|
||||
assert (array[pos[i]] == (unsigned) i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
heap (const C & c) : less (c) { }
|
||||
|
||||
// Number of elements in the heap.
|
||||
//
|
||||
size_t size () const { return array.size (); }
|
||||
|
||||
// Check if no more elements are in the heap.
|
||||
//
|
||||
bool empty () const { return array.empty (); }
|
||||
|
||||
// Check whether 'e' is already in the heap.
|
||||
//
|
||||
bool contains (unsigned e) const {
|
||||
if ((size_t) e >= pos.size ()) return false;
|
||||
return pos[e] != invalid_heap_position;
|
||||
}
|
||||
|
||||
// Add a new (not contained) element 'e' to the heap.
|
||||
//
|
||||
void push_back (unsigned e) {
|
||||
assert (!contains (e));
|
||||
size_t i = array.size ();
|
||||
assert (i < (size_t) invalid_heap_position);
|
||||
array.push_back (e);
|
||||
index (e) = (unsigned) i;
|
||||
up (e);
|
||||
down (e);
|
||||
check ();
|
||||
}
|
||||
|
||||
// Returns the maximum element in the heap.
|
||||
//
|
||||
unsigned front () const { assert (!empty ()); return array[0]; }
|
||||
|
||||
// Removes the maximum element in the heap.
|
||||
//
|
||||
unsigned pop_front () {
|
||||
assert (!empty ());
|
||||
unsigned res = array[0], last = array.back ();
|
||||
if (size () > 1) exchange (res, last);
|
||||
index (res) = invalid_heap_position;
|
||||
array.pop_back ();
|
||||
if (size () > 1) down (last);
|
||||
check ();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Notify the heap, that evaluation of 'less' has changed for 'e'.
|
||||
//
|
||||
void update (unsigned e) {
|
||||
assert (contains (e));
|
||||
up (e);
|
||||
down (e);
|
||||
check ();
|
||||
}
|
||||
|
||||
void clear () {
|
||||
array.clear ();
|
||||
pos.clear ();
|
||||
}
|
||||
|
||||
void erase () {
|
||||
erase_vector (array);
|
||||
erase_vector (pos);
|
||||
}
|
||||
|
||||
void shrink () {
|
||||
shrink_vector (array);
|
||||
shrink_vector (pos);
|
||||
}
|
||||
|
||||
// Standard iterators 'inherited' from 'vector'.
|
||||
//
|
||||
typedef typename vector<unsigned>::iterator iterator;
|
||||
typedef typename vector<unsigned>::const_iterator const_iterator;
|
||||
iterator begin () { return array.begin (); }
|
||||
iterator end () { return array.end (); }
|
||||
const_iterator begin () const { return array.begin (); }
|
||||
const_iterator end () const { return array.end (); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
241
hCaD_V2/src/instantiate.cpp
Normal file
241
hCaD_V2/src/instantiate.cpp
Normal file
@ -0,0 +1,241 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This provides an implementation of variable instantiation, a technique
|
||||
// for removing literals with few occurrence (see also 'instantiate.hpp').
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Triggered at the end of a variable elimination round ('elim_round').
|
||||
|
||||
void
|
||||
Internal::collect_instantiation_candidates (Instantiator & instantiator) {
|
||||
assert (occurring ());
|
||||
for (auto idx : vars) {
|
||||
if (frozen (idx)) continue;
|
||||
if (!active (idx)) continue;
|
||||
if (flags (idx).elim) continue; // BVE attempt pending
|
||||
for (int sign = -1; sign <= 1; sign += 2) {
|
||||
const int lit = sign * idx;
|
||||
if (noccs (lit) > opts.instantiateocclim) continue;
|
||||
Occs & os = occs (lit);
|
||||
for (const auto & c : os) {
|
||||
if (c->garbage) continue;
|
||||
if (opts.instantiateonce && c->instantiated) continue;
|
||||
if (c->size < opts.instantiateclslim) continue;
|
||||
bool satisfied = false;
|
||||
int unassigned = 0;
|
||||
for (const auto & other : *c) {
|
||||
const signed char tmp = val (other);
|
||||
if (tmp > 0) satisfied = true;
|
||||
if (!tmp) unassigned++;
|
||||
}
|
||||
if (satisfied) continue;
|
||||
if (unassigned < 3) continue; // avoid learning units
|
||||
size_t negoccs = occs (-lit).size ();
|
||||
LOG (c,
|
||||
"instantiation candidate literal %d "
|
||||
"with %zu negative occurrences in", lit, negoccs);
|
||||
instantiator.candidate (lit, c, c->size, negoccs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Specialized propagation and assignment routines for instantiation.
|
||||
|
||||
inline void Internal::inst_assign (int lit) {
|
||||
LOG ("instantiate assign %d", lit);
|
||||
assert (!val (lit));
|
||||
vals[lit] = 1;
|
||||
vals[-lit] = -1;
|
||||
trail.push_back (lit);
|
||||
}
|
||||
|
||||
bool Internal::inst_propagate () { // Adapted from 'propagate'.
|
||||
START (propagate);
|
||||
int64_t before = propagated;
|
||||
bool ok = true;
|
||||
while (ok && propagated != trail.size ()) {
|
||||
const int lit = -trail[propagated++];
|
||||
LOG ("instantiate propagating %d", -lit);
|
||||
Watches & ws = watches (lit);
|
||||
const const_watch_iterator eow = ws.end ();
|
||||
const_watch_iterator i = ws.begin ();
|
||||
watch_iterator j = ws.begin ();
|
||||
while (i != eow) {
|
||||
const Watch w = *j++ = *i++;
|
||||
const signed char b = val (w.blit);
|
||||
if (b > 0) continue;
|
||||
if (w.binary ()) {
|
||||
if (b < 0) { ok = false; LOG (w.clause, "conflict"); break; }
|
||||
else inst_assign (w.blit);
|
||||
} else {
|
||||
literal_iterator lits = w.clause->begin ();
|
||||
const int other = lits[0]^lits[1]^lit;
|
||||
lits[0] = other, lits[1] = lit;
|
||||
const signed char u = val (other);
|
||||
if (u > 0) j[-1].blit = other;
|
||||
else {
|
||||
const int size = w.clause->size;
|
||||
const const_literal_iterator end = lits + size;
|
||||
const literal_iterator middle = lits + w.clause->pos;
|
||||
literal_iterator k = middle;
|
||||
signed char v = -1;
|
||||
int r = 0;
|
||||
while (k != end && (v = val (r = *k)) < 0)
|
||||
k++;
|
||||
if (v < 0) {
|
||||
k = lits + 2;
|
||||
assert (w.clause->pos <= size);
|
||||
while (k != middle && (v = val (r = *k)) < 0)
|
||||
k++;
|
||||
}
|
||||
w.clause->pos = k - lits;
|
||||
assert (lits + 2 <= k), assert (k <= w.clause->end ());
|
||||
if (v > 0) {
|
||||
j[-1].blit = r;
|
||||
} else if (!v) {
|
||||
LOG (w.clause, "unwatch %d in", r);
|
||||
lits[1] = r;
|
||||
*k = lit;
|
||||
watch_literal (r, lit, w.clause);
|
||||
j--;
|
||||
} else if (!u) {
|
||||
assert (v < 0);
|
||||
inst_assign (other);
|
||||
} else {
|
||||
assert (u < 0);
|
||||
assert (v < 0);
|
||||
LOG (w.clause, "conflict");
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j != i) {
|
||||
while (i != eow)
|
||||
*j++ = *i++;
|
||||
ws.resize (j - ws.begin ());
|
||||
}
|
||||
}
|
||||
int64_t delta = propagated - before;
|
||||
stats.propagations.instantiate += delta;
|
||||
STOP (propagate);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is the actual instantiation attempt.
|
||||
|
||||
bool Internal::instantiate_candidate (int lit, Clause * c) {
|
||||
stats.instried++;
|
||||
if (c->garbage) return false;
|
||||
assert (!level);
|
||||
bool found = false, satisfied = false, inactive = false;
|
||||
int unassigned = 0;
|
||||
for (const auto & other : *c) {
|
||||
if (other == lit) found = true;
|
||||
const signed char tmp = val (other);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (!tmp && !active (other)) { inactive = true; break; }
|
||||
if (!tmp) unassigned++;
|
||||
}
|
||||
if (!found) return false;
|
||||
if (inactive) return false;
|
||||
if (satisfied) return false;
|
||||
if (unassigned < 3) return false;
|
||||
size_t before = trail.size ();
|
||||
assert (propagated == before);
|
||||
assert (active (lit));
|
||||
LOG (c, "trying to instantiate %d in", lit);
|
||||
assert (!c->garbage);
|
||||
c->instantiated = true;
|
||||
level++;
|
||||
inst_assign (lit); // Assume 'lit' to true.
|
||||
for (const auto & other : *c) {
|
||||
if (other == lit) continue;
|
||||
const signed char tmp = val (other);
|
||||
if (tmp) { assert (tmp < 0); continue; }
|
||||
inst_assign (-other); // Assume other to false.
|
||||
}
|
||||
bool ok = inst_propagate (); // Propagate.
|
||||
while (trail.size () > before) { // Backtrack.
|
||||
const int other = trail.back ();
|
||||
LOG ("instantiate unassign %d", other);
|
||||
trail.pop_back ();
|
||||
assert (val (other) > 0);
|
||||
vals[other] = vals[-other] = 0;
|
||||
}
|
||||
propagated = before;
|
||||
assert (level == 1);
|
||||
level = 0;
|
||||
if (ok) { LOG ("instantiation failed"); return false; }
|
||||
unwatch_clause (c);
|
||||
strengthen_clause (c, lit);
|
||||
watch_clause (c);
|
||||
assert (c->size > 1);
|
||||
LOG ("instantiation succeeded");
|
||||
stats.instantiated++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Try to instantiate all candidates collected before through the
|
||||
// 'collect_instantiation_candidates' routine.
|
||||
|
||||
void Internal::instantiate (Instantiator & instantiator) {
|
||||
assert (opts.instantiate);
|
||||
START (instantiate);
|
||||
stats.instrounds++;
|
||||
#ifndef QUIET
|
||||
const int64_t candidates = instantiator.candidates.size ();
|
||||
#endif
|
||||
int64_t instantiated = 0, tried = 0;
|
||||
init_watches ();
|
||||
connect_watches ();
|
||||
if (propagated < trail.size ()) {
|
||||
if (!propagate ()) {
|
||||
LOG ("propagation after connecting watches failed");
|
||||
learn_empty_clause ();
|
||||
assert (unsat);
|
||||
}
|
||||
}
|
||||
PHASE ("instantiate", stats.instrounds,
|
||||
"attempting to instantiate %" PRId64 " candidate literal clause pairs",
|
||||
candidates);
|
||||
while (!unsat &&
|
||||
!terminated_asynchronously () &&
|
||||
!instantiator.candidates.empty ()) {
|
||||
Instantiator::Candidate cand = instantiator.candidates.back ();
|
||||
instantiator.candidates.pop_back ();
|
||||
tried++;
|
||||
if (!active (cand.lit)) continue;
|
||||
LOG (cand.clause,
|
||||
"trying to instantiate %d with "
|
||||
"%zd negative occurrences in", cand.lit, cand.negoccs);
|
||||
if (!instantiate_candidate (cand.lit, cand.clause)) continue;
|
||||
instantiated++;
|
||||
VERBOSE (2, "instantiation %" PRId64 " (%.1f%%) succeeded "
|
||||
"(%.1f%%) with %zd negative occurrences in size %d clause",
|
||||
tried, percent (tried, candidates),
|
||||
percent (instantiated, tried), cand.negoccs, cand.size);
|
||||
}
|
||||
PHASE ("instantiate", stats.instrounds,
|
||||
"instantiated %" PRId64 " candidate successfully "
|
||||
"out of %" PRId64 " tried %.1f%%",
|
||||
instantiated, tried, percent (instantiated, tried));
|
||||
report ('I', !instantiated);
|
||||
reset_watches ();
|
||||
STOP (instantiate);
|
||||
}
|
||||
|
||||
}
|
47
hCaD_V2/src/instantiate.hpp
Normal file
47
hCaD_V2/src/instantiate.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef _instantiate_hpp_INCLUDED
|
||||
#define _instantiate_hpp_INCLUDED
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// We are trying to remove literals in clauses, which occur in few clauses
|
||||
// and further restrict this removal to variables for which variable
|
||||
// elimination failed. Thus if for instance we succeed in removing the
|
||||
// single occurrence of a literal, pure literal elimination can
|
||||
// eliminate the corresponding variable in the next variable elimination
|
||||
// round. The set of such literal clause candidate pairs is collected at
|
||||
// the end of a variable elimination round and tried before returning. The
|
||||
// name of this technique is inspired by 'variable instantiation' as
|
||||
// described in [AnderssonBjesseCookHanna-DAC'02] and apparently
|
||||
// successfully used in the 'Oepir' SAT solver.
|
||||
|
||||
struct Clause;
|
||||
struct Internal;
|
||||
|
||||
class Instantiator {
|
||||
|
||||
friend struct Internal;
|
||||
|
||||
struct Candidate {
|
||||
int lit;
|
||||
int size;
|
||||
size_t negoccs;
|
||||
Clause * clause;
|
||||
Candidate (int l, Clause * c, int s, size_t n) :
|
||||
lit (l), size (s), negoccs (n), clause (c)
|
||||
{ }
|
||||
};
|
||||
|
||||
vector<Candidate> candidates;
|
||||
|
||||
public:
|
||||
|
||||
void candidate (int l, Clause * c, int s, size_t n) {
|
||||
candidates.push_back (Candidate (l, c, s, n));
|
||||
}
|
||||
|
||||
operator bool () const { return !candidates.empty (); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
739
hCaD_V2/src/internal.cpp
Normal file
739
hCaD_V2/src/internal.cpp
Normal file
@ -0,0 +1,739 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
Internal::Internal ()
|
||||
:
|
||||
mode (SEARCH),
|
||||
unsat (false),
|
||||
iterating (false),
|
||||
localsearching (false),
|
||||
lookingahead (false),
|
||||
preprocessing (false),
|
||||
protected_reasons (false),
|
||||
force_saved_phase (false),
|
||||
searching_lucky_phases (false),
|
||||
stable (false),
|
||||
reported (false),
|
||||
rephased (0),
|
||||
vsize (0),
|
||||
max_var (0),
|
||||
level (0),
|
||||
vals (0),
|
||||
score_inc (1.0),
|
||||
pol_sc_inc(1.0),
|
||||
scores (this),
|
||||
conflict (0),
|
||||
ignore (0),
|
||||
propagated (0),
|
||||
propagated2 (0),
|
||||
best_assigned (0),
|
||||
target_assigned (0),
|
||||
no_conflict_until (0),
|
||||
proof (0),
|
||||
checker (0),
|
||||
tracer (0),
|
||||
opts (this),
|
||||
#ifndef QUIET
|
||||
profiles (this),
|
||||
force_phase_messages (false),
|
||||
#endif
|
||||
arena (this),
|
||||
prefix ("c "),
|
||||
internal (this),
|
||||
external (0),
|
||||
termination_forced (false),
|
||||
vars (this->max_var),
|
||||
lits (this->max_var)
|
||||
{
|
||||
control.push_back (Level (0, 0));
|
||||
}
|
||||
|
||||
Internal::~Internal () {
|
||||
for (const auto & c : clauses)
|
||||
delete_clause (c);
|
||||
if (proof) delete proof;
|
||||
if (tracer) delete tracer;
|
||||
if (checker) delete checker;
|
||||
if (vals) { vals -= vsize; delete [] vals; }
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Values in 'vals' can be accessed in the range '[-max_var,max_var]' that
|
||||
// is directly by a literal. This is crucial for performance. By shifting
|
||||
// the start of 'vals' appropriately, we achieve that negative offsets from
|
||||
// the start of 'vals' can be used. We also need to set both values at
|
||||
// 'lit' and '-lit' during assignments. In MiniSAT integer literals are
|
||||
// encoded, using the least significant bit as negation. This avoids taking
|
||||
// the 'abs ()' (as in our solution) and thus also avoids a branch in the
|
||||
// hot-spot of the solver (clause traversal in propagation). That solution
|
||||
// requires another (branch less) negation of the values though and
|
||||
// debugging is harder since literals occur only encoded in clauses.
|
||||
// The main draw-back of our solution is that we have to shift the memory
|
||||
// and access it through negative indices, which looks less clean (but still
|
||||
// as far I can tell is properly defined C / C++). You might get a warning
|
||||
// by static analyzers though. Clang with '--analyze' thought that this
|
||||
// idiom would generate a memory leak thus we use the following dummy.
|
||||
|
||||
static signed char * ignore_clang_analyze_memory_leak_warning;
|
||||
|
||||
void Internal::enlarge_vals (size_t new_vsize) {
|
||||
signed char * new_vals;
|
||||
const size_t bytes = 2u * new_vsize;
|
||||
new_vals = new signed char [ bytes ]; // g++-4.8 does not like ... { 0 };
|
||||
memset (new_vals, 0, bytes);
|
||||
ignore_clang_analyze_memory_leak_warning = new_vals;
|
||||
new_vals += new_vsize;
|
||||
|
||||
if (vals) memcpy (new_vals - max_var, vals - max_var, 2u*max_var + 1u);
|
||||
vals -= vsize;
|
||||
delete [] vals;
|
||||
vals = new_vals;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
template<class T>
|
||||
static void enlarge_init (vector<T> & v, size_t N, const T & i) {
|
||||
while (v.size () < N)
|
||||
v.push_back (i);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void enlarge_only (vector<T> & v, size_t N) {
|
||||
while (v.size () < N)
|
||||
v.push_back (T ());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void enlarge_zero (vector<T> & v, size_t N) {
|
||||
enlarge_init (v, N, (const T &) 0);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::enlarge (int new_max_var) {
|
||||
assert (!level);
|
||||
size_t new_vsize = vsize ? 2*vsize : 1 + (size_t) new_max_var;
|
||||
while (new_vsize <= (size_t) new_max_var) new_vsize *= 2;
|
||||
LOG ("enlarge internal size from %zd to new size %zd", vsize, new_vsize);
|
||||
// Ordered in the size of allocated memory (larger block first).
|
||||
enlarge_only (wtab, 2*new_vsize);
|
||||
enlarge_only (vtab, new_vsize);
|
||||
enlarge_zero (parents, new_vsize);
|
||||
enlarge_only (links, new_vsize);
|
||||
enlarge_zero (btab, new_vsize);
|
||||
enlarge_zero (gtab, new_vsize);
|
||||
enlarge_zero (stab, new_vsize);
|
||||
enlarge_init (ptab, 2*new_vsize, -1);
|
||||
enlarge_only (ftab, new_vsize);
|
||||
enlarge_vals (new_vsize);
|
||||
enlarge_zero (frozentab, new_vsize);
|
||||
const signed char val = opts.phase ? 1 : -1;
|
||||
enlarge_init (phases.saved, new_vsize, val);
|
||||
if(opts.psids) enlarge_zero (phases.act, 2*new_vsize);
|
||||
enlarge_zero (phases.forced, new_vsize);
|
||||
enlarge_zero (phases.target, new_vsize);
|
||||
enlarge_zero (phases.best, new_vsize);
|
||||
enlarge_zero (phases.prev, new_vsize);
|
||||
enlarge_zero (phases.min, new_vsize);
|
||||
enlarge_zero (marks, new_vsize);
|
||||
vsize = new_vsize;
|
||||
}
|
||||
|
||||
void Internal::init_vars (int new_max_var) {
|
||||
if (new_max_var <= max_var) return;
|
||||
if (level) backtrack ();
|
||||
LOG ("initializing %d internal variables from %d to %d",
|
||||
new_max_var - max_var, max_var + 1, new_max_var);
|
||||
if ((size_t) new_max_var >= vsize) enlarge (new_max_var);
|
||||
#ifndef NDEBUG
|
||||
for (int64_t i = -new_max_var; i < -max_var; i++) assert (!vals[i]);
|
||||
for (unsigned i = max_var + 1; i <= (unsigned) new_max_var; i++)
|
||||
assert (!vals[i]), assert (!btab[i]), assert (!gtab[i]);
|
||||
for (uint64_t i = 2*((uint64_t)max_var + 1);
|
||||
i <= 2*(uint64_t)new_max_var + 1;
|
||||
i++)
|
||||
assert (ptab[i] == -1);
|
||||
#endif
|
||||
assert (!btab[0]);
|
||||
int old_max_var = max_var;
|
||||
max_var = new_max_var;
|
||||
init_queue (old_max_var, new_max_var);
|
||||
init_scores (old_max_var, new_max_var);
|
||||
int initialized = new_max_var - old_max_var;
|
||||
stats.vars += initialized;
|
||||
stats.unused += initialized;
|
||||
stats.inactive += initialized;
|
||||
LOG ("finished initializing %d internal variables", initialized);
|
||||
}
|
||||
|
||||
void Internal::add_original_lit (int lit) {
|
||||
assert (abs (lit) <= max_var);
|
||||
if (lit) {
|
||||
original.push_back (lit);
|
||||
} else {
|
||||
if (proof) proof->add_original_clause (original);
|
||||
add_new_original_clause ();
|
||||
original.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// This is the main CDCL loop with interleaved inprocessing.
|
||||
|
||||
int Internal::cdcl_loop_with_inprocessing () {
|
||||
|
||||
int res = 0;
|
||||
|
||||
START (search);
|
||||
|
||||
if (stable) { START (stable); report ('['); }
|
||||
else { START (unstable); report ('{'); }
|
||||
|
||||
while (!res) {
|
||||
if (unsat) res = 20;
|
||||
else if (!propagate ()) analyze (); // propagate and analyze
|
||||
else if (iterating) iterate (); // report learned unit
|
||||
else if (satisfied ()) res = 10; // found model
|
||||
else if (search_limits_hit ()) break; // decision or conflict limit
|
||||
else if (terminated_asynchronously ()) // externally terminated
|
||||
break;
|
||||
else if (restarting ()) restart (); // restart by backtracking
|
||||
else if (rephasing ()) rephase (); // reset variable phases
|
||||
else if (reducing ()) reduce (); // collect useless clauses
|
||||
else if (probing ()) probe (); // failed literal probing
|
||||
else if (subsuming ()) subsume (); // subsumption algorithm
|
||||
else if (eliminating ()) elim (); // variable elimination
|
||||
else if (compacting ()) compact (); // collect variables
|
||||
else if (conditioning ()) condition (); // globally blocked clauses
|
||||
else res = decide (); // next decision
|
||||
}
|
||||
|
||||
if (stable) { STOP (stable); report (']'); }
|
||||
else { STOP (unstable); report ('}'); }
|
||||
|
||||
STOP (search);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Most of the limits are only initialized in the first 'solve' call and
|
||||
// increased as in a stand-alone non-incremental SAT call except for those
|
||||
// explicitly marked as being reset below.
|
||||
|
||||
void Internal::init_report_limits () {
|
||||
reported = false;
|
||||
lim.report = 0;
|
||||
}
|
||||
|
||||
void Internal::init_preprocessing_limits () {
|
||||
|
||||
const bool incremental = lim.initialized;
|
||||
if (incremental)
|
||||
LOG ("reinitializing preprocessing limits incrementally");
|
||||
else LOG ("initializing preprocessing limits and increments");
|
||||
|
||||
const char * mode = 0;
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (incremental) mode = "keeping";
|
||||
else {
|
||||
lim.subsume = stats.conflicts + scale (opts.subsumeint);
|
||||
mode = "initial";
|
||||
}
|
||||
(void) mode;
|
||||
LOG ("%s subsume limit %" PRId64 " after %" PRId64 " conflicts",
|
||||
mode, lim.subsume, lim.subsume - stats.conflicts);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (incremental) mode = "keeping";
|
||||
else {
|
||||
last.elim.marked = -1;
|
||||
lim.elim = stats.conflicts + scale (opts.elimint);
|
||||
mode = "initial";
|
||||
}
|
||||
(void) mode;
|
||||
LOG ("%s elim limit %" PRId64 " after %" PRId64 " conflicts",
|
||||
mode, lim.elim, lim.elim - stats.conflicts);
|
||||
|
||||
// Initialize and reset elimination bounds in any case.
|
||||
|
||||
lim.elimbound = opts.elimboundmin;
|
||||
LOG ("elimination bound %" PRId64 "", lim.elimbound);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (!incremental) {
|
||||
|
||||
last.ternary.marked = -1; // TODO explain why this is necessary.
|
||||
|
||||
lim.compact = stats.conflicts + opts.compactint;
|
||||
LOG ("initial compact limit %" PRId64 " increment %" PRId64 "",
|
||||
lim.compact, lim.compact - stats.conflicts);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (incremental) mode = "keeping";
|
||||
else {
|
||||
lim.probe = stats.conflicts + opts.probeint;
|
||||
mode = "initial";
|
||||
}
|
||||
(void) mode;
|
||||
LOG ("%s probe limit %" PRId64 " after %" PRId64 " conflicts",
|
||||
mode, lim.probe, lim.probe - stats.conflicts);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (incremental) mode = "keeping";
|
||||
else {
|
||||
lim.condition = stats.conflicts + opts.conditionint;
|
||||
mode = "initial";
|
||||
}
|
||||
LOG ("%s condition limit %" PRId64 " increment %" PRId64,
|
||||
mode, lim.condition, lim.condition - stats.conflicts);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Initial preprocessing rounds.
|
||||
|
||||
if (inc.preprocessing <= 0) {
|
||||
lim.preprocessing = 0;
|
||||
LOG ("no preprocessing");
|
||||
} else {
|
||||
lim.preprocessing = inc.preprocessing;
|
||||
LOG ("limiting to %" PRId64 " preprocessing rounds", lim.preprocessing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Internal::init_search_limits () {
|
||||
|
||||
const bool incremental = lim.initialized;
|
||||
if (incremental) LOG ("reinitializing search limits incrementally");
|
||||
else LOG ("initializing search limits and increments");
|
||||
|
||||
const char * mode = 0;
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (incremental) mode = "keeping";
|
||||
else {
|
||||
last.reduce.conflicts = -1;
|
||||
lim.reduce = stats.conflicts + opts.reduceint;
|
||||
mode = "initial";
|
||||
}
|
||||
(void) mode;
|
||||
LOG ("%s reduce limit %" PRId64 " after %" PRId64 " conflicts",
|
||||
mode, lim.reduce, lim.reduce - stats.conflicts);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (incremental) mode = "keeping";
|
||||
else {
|
||||
lim.flush = opts.flushint;
|
||||
inc.flush = opts.flushint;
|
||||
mode = "initial";
|
||||
}
|
||||
(void) mode;
|
||||
LOG ("%s flush limit %" PRId64 " interval %" PRId64 "",
|
||||
mode, lim.flush, inc.flush);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Initialize or reset 'rephase' limits in any case.
|
||||
|
||||
lim.rephase = stats.conflicts + opts.rephaseint;
|
||||
lim.rephased[0] = lim.rephased[1] = 0;
|
||||
LOG ("new rephase limit %" PRId64 " after %" PRId64 " conflicts",
|
||||
lim.rephase, lim.rephase - stats.conflicts);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Initialize or reset 'restart' limits in any case.
|
||||
|
||||
lim.restart = stats.conflicts + opts.restartint;
|
||||
LOG ("new restart limit %" PRId64 " increment %" PRId64 "",
|
||||
lim.restart, lim.restart - stats.conflicts);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
if (!incremental) {
|
||||
stable = opts.stabilize && opts.stabilizeonly;
|
||||
if (stable) LOG ("starting in always forced stable phase");
|
||||
else LOG ("starting in default non-stable phase");
|
||||
init_averages ();
|
||||
} else if (opts.stabilize && opts.stabilizeonly) {
|
||||
LOG ("keeping always forced stable phase");
|
||||
assert (stable);
|
||||
} else if (stable) {
|
||||
LOG ("switching back to default non-stable phase");
|
||||
stable = false;
|
||||
swap_averages ();
|
||||
} else LOG ("keeping non-stable phase");
|
||||
|
||||
inc.stabilize = opts.stabilizeint;
|
||||
lim.stabilize = stats.conflicts + inc.stabilize;
|
||||
LOG ("new stabilize limit %" PRId64 " after %" PRId64 " conflicts",
|
||||
lim.stabilize, inc.stabilize);
|
||||
|
||||
if (opts.stabilize && opts.reluctant) {
|
||||
LOG ("new restart reluctant doubling sequence period %d",
|
||||
opts.reluctant);
|
||||
reluctant.enable (opts.reluctant, opts.reluctantmax);
|
||||
} else reluctant.disable ();
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Conflict and decision limits.
|
||||
|
||||
if (inc.conflicts < 0) {
|
||||
lim.conflicts = -1;
|
||||
LOG ("no limit on conflicts");
|
||||
} else {
|
||||
lim.conflicts = stats.conflicts + inc.conflicts;
|
||||
LOG ("conflict limit after %" PRId64 " conflicts at %" PRId64 " conflicts",
|
||||
inc.conflicts, lim.conflicts);
|
||||
}
|
||||
|
||||
if (inc.decisions < 0) {
|
||||
lim.decisions = -1;
|
||||
LOG ("no limit on decisions");
|
||||
} else {
|
||||
lim.decisions = stats.decisions + inc.decisions;
|
||||
LOG ("conflict limit after %" PRId64 " decisions at %" PRId64 " decisions",
|
||||
inc.decisions, lim.decisions);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
// Initial preprocessing rounds.
|
||||
|
||||
if (inc.localsearch <= 0) {
|
||||
lim.localsearch = 0;
|
||||
LOG ("no local search");
|
||||
} else {
|
||||
lim.localsearch = inc.localsearch;
|
||||
LOG ("limiting to %" PRId64 " local search rounds", lim.localsearch);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
lim.initialized = true;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::preprocess_round (int round) {
|
||||
(void) round;
|
||||
if (unsat) return false;
|
||||
if (!max_var) return false;
|
||||
START (preprocess);
|
||||
struct { int64_t vars, clauses; } before, after;
|
||||
before.vars = active ();
|
||||
before.clauses = stats.current.irredundant;
|
||||
stats.preprocessings++;
|
||||
assert (!preprocessing);
|
||||
preprocessing = true;
|
||||
PHASE ("preprocessing", stats.preprocessings,
|
||||
"starting round %d with %" PRId64 " variables and %" PRId64 " clauses",
|
||||
round, before.vars, before.clauses);
|
||||
int old_elimbound = lim.elimbound;
|
||||
if (opts.probe) probe (false);
|
||||
if (opts.elim) elim (false);
|
||||
if (opts.condition) condition (false);
|
||||
after.vars = active ();
|
||||
after.clauses = stats.current.irredundant;
|
||||
assert (preprocessing);
|
||||
preprocessing = false;
|
||||
PHASE ("preprocessing", stats.preprocessings,
|
||||
"finished round %d with %" PRId64 " variables and %" PRId64 " clauses",
|
||||
round, after.vars, after.clauses);
|
||||
STOP (preprocess);
|
||||
report ('P');
|
||||
if (unsat) return false;
|
||||
if (after.vars < before.vars) return true;
|
||||
if (old_elimbound < lim.elimbound) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int Internal::preprocess () {
|
||||
for (int i = 0; i < lim.preprocessing; i++)
|
||||
if (!preprocess_round (i))
|
||||
break;
|
||||
if (unsat) return 20;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int Internal::try_to_satisfy_formula_by_saved_phases () {
|
||||
LOG ("satisfying formula by saved phases");
|
||||
assert (!level);
|
||||
assert (!force_saved_phase);
|
||||
assert (propagated == trail.size ());
|
||||
force_saved_phase = true;
|
||||
int res = 0;
|
||||
while (!res) {
|
||||
if (satisfied ()) {
|
||||
LOG ("formula indeed satisfied by saved phases");
|
||||
res = 10;
|
||||
} else if (decide ()) {
|
||||
LOG ("inconsistent assumptions with redundant clauses and phases");
|
||||
res = 20;
|
||||
} else if (!propagate ()) {
|
||||
LOG ("saved phases do not satisfy redundant clauses");
|
||||
assert (level > 0);
|
||||
backtrack ();
|
||||
conflict = 0; // ignore conflict
|
||||
assert (!res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert (force_saved_phase);
|
||||
force_saved_phase = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::produce_failed_assumptions () {
|
||||
LOG ("producing failed assumptions");
|
||||
assert (!level);
|
||||
assert (!assumptions.empty ());
|
||||
while (!unsat) {
|
||||
assert (!satisfied ());
|
||||
if (decide ()) break;
|
||||
while (!unsat && !propagate ())
|
||||
analyze ();
|
||||
}
|
||||
if (unsat) LOG ("formula is actually unsatisfiable unconditionally");
|
||||
else LOG ("assumptions indeed failing");
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int Internal::local_search_round (int round) {
|
||||
|
||||
assert (round > 0);
|
||||
|
||||
if (unsat) return false;
|
||||
if (!max_var) return false;
|
||||
|
||||
START_OUTER_WALK ();
|
||||
assert (!localsearching);
|
||||
localsearching = true;
|
||||
|
||||
// Determine propagation limit quadratically scaled with rounds.
|
||||
//
|
||||
int64_t limit = opts.walkmineff;
|
||||
limit *= round;
|
||||
if (LONG_MAX / round > limit) limit *= round;
|
||||
else limit = LONG_MAX;
|
||||
|
||||
int res = walk_round (limit, true);
|
||||
|
||||
assert (localsearching);
|
||||
localsearching = false;
|
||||
STOP_OUTER_WALK ();
|
||||
|
||||
report ('L');
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int Internal::local_search () {
|
||||
|
||||
if (unsat) return 0;
|
||||
if (!max_var) return 0;
|
||||
if (!opts.walk) return 0;
|
||||
|
||||
int res = 0;
|
||||
|
||||
for (int i = 1; !res && i <= lim.localsearch; i++)
|
||||
res = local_search_round (i);
|
||||
|
||||
if (res == 10) {
|
||||
LOG ("local search determined formula to be satisfiable");
|
||||
assert (!stats.walk.minimum);
|
||||
res = try_to_satisfy_formula_by_saved_phases ();
|
||||
} else if (res == 20) {
|
||||
LOG ("local search determined assumptions to be inconsistent");
|
||||
assert (!assumptions.empty ());
|
||||
produce_failed_assumptions ();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int Internal::solve (bool preprocess_only) {
|
||||
assert (clause.empty ());
|
||||
START (solve);
|
||||
if (preprocess_only) LOG ("internal solving in preprocessing only mode");
|
||||
else LOG ("internal solving in full mode");
|
||||
init_report_limits ();
|
||||
int res = already_solved ();
|
||||
if (!res) res = restore_clauses ();
|
||||
if (!res) {
|
||||
init_preprocessing_limits ();
|
||||
if (!preprocess_only) init_search_limits ();
|
||||
}
|
||||
if (!res) res = preprocess ();
|
||||
if (!preprocess_only) {
|
||||
if (!res) res = local_search ();
|
||||
if (!res) res = lucky_phases ();
|
||||
if (!res) res = cdcl_loop_with_inprocessing ();
|
||||
}
|
||||
reset_solving ();
|
||||
report_solving (res);
|
||||
STOP (solve);
|
||||
return res;
|
||||
}
|
||||
|
||||
int Internal::already_solved () {
|
||||
int res = 0;
|
||||
if (unsat) {
|
||||
LOG ("already inconsistent");
|
||||
res = 20;
|
||||
} else {
|
||||
if (level) backtrack ();
|
||||
if (!propagate ()) {
|
||||
LOG ("root level propagation produces conflict");
|
||||
learn_empty_clause ();
|
||||
res = 20;
|
||||
}
|
||||
if(max_var == 0 && res == 0)
|
||||
res = 10;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
void Internal::report_solving (int res) {
|
||||
if (res == 10) report ('1');
|
||||
else if (res == 20) report ('0');
|
||||
else report ('?');
|
||||
}
|
||||
|
||||
void Internal::reset_solving () {
|
||||
if (termination_forced) {
|
||||
|
||||
// TODO this leads potentially to a data race if the external
|
||||
// user is calling 'terminate' twice within one 'solve' call.
|
||||
// A proper solution would be to guard / protect setting the
|
||||
// 'termination_forced' flag and only allow it during solving and
|
||||
// ignore it otherwise thus also the second time it is called during a
|
||||
// 'solve' call. We could move resetting it also the start of
|
||||
// 'solve'.
|
||||
//
|
||||
termination_forced = false;
|
||||
|
||||
LOG ("reset forced termination");
|
||||
}
|
||||
}
|
||||
|
||||
int Internal::restore_clauses () {
|
||||
int res = 0;
|
||||
if (opts.restoreall <= 1 &&
|
||||
external->tainted.empty ()) {
|
||||
LOG ("no tainted literals and nothing to restore");
|
||||
report ('*');
|
||||
} else {
|
||||
report ('+');
|
||||
external->restore_clauses ();
|
||||
internal->report ('r');
|
||||
if (!unsat && !propagate ()) {
|
||||
LOG ("root level propagation after restore produces conflict");
|
||||
learn_empty_clause ();
|
||||
res = 20;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int Internal::lookahead () {
|
||||
assert (clause.empty ());
|
||||
START (lookahead);
|
||||
assert (!lookingahead);
|
||||
lookingahead = true;
|
||||
int tmp = already_solved ();
|
||||
if (!tmp) tmp = restore_clauses ();
|
||||
int res = 0;
|
||||
if (!tmp) res = lookahead_probing ();
|
||||
if (res == INT_MIN) res = 0;
|
||||
reset_solving ();
|
||||
report_solving (tmp);
|
||||
assert (lookingahead);
|
||||
lookingahead = false;
|
||||
STOP(lookahead);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::print_statistics () {
|
||||
stats.print (this);
|
||||
if (checker) checker->print_stats ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Only useful for debugging purposes.
|
||||
|
||||
void Internal::dump (Clause * c) {
|
||||
for (const auto & lit : *c)
|
||||
printf ("%d ", lit);
|
||||
printf ("0\n");
|
||||
}
|
||||
|
||||
void Internal::dump () {
|
||||
int64_t m = assumptions.size ();
|
||||
for (auto idx : vars)
|
||||
if (fixed (idx)) m++;
|
||||
for (const auto & c : clauses)
|
||||
if (!c->garbage) m++;
|
||||
printf ("p cnf %d %" PRId64 "\n", max_var, m);
|
||||
for (auto idx : vars) {
|
||||
const int tmp = fixed (idx);
|
||||
if (tmp) printf ("%d 0\n", tmp < 0 ? -idx : idx);
|
||||
}
|
||||
for (const auto & c : clauses)
|
||||
if (!c->garbage) dump (c);
|
||||
for (const auto & lit : assumptions)
|
||||
printf ("%d 0\n", lit);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
bool Internal::traverse_clauses (ClauseIterator & it) {
|
||||
vector<int> eclause;
|
||||
if (unsat) return it.clause (eclause);
|
||||
for (const auto & c : clauses) {
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
bool satisfied = false;
|
||||
for (const auto & ilit : *c) {
|
||||
const int tmp = fixed (ilit);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (tmp < 0) continue;
|
||||
const int elit = externalize (ilit);
|
||||
eclause.push_back (elit);
|
||||
}
|
||||
if (!satisfied && !it.clause (eclause))
|
||||
return false;
|
||||
eclause.clear ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
1351
hCaD_V2/src/internal.hpp
Normal file
1351
hCaD_V2/src/internal.hpp
Normal file
File diff suppressed because it is too large
Load Diff
48
hCaD_V2/src/ipasir.cpp
Normal file
48
hCaD_V2/src/ipasir.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "ipasir.h"
|
||||
#include "ccadical.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
const char * ipasir_signature () {
|
||||
return ccadical_signature ();
|
||||
}
|
||||
|
||||
void * ipasir_init () {
|
||||
return ccadical_init (); }
|
||||
|
||||
void ipasir_release (void * solver) {
|
||||
ccadical_release ((CCaDiCaL*) solver);
|
||||
}
|
||||
|
||||
void ipasir_add (void * solver, int lit) {
|
||||
ccadical_add ((CCaDiCaL *) solver, lit);
|
||||
}
|
||||
|
||||
void ipasir_assume (void * solver, int lit) {
|
||||
ccadical_assume ((CCaDiCaL *) solver, lit);
|
||||
}
|
||||
|
||||
int ipasir_solve (void * solver) {
|
||||
return ccadical_solve ((CCaDiCaL *) solver);
|
||||
}
|
||||
|
||||
int ipasir_val (void * solver, int lit) {
|
||||
return ccadical_val ((CCaDiCaL *) solver, lit);
|
||||
}
|
||||
|
||||
int ipasir_failed (void * solver, int lit) {
|
||||
return ccadical_failed ((CCaDiCaL *) solver, lit);
|
||||
}
|
||||
|
||||
void ipasir_set_terminate (void * solver,
|
||||
void * state, int (*terminate)(void * state)) {
|
||||
ccadical_set_terminate ((CCaDiCaL *) solver, state, terminate);
|
||||
}
|
||||
|
||||
void ipasir_set_learn (void * solver,
|
||||
void * state, int max_length,
|
||||
void (*learn)(void * state, int * clause)) {
|
||||
ccadical_set_learn ((CCaDiCaL*) solver, state, max_length, learn);
|
||||
}
|
||||
|
||||
}
|
38
hCaD_V2/src/ipasir.h
Normal file
38
hCaD_V2/src/ipasir.h
Normal file
@ -0,0 +1,38 @@
|
||||
#ifndef _ipasir_h_INCLUDED
|
||||
#define _ipasir_h_INCLUDED
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Here are the declarations for the actual IPASIR functions, which is the
|
||||
// generic incremental reentrant SAT solver API used for instance in the SAT
|
||||
// competition. The other 'C' API in 'ccadical.h' is (more) type safe and
|
||||
// has additional functions only supported by the CaDiCaL library. Please
|
||||
// also refer to our SAT Race 2015 article in the Journal of AI from 2016.
|
||||
|
||||
const char * ipasir_signature (void);
|
||||
void * ipasir_init (void);
|
||||
void ipasir_release (void * solver);
|
||||
void ipasir_add (void * solver, int lit);
|
||||
void ipasir_assume (void * solver, int lit);
|
||||
int ipasir_solve (void * solver);
|
||||
int ipasir_val (void * solver, int lit);
|
||||
int ipasir_failed (void * solver, int lit);
|
||||
|
||||
void ipasir_set_terminate (void * solver,
|
||||
void * state, int (*terminate)(void * state));
|
||||
|
||||
void ipasir_set_learn (void * solver,
|
||||
void * state, int max_length,
|
||||
void (*learn)(void * state, int * clause));
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
30
hCaD_V2/src/level.hpp
Normal file
30
hCaD_V2/src/level.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef _level_hpp_INCLUDED
|
||||
#define _level_hpp_INCLUDED
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// For each new decision we increase the decision level and push a 'Level'
|
||||
// on the 'control' stack. The information gathered here is used in
|
||||
// 'reuse_trail' and for early aborts in clause minimization.
|
||||
|
||||
struct Level {
|
||||
|
||||
int decision; // decision literal of this level
|
||||
int trail; // trail start of this level
|
||||
|
||||
struct {
|
||||
int count; // how many variables seen during 'analyze'
|
||||
int trail; // smallest trail position seen on this level
|
||||
} seen;
|
||||
|
||||
void reset () { seen.count = 0; seen.trail = INT_MAX; }
|
||||
|
||||
Level (int d, int t) : decision (d), trail (t) { reset (); }
|
||||
Level () { }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
122
hCaD_V2/src/limit.cpp
Normal file
122
hCaD_V2/src/limit.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
Limit::Limit () {
|
||||
memset (this, 0, sizeof *this);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
double Internal::scale (double v) const {
|
||||
const double ratio = clause_variable_ratio ();
|
||||
const double factor = (ratio <= 2) ? 1.0 : log (ratio) / log (2);
|
||||
double res = factor * v;
|
||||
if (res < 1) res = 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
Last::Last () {
|
||||
memset (this, 0, sizeof *this);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
Inc::Inc () {
|
||||
memset (this, 0, sizeof *this);
|
||||
decisions = conflicts = -1; // unlimited
|
||||
}
|
||||
|
||||
void Internal::limit_terminate (int l) {
|
||||
if (l <= 0 && !lim.terminate.forced) {
|
||||
LOG ("keeping unbounded terminate limit");
|
||||
} else if (l <= 0) {
|
||||
LOG ("reset terminate limit to be unbounded");
|
||||
lim.terminate.forced = 0;
|
||||
} else {
|
||||
lim.terminate.forced = l;
|
||||
LOG ("new terminate limit of %d calls", l);
|
||||
}
|
||||
}
|
||||
|
||||
void Internal::limit_conflicts (int l) {
|
||||
if (l < 0 && inc.conflicts < 0) {
|
||||
LOG ("keeping unbounded conflict limit");
|
||||
} else if (l < 0) {
|
||||
LOG ("reset conflict limit to be unbounded");
|
||||
inc.conflicts = -1;
|
||||
} else {
|
||||
inc.conflicts = l;
|
||||
LOG ("new conflict limit of %d conflicts", l);
|
||||
}
|
||||
}
|
||||
|
||||
void Internal::limit_decisions (int l) {
|
||||
if (l < 0 && inc.decisions < 0) {
|
||||
LOG ("keeping unbounded decision limit");
|
||||
} else if (l < 0) {
|
||||
LOG ("reset decision limit to be unbounded");
|
||||
inc.decisions = -1;
|
||||
} else {
|
||||
inc.decisions = l;
|
||||
LOG ("new decision limit of %d decisions", l);
|
||||
}
|
||||
}
|
||||
|
||||
void Internal::limit_preprocessing (int l) {
|
||||
if (l < 0) {
|
||||
LOG ("ignoring invalid preprocessing limit %d", l);
|
||||
} else if (!l) {
|
||||
LOG ("reset preprocessing limit to no preprocessing");
|
||||
inc.preprocessing = 0;
|
||||
} else {
|
||||
inc.preprocessing = l;
|
||||
LOG ("new preprocessing limit of %d preprocessing rounds", l);
|
||||
}
|
||||
}
|
||||
|
||||
void Internal::limit_local_search (int l) {
|
||||
if (l < 0) {
|
||||
LOG ("ignoring invalid local search limit %d", l);
|
||||
} else if (!l) {
|
||||
LOG ("reset local search limit to no local search");
|
||||
inc.localsearch = 0;
|
||||
} else {
|
||||
inc.localsearch = l;
|
||||
LOG ("new local search limit of %d local search rounds", l);
|
||||
}
|
||||
}
|
||||
|
||||
bool Internal::is_valid_limit (const char * name) {
|
||||
if (!strcmp (name, "terminate")) return true;
|
||||
if (!strcmp (name, "conflicts")) return true;
|
||||
if (!strcmp (name, "decisions")) return true;
|
||||
if (!strcmp (name, "preprocessing")) return true;
|
||||
if (!strcmp (name, "localsearch")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Internal::limit (const char * name, int l) {
|
||||
bool res = true;
|
||||
if (!strcmp (name, "terminate")) limit_terminate (l);
|
||||
else if (!strcmp (name, "conflicts")) limit_conflicts (l);
|
||||
else if (!strcmp (name, "decisions")) limit_decisions (l);
|
||||
else if (!strcmp (name, "preprocessing")) limit_preprocessing (l);
|
||||
else if (!strcmp (name, "localsearch")) limit_local_search (l);
|
||||
else res = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
void Internal::reset_limits () {
|
||||
LOG ("reset limits");
|
||||
limit_terminate (0);
|
||||
limit_conflicts (-1);
|
||||
limit_decisions (-1);
|
||||
limit_preprocessing (0);
|
||||
limit_local_search (0);
|
||||
}
|
||||
|
||||
}
|
||||
|
68
hCaD_V2/src/limit.hpp
Normal file
68
hCaD_V2/src/limit.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef _limit_hpp_INCLUDED
|
||||
#define _limit_hpp_INCLUDED
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct Limit {
|
||||
|
||||
bool initialized;
|
||||
|
||||
int64_t conflicts; // conflict limit if non-negative
|
||||
int64_t decisions; // decision limit if non-negative
|
||||
int64_t preprocessing; // limit on preprocessing rounds
|
||||
int64_t localsearch; // limit on local search rounds
|
||||
|
||||
int64_t compact; // conflict limit for next 'compact'
|
||||
int64_t condition; // conflict limit for next 'condition'
|
||||
int64_t elim; // conflict limit for next 'elim'
|
||||
int64_t flush; // conflict limit for next 'flush'
|
||||
int64_t probe; // conflict limit for next 'probe'
|
||||
int64_t reduce; // conflict limit for next 'reduce'
|
||||
int64_t rephase; // conflict limit for next 'rephase'
|
||||
int64_t report; // report limit for header
|
||||
int64_t restart; // conflict limit for next 'restart'
|
||||
int64_t stabilize; // conflict limit for next 'stabilize'
|
||||
int64_t subsume; // conflict limit for next 'subsume'
|
||||
|
||||
int keptsize; // maximum kept size in 'reduce'
|
||||
int keptglue; // maximum kept glue in 'reduce'
|
||||
|
||||
// How often rephased during (1) or out (0) of stabilization.
|
||||
//
|
||||
int64_t rephased[2];
|
||||
|
||||
// Current elimination bound per eliminated variable.
|
||||
//
|
||||
int64_t elimbound;
|
||||
|
||||
struct {
|
||||
int check; // countdown to next terminator call
|
||||
int forced; // forced termination for testing
|
||||
} terminate;
|
||||
|
||||
Limit ();
|
||||
};
|
||||
|
||||
struct Last {
|
||||
struct { int64_t propagations; } transred, vivify;
|
||||
struct { int64_t fixed, subsumephases, marked; } elim;
|
||||
struct { int64_t propagations, reductions; } probe;
|
||||
struct { int64_t conflicts; } reduce, rephase;
|
||||
struct { int64_t marked; } ternary;
|
||||
struct { int64_t fixed; } collect;
|
||||
Last ();
|
||||
};
|
||||
|
||||
struct Inc {
|
||||
int64_t flush; // flushing interval in terms of conflicts
|
||||
int64_t stabilize; // stabilization interval increment
|
||||
int64_t conflicts; // next conflict limit if non-negative
|
||||
int64_t decisions; // next decision limit if non-negative
|
||||
int64_t preprocessing; // next preprocessing limit if non-negative
|
||||
int64_t localsearch; // next local search limit if non-negative
|
||||
Inc ();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
133
hCaD_V2/src/logging.cpp
Normal file
133
hCaD_V2/src/logging.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#ifdef LOGGING
|
||||
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
void Logger::print_log_prefix (Internal * internal) {
|
||||
internal->print_prefix ();
|
||||
tout.magenta ();
|
||||
fputs ("LOG ", stdout);
|
||||
tout.magenta (true);
|
||||
printf ("%d ", internal->level);
|
||||
tout.normal ();
|
||||
}
|
||||
|
||||
void Logger::log_empty_line (Internal * internal) {
|
||||
internal->print_prefix ();
|
||||
tout.magenta ();
|
||||
const int len = internal->prefix.size (), max = 78 - len;
|
||||
for (int i = 0; i < max; i++)
|
||||
fputc ('-', stdout);
|
||||
fputc ('\n', stdout);
|
||||
tout.normal ();
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
void Logger::log (Internal * internal, const char * fmt, ...) {
|
||||
print_log_prefix (internal);
|
||||
tout.magenta ();
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vprintf (fmt, ap);
|
||||
va_end (ap);
|
||||
fputc ('\n', stdout);
|
||||
tout.normal ();
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
// It is hard to factor out the common part between the two clause loggers,
|
||||
// since they are also used in slightly different contexts. Our attempt to
|
||||
// do so were not more readable than the current version. See the header
|
||||
// for an explanation of the difference between the following two functions.
|
||||
|
||||
void Logger::log (Internal * internal,
|
||||
const Clause * c, const char *fmt, ...) {
|
||||
print_log_prefix (internal);
|
||||
tout.magenta ();
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vprintf (fmt, ap);
|
||||
va_end (ap);
|
||||
if (c) {
|
||||
if (c->redundant) printf (" glue %d redundant", c->glue);
|
||||
else printf (" irredundant");
|
||||
printf (" size %d clause[%" PRId64 "]", c->size, c->id);
|
||||
if (c->moved) printf (" ... (moved)");
|
||||
else {
|
||||
if (internal->opts.logsort) {
|
||||
vector<int> s;
|
||||
for (const auto & lit : *c)
|
||||
s.push_back (lit);
|
||||
sort (s.begin (), s.end (), clause_lit_less_than ());
|
||||
for (const auto & lit : s)
|
||||
printf (" %d", lit);
|
||||
} else {
|
||||
for (const auto & lit : *c)
|
||||
printf (" %d", lit);
|
||||
}
|
||||
}
|
||||
} else if (internal->level) printf (" decision");
|
||||
else printf (" unit");
|
||||
fputc ('\n', stdout);
|
||||
tout.normal ();
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
// Same as above, but for the global clause 'c' (which is not a reason).
|
||||
|
||||
void Logger::log (Internal * internal,
|
||||
const vector<int> & c, const char *fmt, ...) {
|
||||
print_log_prefix (internal);
|
||||
tout.magenta ();
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vprintf (fmt, ap);
|
||||
va_end (ap);
|
||||
if (internal->opts.logsort) {
|
||||
vector<int> s;
|
||||
for (const auto & lit : c)
|
||||
s.push_back (lit);
|
||||
sort (s.begin (), s.end (), clause_lit_less_than ());
|
||||
for (const auto & lit : s)
|
||||
printf (" %d", lit);
|
||||
} else {
|
||||
for (const auto & lit : c)
|
||||
printf (" %d", lit);
|
||||
}
|
||||
fputc ('\n', stdout);
|
||||
tout.normal ();
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
// Now for 'restore_clause' to avoid copying (without logging).
|
||||
|
||||
void Logger::log (Internal * internal,
|
||||
const vector<int>::const_iterator & begin,
|
||||
const vector<int>::const_iterator & end,
|
||||
const char *fmt, ...) {
|
||||
print_log_prefix (internal);
|
||||
tout.magenta ();
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vprintf (fmt, ap);
|
||||
va_end (ap);
|
||||
if (internal->opts.logsort) {
|
||||
vector<int> s;
|
||||
for (auto p = begin; p != end; p++)
|
||||
s.push_back (*p);
|
||||
sort (s.begin (), s.end (), clause_lit_less_than ());
|
||||
for (const auto & lit : s)
|
||||
printf (" %d", lit);
|
||||
} else {
|
||||
for (auto p = begin; p != end; p++)
|
||||
printf (" %d", *p);
|
||||
}
|
||||
fputc ('\n', stdout);
|
||||
tout.normal ();
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
79
hCaD_V2/src/logging.hpp
Normal file
79
hCaD_V2/src/logging.hpp
Normal file
@ -0,0 +1,79 @@
|
||||
#ifndef _logging_hpp_INCLUDED
|
||||
#define _logging_hpp_INCLUDED
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifdef LOGGING
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// For debugging purposes and to help understanding what the solver is doing
|
||||
// there is a logging facility which is compiled in by './configure -l'. It
|
||||
// still has to be enabled at run-time though (again using the '-l' option
|
||||
// in the stand-alone solver). It produces quite a bit of information.
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct Clause;
|
||||
struct Internal;
|
||||
|
||||
struct Logger {
|
||||
|
||||
static void print_log_prefix (Internal *);
|
||||
|
||||
// Simple logging of a C-style format string.
|
||||
//
|
||||
static void log (Internal *, const char * fmt, ...)
|
||||
CADICAL_ATTRIBUTE_FORMAT (2, 3);
|
||||
|
||||
// Prints the format string (with its argument) and then the clause. The
|
||||
// clause can also be a zero pointer and then is interpreted as a decision
|
||||
// (current decision level > 0) or unit clause (zero decision level) and
|
||||
// printed accordingly.
|
||||
//
|
||||
static void log (Internal *, const Clause *, const char *fmt, ...)
|
||||
CADICAL_ATTRIBUTE_FORMAT (3, 4);
|
||||
|
||||
// Same as before, except that this is meant for the global 'clause' stack
|
||||
// used for new clauses (and not for reasons).
|
||||
//
|
||||
static void log (Internal *, const vector<int> &, const char *fmt, ...)
|
||||
CADICAL_ATTRIBUTE_FORMAT (3, 4);
|
||||
|
||||
// Another variant, to avoid copying (without logging).
|
||||
//
|
||||
static void log (Internal *,
|
||||
const vector<int>::const_iterator & begin,
|
||||
const vector<int>::const_iterator & end,
|
||||
const char *fmt, ...)
|
||||
CADICAL_ATTRIBUTE_FORMAT (4, 5);
|
||||
|
||||
static void log_empty_line (Internal *);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Make sure that 'logging' code is really not included (second case of the
|
||||
// '#ifdef') if logging code is not included.
|
||||
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
if (!internal->opts.log) break; \
|
||||
Logger::log (internal, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#else // end of 'then' part of 'ifdef LOGGING'
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#define LOG(...) do { } while (0)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#endif // end of 'else' part of 'ifdef LOGGING'
|
||||
/*------------------------------------------------------------------------*/
|
||||
#endif
|
504
hCaD_V2/src/lookahead.cpp
Normal file
504
hCaD_V2/src/lookahead.cpp
Normal file
@ -0,0 +1,504 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
struct literal_occ {
|
||||
int lit;
|
||||
int count;
|
||||
bool operator<(const literal_occ &locc) const {
|
||||
return (count > locc.count) || (count == locc.count && lit < locc.lit);
|
||||
|
||||
}
|
||||
literal_occ operator++() {
|
||||
++count;
|
||||
return *this;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::vector<int> Internal::lookahead_populate_locc() {
|
||||
std::vector<literal_occ> loccs((std::size_t)max_var+1);
|
||||
for (std::size_t lit = 0; lit < loccs.size(); ++lit) {
|
||||
loccs[lit].lit = lit;
|
||||
}
|
||||
for (const auto &c : clauses)
|
||||
if (!c->redundant)
|
||||
for (const auto &lit : *c)
|
||||
if (active(lit))
|
||||
++loccs[std::abs(lit)];
|
||||
std::sort(begin(loccs), end(loccs));
|
||||
std::vector<int> locc_map;
|
||||
locc_map.reserve(max_var);
|
||||
for (const auto &locc : loccs)
|
||||
locc_map.push_back(locc.lit);
|
||||
return locc_map;
|
||||
|
||||
}
|
||||
|
||||
int Internal::lookahead_locc(const std::vector<int> &loccs) {
|
||||
for (auto lit : loccs)
|
||||
if (active(abs(lit)) && !assumed(lit) && !assumed(-lit) && !val(lit))
|
||||
return lit;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
// This calculates the literal that appears the most often reusing the
|
||||
// available datastructures and iterating over the clause set. This is too
|
||||
// slow to be called iteratively. A faster (but inexact) version is
|
||||
// lookahead_populate_loc and lookahead_loc.
|
||||
int Internal::most_occurring_literal () {
|
||||
init_noccs ();
|
||||
for (const auto & c : clauses)
|
||||
if (!c->redundant)
|
||||
for (const auto & lit : *c)
|
||||
if (active (lit))
|
||||
noccs (lit)++;
|
||||
int64_t max_noccs = 0;
|
||||
int res = 0;
|
||||
|
||||
if(unsat)
|
||||
return INT_MIN;
|
||||
|
||||
propagate();
|
||||
for (int idx = 1; idx <= max_var; idx++) {
|
||||
if (!active (idx) || assumed(idx) || assumed(-idx) || val(idx)) continue;
|
||||
for (int sign = -1; sign <= 1; sign += 2) {
|
||||
const int lit = sign * idx;
|
||||
if (!active(lit))
|
||||
continue;
|
||||
int64_t tmp = noccs(lit);
|
||||
if (tmp <= max_noccs)
|
||||
continue;
|
||||
max_noccs = tmp;
|
||||
res = lit;
|
||||
}
|
||||
}
|
||||
MSG ("maximum occurrence %" PRId64 " of literal %d", max_noccs, res);
|
||||
reset_noccs ();
|
||||
return res;
|
||||
}
|
||||
|
||||
// We probe on literals first, which occur more often negated and thus we
|
||||
// sort the 'probes' stack in such a way that literals which occur negated
|
||||
// less frequently come first. Probes are taken from the back of the stack.
|
||||
|
||||
struct probe_negated_noccs_rank {
|
||||
Internal * internal;
|
||||
probe_negated_noccs_rank (Internal * i) : internal (i) { }
|
||||
typedef size_t Type;
|
||||
Type operator () (int a) const { return internal->noccs (-a); }
|
||||
};
|
||||
|
||||
// Follow the ideas in 'generate_probes' but flush non root probes and
|
||||
// reorder remaining probes.
|
||||
|
||||
void Internal::lookahead_flush_probes () {
|
||||
|
||||
assert (!probes.empty ());
|
||||
|
||||
init_noccs ();
|
||||
for (const auto & c : clauses) {
|
||||
int a, b;
|
||||
if (!is_binary_clause (c, a, b)) continue;
|
||||
noccs (a)++;
|
||||
noccs (b)++;
|
||||
}
|
||||
|
||||
const auto eop = probes.end ();
|
||||
auto j = probes.begin ();
|
||||
for (auto i = j; i != eop; i++) {
|
||||
int lit = *i;
|
||||
if (!active (lit)) continue;
|
||||
const bool have_pos_bin_occs = noccs (lit) > 0;
|
||||
const bool have_neg_bin_occs = noccs (-lit) > 0;
|
||||
if (have_pos_bin_occs == have_neg_bin_occs) continue;
|
||||
if (have_pos_bin_occs) lit = -lit;
|
||||
assert (!noccs (lit)), assert (noccs (-lit) > 0);
|
||||
if (propfixed (lit) >= stats.all.fixed) continue;
|
||||
MSG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit));
|
||||
*j++ = lit;
|
||||
}
|
||||
size_t remain = j - probes.begin ();
|
||||
#ifndef QUIET
|
||||
size_t flushed = probes.size () - remain;
|
||||
#endif
|
||||
probes.resize (remain);
|
||||
|
||||
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
|
||||
|
||||
reset_noccs ();
|
||||
shrink_vector (probes);
|
||||
|
||||
PHASE ("probe-round", stats.probingrounds,
|
||||
"flushed %zd literals %.0f%% remaining %zd",
|
||||
flushed, percent (flushed, remain + flushed), remain);
|
||||
}
|
||||
|
||||
void Internal::lookahead_generate_probes () {
|
||||
|
||||
assert (probes.empty ());
|
||||
|
||||
// First determine all the literals which occur in binary clauses. It is
|
||||
// way faster to go over the clauses once, instead of walking the watch
|
||||
// lists for each literal.
|
||||
//
|
||||
init_noccs ();
|
||||
for (const auto & c : clauses) {
|
||||
int a, b;
|
||||
if (!is_binary_clause (c, a, b)) continue;
|
||||
noccs (a)++;
|
||||
noccs (b)++;
|
||||
}
|
||||
|
||||
for (int idx = 1; idx <= max_var; idx++) {
|
||||
|
||||
// Then focus on roots of the binary implication graph, which are
|
||||
// literals occurring negatively in a binary clause, but not positively.
|
||||
// If neither 'idx' nor '-idx' is a root it makes less sense to probe
|
||||
// this variable.
|
||||
|
||||
// This argument requires that equivalent literal substitution through
|
||||
// 'decompose' is performed, because otherwise there might be 'cyclic
|
||||
// roots' which are not tried, i.e., -1 2 0, 1 -2 0, 1 2 3 0, 1 2 -3 0.
|
||||
|
||||
const bool have_pos_bin_occs = noccs (idx) > 0;
|
||||
const bool have_neg_bin_occs = noccs (-idx) > 0;
|
||||
|
||||
// if (have_pos_bin_occs == have_neg_bin_occs) continue;
|
||||
|
||||
if (have_pos_bin_occs) {
|
||||
int probe = -idx;
|
||||
|
||||
// See the discussion where 'propfixed' is used below.
|
||||
//
|
||||
if (propfixed (probe) >= stats.all.fixed) continue;
|
||||
|
||||
MSG ("scheduling probe %d negated occs %" PRId64 "", probe, noccs (-probe));
|
||||
probes.push_back (probe);
|
||||
}
|
||||
|
||||
if (have_neg_bin_occs) {
|
||||
int probe = idx;
|
||||
|
||||
// See the discussion where 'propfixed' is used below.
|
||||
//
|
||||
if (propfixed (probe) >= stats.all.fixed) continue;
|
||||
|
||||
MSG ("scheduling probe %d negated occs %" PRId64 "",
|
||||
probe, noccs (-probe));
|
||||
probes.push_back (probe);
|
||||
}
|
||||
}
|
||||
|
||||
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
|
||||
|
||||
reset_noccs ();
|
||||
shrink_vector (probes);
|
||||
|
||||
PHASE ("probe-round", stats.probingrounds,
|
||||
"scheduled %zd literals %.0f%%",
|
||||
probes.size (), percent (probes.size (), 2*max_var));
|
||||
}
|
||||
|
||||
int Internal::lookahead_next_probe () {
|
||||
|
||||
int generated = 0;
|
||||
|
||||
for (;;) {
|
||||
|
||||
if (probes.empty ()) {
|
||||
if (generated++) return 0;
|
||||
lookahead_generate_probes ();
|
||||
}
|
||||
|
||||
while (!probes.empty ()) {
|
||||
|
||||
int probe = probes.back ();
|
||||
probes.pop_back ();
|
||||
|
||||
// Eliminated or assigned.
|
||||
//
|
||||
if (!active (probe) || assumed(probe) || assumed (-probe)) continue;
|
||||
|
||||
// There is now new unit since the last time we propagated this probe,
|
||||
// thus we propagated it before without obtaining a conflict and
|
||||
// nothing changed since then. Thus there is no need to propagate it
|
||||
// again. This observation was independently made by Partik Simons
|
||||
// et.al. in the context of implementing 'smodels' (see for instance
|
||||
// Alg. 4 in his JAIR article from 2002) and it has also been
|
||||
// contributed to the thesis work of Yacine Boufkhad.
|
||||
//
|
||||
if (propfixed (probe) >= stats.all.fixed) continue;
|
||||
|
||||
return probe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool non_tautological_cube (std::vector<int> cube) {
|
||||
std::sort(begin(cube), end(cube), clause_lit_less_than ());
|
||||
|
||||
for(size_t i = 0, j = 1; j < cube.size(); ++i, ++j)
|
||||
if(cube[i] == cube[j])
|
||||
return false;
|
||||
else if (cube[i] == - cube[j])
|
||||
return false;
|
||||
else if (cube[i] == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool Internal::terminating_asked() {
|
||||
|
||||
if (external->terminator && external->terminator->terminate()) {
|
||||
MSG("connected terminator forces termination");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (termination_forced) {
|
||||
MSG("termination forced");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// We run probing on all literals with some differences:
|
||||
//
|
||||
// * no limit on the number of propagations. We rely on terminating to stop()
|
||||
// * we run only one round
|
||||
//
|
||||
// The run can be expensive, so we actually first run the cheaper
|
||||
// occurrence version and only then run lookahead.
|
||||
//
|
||||
int Internal::lookahead_probing() {
|
||||
|
||||
if (!active ())
|
||||
return 0;
|
||||
|
||||
MSG ("lookahead-probe-round %" PRId64
|
||||
" without propagations limit and %zu assumptions",
|
||||
stats.probingrounds, assumptions.size());
|
||||
|
||||
termination_forced = false;
|
||||
|
||||
#ifndef QUIET
|
||||
int old_failed = stats.failed;
|
||||
int64_t old_probed = stats.probed;
|
||||
#endif
|
||||
int64_t old_hbrs = stats.hbrs;
|
||||
|
||||
if (unsat) return INT_MIN;
|
||||
if (level) backtrack ();
|
||||
if (!propagate ()) {
|
||||
MSG ("empty clause before probing");
|
||||
learn_empty_clause ();
|
||||
return INT_MIN;
|
||||
}
|
||||
|
||||
if (terminating_asked())
|
||||
return most_occurring_literal();
|
||||
|
||||
decompose ();
|
||||
|
||||
if (ternary ()) // If we derived a binary clause
|
||||
decompose (); // then start another round of ELS.
|
||||
|
||||
// Remove duplicated binary clauses and perform in essence hyper unary
|
||||
// resolution, i.e., derive the unit '2' from '1 2' and '-1 2'.
|
||||
//
|
||||
mark_duplicated_binary_clauses_as_garbage ();
|
||||
|
||||
lim.conflicts = -1;
|
||||
|
||||
if (!probes.empty ()) lookahead_flush_probes ();
|
||||
|
||||
// We reset 'propfixed' since there was at least another conflict thus
|
||||
// a new learned clause, which might produce new propagations (and hyper
|
||||
// binary resolvents). During 'generate_probes' we keep the old value.
|
||||
//
|
||||
for (int idx = 1; idx <= max_var; idx++)
|
||||
propfixed (idx) = propfixed (-idx) = -1;
|
||||
|
||||
assert (unsat || propagated == trail.size ());
|
||||
propagated = propagated2 = trail.size ();
|
||||
|
||||
int probe;
|
||||
int res = most_occurring_literal();
|
||||
int max_hbrs = -1;
|
||||
|
||||
set_mode (PROBE);
|
||||
|
||||
MSG("unsat = %d, terminating_asked () = %d ", unsat, terminating_asked ());
|
||||
while (!unsat &&
|
||||
!terminating_asked () &&
|
||||
(probe = lookahead_next_probe ())) {
|
||||
stats.probed++;
|
||||
int hbrs;
|
||||
|
||||
probe_assign_decision (probe);
|
||||
if (probe_propagate ())
|
||||
hbrs = trail.size(), backtrack();
|
||||
else hbrs = 0, failed_literal (probe);
|
||||
if (max_hbrs < hbrs ||
|
||||
(max_hbrs == hbrs &&
|
||||
internal->bumped(probe) > internal->bumped(res))) {
|
||||
res = probe;
|
||||
max_hbrs = hbrs;
|
||||
}
|
||||
}
|
||||
|
||||
reset_mode (PROBE);
|
||||
|
||||
if (unsat) {
|
||||
MSG ("probing derived empty clause");
|
||||
res = INT_MIN;
|
||||
}
|
||||
else if (propagated < trail.size ()) {
|
||||
MSG ("probing produced %zd units",
|
||||
(size_t)(trail.size () - propagated));
|
||||
if (!propagate ()) {
|
||||
MSG ("propagating units after probing results in empty clause");
|
||||
learn_empty_clause ();
|
||||
res = INT_MIN;
|
||||
} else sort_watches ();
|
||||
}
|
||||
|
||||
#ifndef QUIET
|
||||
int failed = stats.failed - old_failed;
|
||||
int64_t probed = stats.probed - old_probed;
|
||||
#endif
|
||||
int64_t hbrs = stats.hbrs - old_hbrs;
|
||||
|
||||
MSG ("lookahead-probe-round %" PRId64 " probed %" PRId64
|
||||
" and found %d failed literals",
|
||||
stats.probingrounds,
|
||||
probed, failed);
|
||||
|
||||
if (hbrs)
|
||||
PHASE ("lookahead-probe-round", stats.probingrounds,
|
||||
"found %" PRId64 " hyper binary resolvents", hbrs);
|
||||
|
||||
MSG ("lookahead literal %d with %d\n", res, max_hbrs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
CubesWithStatus Internal::generate_cubes(int depth, int min_depth) {
|
||||
if (!active() || depth == 0) {
|
||||
CubesWithStatus cubes;
|
||||
cubes.cubes.push_back(std::vector<int>());
|
||||
return cubes;
|
||||
}
|
||||
|
||||
lookingahead = true;
|
||||
START(lookahead);
|
||||
MSG("Generating cubes of depth %i", depth);
|
||||
|
||||
// presimplify required due to assumptions
|
||||
|
||||
termination_forced = false;
|
||||
int res = already_solved();
|
||||
if (res == 0)
|
||||
res = restore_clauses();
|
||||
if(unsat)
|
||||
res = 10;
|
||||
if (res != 0)
|
||||
res = solve(true);
|
||||
if (res != 0) {
|
||||
MSG("Solved during preprocessing");
|
||||
CubesWithStatus cubes;
|
||||
cubes.status = res;
|
||||
cubes.status = 20;
|
||||
lookingahead = false;
|
||||
STOP(lookahead);
|
||||
return cubes;
|
||||
}
|
||||
|
||||
reset_limits();
|
||||
MSG ("generate cubes with %zu assumptions\n", assumptions.size());
|
||||
|
||||
assert(ntab.empty());
|
||||
std::vector<int> current_assumptions{assumptions};
|
||||
std::vector<std::vector<int>> cubes {{assumptions}};
|
||||
auto loccs{lookahead_populate_locc()};
|
||||
LOG("loccs populated\n");
|
||||
assert(ntab.empty());
|
||||
|
||||
for (int i = 0; i < depth; ++i) {
|
||||
LOG("Probing at depth %i, currently %zu have been generated",
|
||||
i, cubes.size());
|
||||
std::vector<std::vector<int>> cubes2 {std::move(cubes)};
|
||||
cubes.clear ();
|
||||
|
||||
for (size_t j = 0; j < cubes2.size(); ++j) {
|
||||
assert(ntab.empty());
|
||||
assert(!unsat);
|
||||
reset_assumptions();
|
||||
for (auto lit : cubes2[j])
|
||||
assume(lit);
|
||||
restore_clauses();
|
||||
propagate();
|
||||
// preprocess_round(0); //uncomment maybe
|
||||
|
||||
if (unsat) {
|
||||
LOG("current cube is unsat; skipping");
|
||||
unsat = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
int res = terminating_asked() ? lookahead_locc(loccs) : lookahead_probing();
|
||||
if(unsat) {
|
||||
LOG("current cube is unsat; skipping");
|
||||
unsat = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
LOG("no lit to split %i", res);
|
||||
cubes.push_back(cubes2[j]);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(res != 0);
|
||||
LOG("splitting on lit %i", res);
|
||||
std::vector<int> cube1{cubes2[j]};
|
||||
cube1.push_back(res);
|
||||
std::vector<int> cube2{std::move(cubes2[j])};
|
||||
cube2.push_back(-res);
|
||||
cubes.push_back(cube1);
|
||||
cubes.push_back(cube2);
|
||||
}
|
||||
|
||||
if (terminating_asked() && i >= min_depth)
|
||||
break;
|
||||
}
|
||||
|
||||
assert(std::for_each(std::begin(cubes), std::end(cubes), [](std::vector<int> cube){return non_tautological_cube (cube);}));
|
||||
reset_assumptions();
|
||||
|
||||
for(auto lit : current_assumptions)
|
||||
assume(lit);
|
||||
|
||||
STOP(lookahead);
|
||||
lookingahead = false;
|
||||
|
||||
if (unsat) {
|
||||
LOG("Solved during preprocessing");
|
||||
CubesWithStatus cubes;
|
||||
cubes.status = 20;
|
||||
cubes.status = res;
|
||||
return cubes;
|
||||
}
|
||||
|
||||
CubesWithStatus rcubes;
|
||||
rcubes.status = 0;
|
||||
rcubes.cubes = cubes;
|
||||
|
||||
return rcubes;
|
||||
}
|
||||
|
||||
} // namespace CaDiCaL
|
307
hCaD_V2/src/lucky.cpp
Normal file
307
hCaD_V2/src/lucky.cpp
Normal file
@ -0,0 +1,307 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// It turns out that even in the competition there are formulas which are
|
||||
// easy to satisfy by either setting all variables to the same truth value
|
||||
// or by assigning variables to the same value and propagating it. In the
|
||||
// latter situation this can be done either in the order of all variables
|
||||
// (forward or backward) or in the order of all clauses. These lucky
|
||||
// assignments can be tested initially in a kind of pre-solving step.
|
||||
|
||||
// This function factors out clean up code common among the 'lucky'
|
||||
// functions for backtracking and resetting a potential conflict. One could
|
||||
// also use exceptions here, but there are two different reasons for
|
||||
// aborting early. The first kind of aborting is due to asynchronous
|
||||
// termination and the second kind due to a situation in which it is clear
|
||||
// that a particular function will not be successful (for instance a
|
||||
// completely negative clause is found). The latter situation returns zero
|
||||
// and will just abort the particular lucky function, while the former will
|
||||
// abort all (by returning '-1').
|
||||
|
||||
int Internal::unlucky (int res) {
|
||||
if (level > 0) backtrack ();
|
||||
if (conflict) conflict = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
int Internal::trivially_false_satisfiable () {
|
||||
LOG ("checking that all clauses contain a negative literal");
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (const auto & c : clauses) {
|
||||
if (terminated_asynchronously (100)) return unlucky (-1);
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
bool satisfied = false, found_negative_literal = false;
|
||||
for (const auto & lit : *c) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (tmp < 0) continue;
|
||||
if (lit > 0) continue;
|
||||
found_negative_literal = true;
|
||||
break;
|
||||
}
|
||||
if (satisfied || found_negative_literal) continue;
|
||||
LOG (c, "found purely positively");
|
||||
return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "all clauses contain a negative literal");
|
||||
for (auto idx : vars) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (-idx);
|
||||
if (propagate ()) continue;
|
||||
assert (level > 0);
|
||||
LOG ("propagation failed including redundant clauses");
|
||||
return unlucky (0);
|
||||
}
|
||||
stats.lucky.constant.zero++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
int Internal::trivially_true_satisfiable () {
|
||||
LOG ("checking that all clauses contain a positive literal");
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (const auto & c : clauses) {
|
||||
if (terminated_asynchronously (100)) return unlucky (-1);
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
bool satisfied = false, found_positive_literal = false;
|
||||
for (const auto & lit : *c) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (tmp < 0) continue;
|
||||
if (lit < 0) continue;
|
||||
found_positive_literal = true;
|
||||
break;
|
||||
}
|
||||
if (satisfied || found_positive_literal) continue;
|
||||
LOG (c, "found purely negatively");
|
||||
return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "all clauses contain a positive literal");
|
||||
for (auto idx : vars) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (idx);
|
||||
if (propagate ()) continue;
|
||||
assert (level > 0);
|
||||
LOG ("propagation failed including redundant clauses");
|
||||
return unlucky (0);
|
||||
}
|
||||
stats.lucky.constant.one++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int Internal::forward_false_satisfiable () {
|
||||
LOG ("checking increasing variable index false assignment");
|
||||
assert (!unsat);
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (auto idx : vars) {
|
||||
if (terminated_asynchronously (100)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (-idx);
|
||||
if (!propagate ()) return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "forward assuming variables false satisfies formula");
|
||||
assert (satisfied ());
|
||||
stats.lucky.forward.zero++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
int Internal::forward_true_satisfiable () {
|
||||
LOG ("checking increasing variable index true assignment");
|
||||
assert (!unsat);
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (auto idx : vars) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (idx);
|
||||
if (!propagate ()) return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "forward assuming variables true satisfies formula");
|
||||
assert (satisfied ());
|
||||
stats.lucky.forward.one++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int Internal::backward_false_satisfiable () {
|
||||
LOG ("checking decreasing variable index false assignment");
|
||||
assert (!unsat);
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (int idx = max_var; idx > 0; idx--) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (-idx);
|
||||
if (!propagate ()) return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "backward assuming variables false satisfies formula");
|
||||
assert (satisfied ());
|
||||
stats.lucky.backward.zero++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
int Internal::backward_true_satisfiable () {
|
||||
LOG ("checking decreasing variable index true assignment");
|
||||
assert (!unsat);
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (int idx = max_var; idx > 0; idx--) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (idx);
|
||||
if (!propagate ()) return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "backward assuming variables true satisfies formula");
|
||||
assert (satisfied ());
|
||||
stats.lucky.backward.one++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// The following two functions test if the formula is a satisfiable horn
|
||||
// formula. Actually the test is slightly more general. It goes over all
|
||||
// clauses and assigns the first positive literal to true and propagates.
|
||||
// Already satisfied clauses are of course skipped. A reverse function
|
||||
// is not implemented yet.
|
||||
|
||||
int Internal::positive_horn_satisfiable () {
|
||||
LOG ("checking that all clauses are positive horn satisfiable");
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (const auto & c : clauses) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
int positive_literal = 0;
|
||||
bool satisfied = false;
|
||||
for (const auto & lit : *c) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (tmp < 0) continue;
|
||||
if (lit < 0) continue;
|
||||
positive_literal = lit;
|
||||
break;
|
||||
}
|
||||
if (satisfied) continue;
|
||||
if (!positive_literal) {
|
||||
LOG (c, "no positive unassigned literal in");
|
||||
return unlucky (0);
|
||||
}
|
||||
assert (positive_literal > 0);
|
||||
LOG (c, "found positive literal %d in", positive_literal);
|
||||
search_assume_decision (positive_literal);
|
||||
if (propagate ()) continue;
|
||||
LOG ("propagation of positive literal %d leads to conflict",
|
||||
positive_literal);
|
||||
return unlucky (0);
|
||||
}
|
||||
for (auto idx : vars) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (-idx);
|
||||
if (propagate ()) continue;
|
||||
LOG ("propagation of remaining literal %d leads to conflict", -idx);
|
||||
return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "clauses are positive horn satisfied");
|
||||
assert (!conflict);
|
||||
assert (satisfied ());
|
||||
stats.lucky.horn.positive++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
int Internal::negative_horn_satisfiable () {
|
||||
LOG ("checking that all clauses are negative horn satisfiable");
|
||||
assert (!level);
|
||||
assert (assumptions.empty ());
|
||||
for (const auto & c : clauses) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (c->garbage) continue;
|
||||
if (c->redundant) continue;
|
||||
int negative_literal = 0;
|
||||
bool satisfied = false;
|
||||
for (const auto & lit : *c) {
|
||||
const signed char tmp = val (lit);
|
||||
if (tmp > 0) { satisfied = true; break; }
|
||||
if (tmp < 0) continue;
|
||||
if (lit > 0) continue;
|
||||
negative_literal = lit;
|
||||
break;
|
||||
}
|
||||
if (satisfied) continue;
|
||||
if (!negative_literal) {
|
||||
if (level > 0) backtrack ();
|
||||
LOG (c, "no negative unassigned literal in");
|
||||
return unlucky (0);
|
||||
}
|
||||
assert (negative_literal < 0);
|
||||
LOG (c, "found negative literal %d in", negative_literal);
|
||||
search_assume_decision (negative_literal);
|
||||
if (propagate ()) continue;
|
||||
LOG ("propagation of negative literal %d leads to conflict",
|
||||
negative_literal);
|
||||
return unlucky (0);
|
||||
}
|
||||
for (auto idx : vars) {
|
||||
if (terminated_asynchronously (10)) return unlucky (-1);
|
||||
if (val (idx)) continue;
|
||||
search_assume_decision (idx);
|
||||
if (propagate ()) continue;
|
||||
LOG ("propagation of remaining literal %d leads to conflict", idx);
|
||||
return unlucky (0);
|
||||
}
|
||||
VERBOSE (1, "clauses are negative horn satisfied");
|
||||
assert (!conflict);
|
||||
assert (satisfied ());
|
||||
stats.lucky.horn.negative++;
|
||||
return 10;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
int Internal::lucky_phases () {
|
||||
assert (!level);
|
||||
require_mode (SEARCH);
|
||||
if (!opts.lucky) return 0;
|
||||
|
||||
// TODO: Some of the lucky assignments can also be found if there are
|
||||
// assumptions, but this is not completely implemented nor tested yet.
|
||||
//
|
||||
if (!assumptions.empty ()) return 0;
|
||||
|
||||
START (search);
|
||||
START (lucky);
|
||||
assert (!searching_lucky_phases);
|
||||
searching_lucky_phases = true;
|
||||
stats.lucky.tried++;
|
||||
int res = trivially_false_satisfiable ();
|
||||
if (!res) res = trivially_true_satisfiable ();
|
||||
if (!res) res = forward_true_satisfiable ();
|
||||
if (!res) res = forward_false_satisfiable ();
|
||||
if (!res) res = backward_false_satisfiable ();
|
||||
if (!res) res = backward_true_satisfiable ();
|
||||
if (!res) res = positive_horn_satisfiable ();
|
||||
if (!res) res = negative_horn_satisfiable ();
|
||||
if (res < 0) assert (termination_forced), res = 0;
|
||||
if (res == 10) stats.lucky.succeeded++;
|
||||
report ('l', !res);
|
||||
assert (searching_lucky_phases);
|
||||
searching_lucky_phases = false;
|
||||
STOP (lucky);
|
||||
STOP (search);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
1
hCaD_V2/src/makefile
Symbolic link
1
hCaD_V2/src/makefile
Symbolic link
@ -0,0 +1 @@
|
||||
/home/chenzh/experiments/cec/equal/hCaD_V2/makefile
|
206
hCaD_V2/src/message.cpp
Normal file
206
hCaD_V2/src/message.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#ifndef QUIET
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::print_prefix () { fputs (prefix.c_str (), stdout); }
|
||||
|
||||
void Internal::vmessage (const char * fmt, va_list & ap) {
|
||||
#ifdef LOGGING
|
||||
if (!opts.log)
|
||||
#endif
|
||||
if (opts.quiet) return;
|
||||
print_prefix ();
|
||||
vprintf (fmt, ap);
|
||||
fputc ('\n', stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
void Internal::message (const char * fmt, ...) {
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vmessage (fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
void Internal::message () {
|
||||
#ifdef LOGGING
|
||||
if (!opts.log)
|
||||
#endif
|
||||
if (opts.quiet) return;
|
||||
print_prefix ();
|
||||
fputc ('\n', stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::vverbose (int level, const char * fmt, va_list & ap) {
|
||||
#ifdef LOGGING
|
||||
if (!opts.log)
|
||||
#endif
|
||||
if (opts.quiet || level > opts.verbose) return;
|
||||
print_prefix ();
|
||||
vprintf (fmt, ap);
|
||||
fputc ('\n', stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
void Internal::verbose (int level, const char * fmt, ...) {
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vverbose (level, fmt, ap);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
void Internal::verbose (int level) {
|
||||
#ifdef LOGGING
|
||||
if (!opts.log)
|
||||
#endif
|
||||
if (opts.quiet || level > opts.verbose) return;
|
||||
print_prefix ();
|
||||
fputc ('\n', stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::section (const char * title) {
|
||||
#ifdef LOGGING
|
||||
if (!opts.log)
|
||||
#endif
|
||||
if (opts.quiet) return;
|
||||
if (stats.sections++) MSG ();
|
||||
print_prefix ();
|
||||
tout.blue ();
|
||||
fputs ("--- [ ", stdout);
|
||||
tout.blue (true);
|
||||
fputs (title, stdout);
|
||||
tout.blue ();
|
||||
fputs (" ] ", stdout);
|
||||
for (int i = strlen (title) + strlen (prefix.c_str ()) + 9; i < 78; i++)
|
||||
fputc ('-' , stdout);
|
||||
tout.normal ();
|
||||
fputc ('\n', stdout);
|
||||
MSG ();
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::phase (const char * phase, const char * fmt, ...) {
|
||||
#ifdef LOGGING
|
||||
if (!opts.log)
|
||||
#endif
|
||||
if (opts.quiet ||
|
||||
(!force_phase_messages && opts.verbose < 2)) return;
|
||||
print_prefix ();
|
||||
printf ("[%s] ", phase);
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vprintf (fmt, ap);
|
||||
va_end (ap);
|
||||
fputc ('\n', stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
void Internal::phase (const char * phase,
|
||||
int64_t count, const char * fmt, ...) {
|
||||
#ifdef LOGGING
|
||||
if (!opts.log)
|
||||
#endif
|
||||
if (opts.quiet ||
|
||||
(!force_phase_messages && opts.verbose < 2)) return;
|
||||
print_prefix ();
|
||||
printf ("[%s-%" PRId64 "] ", phase, count);
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vprintf (fmt, ap);
|
||||
va_end (ap);
|
||||
fputc ('\n', stdout);
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
#endif // ifndef QUIET
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::warning (const char *fmt, ...) {
|
||||
fflush (stdout);
|
||||
terr.bold ();
|
||||
fputs ("cadical: ", stderr);
|
||||
terr.red (1);
|
||||
fputs ("warning:", stderr);
|
||||
terr.normal ();
|
||||
fputc (' ', stderr);
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
fputc ('\n', stderr);
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void Internal::error_message_start () {
|
||||
fflush (stdout);
|
||||
terr.bold ();
|
||||
fputs ("cadical: ", stderr);
|
||||
terr.red (1);
|
||||
fputs ("error:", stderr);
|
||||
terr.normal ();
|
||||
fputc (' ', stderr);
|
||||
}
|
||||
|
||||
void Internal::error_message_end () {
|
||||
fputc ('\n', stderr);
|
||||
fflush (stderr);
|
||||
// TODO add possibility to use call back instead.
|
||||
exit (1);
|
||||
}
|
||||
|
||||
void Internal::verror (const char *fmt, va_list & ap) {
|
||||
error_message_start ();
|
||||
vfprintf (stderr, fmt, ap);
|
||||
error_message_end ();
|
||||
}
|
||||
|
||||
void Internal::error (const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
verror (fmt, ap);
|
||||
va_end (ap); // unreachable
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void fatal_message_start () {
|
||||
fflush (stdout);
|
||||
terr.bold ();
|
||||
fputs ("cadical: ", stderr);
|
||||
terr.red (1);
|
||||
fputs ("fatal error:", stderr);
|
||||
terr.normal ();
|
||||
fputc (' ', stderr);
|
||||
}
|
||||
|
||||
void fatal_message_end () {
|
||||
fputc ('\n', stderr);
|
||||
fflush (stderr);
|
||||
abort ();
|
||||
}
|
||||
|
||||
void fatal (const char *fmt, ...) {
|
||||
fatal_message_start ();
|
||||
va_list ap;
|
||||
va_start (ap, fmt);
|
||||
vfprintf (stderr, fmt, ap);
|
||||
va_end (ap);
|
||||
fatal_message_end ();
|
||||
abort ();
|
||||
}
|
||||
|
||||
}
|
40
hCaD_V2/src/message.hpp
Normal file
40
hCaD_V2/src/message.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef _message_h_INCLUDED
|
||||
#define _message_h_INCLUDED
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
// Macros for compact message code.
|
||||
|
||||
#ifndef QUIET
|
||||
|
||||
#define LINE() \
|
||||
do { if (internal) internal->message (); } while (0)
|
||||
|
||||
#define MSG(...) \
|
||||
do { if (internal) internal->message (__VA_ARGS__); } while (0)
|
||||
|
||||
#define PHASE(...) \
|
||||
do { if (internal) internal->phase (__VA_ARGS__); } while (0)
|
||||
|
||||
#define SECTION(...) \
|
||||
do { if (internal) internal->section (__VA_ARGS__); } while (0)
|
||||
|
||||
#define VERBOSE(...) \
|
||||
do { if (internal) internal->verbose (__VA_ARGS__); } while (0)
|
||||
|
||||
#else
|
||||
|
||||
#define LINE() do { } while (0)
|
||||
#define MSG(...) do { } while (0)
|
||||
#define PHASE(...) do { } while (0)
|
||||
#define SECTION(...) do { } while (0)
|
||||
#define VERBOSE(...) do { } while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#define FATAL fatal
|
||||
#define WARNING(...) internal->warning (__VA_ARGS__)
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
#endif // ifndef _message_h_INCLUDED
|
104
hCaD_V2/src/minimize.cpp
Normal file
104
hCaD_V2/src/minimize.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
#include "internal.hpp"
|
||||
|
||||
namespace CaDiCaL {
|
||||
|
||||
// Functions for learned clause minimization. We only have the recursive
|
||||
// version, which actually really is implemented recursively. We also
|
||||
// played with a derecursified version, which however was more complex and
|
||||
// slower. The trick to keep potential stack exhausting recursion under
|
||||
// guards is to explicitly limit the recursion depth.
|
||||
|
||||
// Instead of signatures as in the original implementation in MiniSAT and
|
||||
// our corresponding paper, we use the 'poison' idea of Allen Van Gelder to
|
||||
// mark unsuccessful removal attempts, then Donald Knuth's idea to abort
|
||||
// minimization if only one literal was seen on the level and a new idea of
|
||||
// also aborting if the earliest seen literal was assigned afterwards.
|
||||
|
||||
bool Internal::minimize_literal (int lit, int depth) {
|
||||
LOG("attempt to minimize lit %d at depth %d", lit, depth);
|
||||
assert(val(lit) > 0);
|
||||
Flags & f = flags (lit);
|
||||
Var & v = var (lit);
|
||||
if (!v.level || f.removable || f.keep) return true;
|
||||
if (!v.reason || f.poison || v.level == level) return false;
|
||||
const Level & l = control[v.level];
|
||||
if (!depth && l.seen.count < 2) return false; // Don Knuth's idea
|
||||
if (v.trail <= l.seen.trail) return false; // new early abort
|
||||
if (depth > opts.minimizedepth) return false;
|
||||
bool res = true;
|
||||
assert (v.reason);
|
||||
const const_literal_iterator end = v.reason->end ();
|
||||
const_literal_iterator i;
|
||||
for (i = v.reason->begin (); res && i != end; i++) {
|
||||
const int other = *i;
|
||||
if (other == lit) continue;
|
||||
res = minimize_literal (-other, depth + 1);
|
||||
}
|
||||
if (res) f.removable = true; else f.poison = true;
|
||||
minimized.push_back (lit);
|
||||
if (!depth) LOG ("minimizing %d %s", lit, res ? "succeeded" : "failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
// Sorting the clause before minimization with respect to the trail order
|
||||
// (literals with smaller trail height first) is necessary but natural and
|
||||
// might help to minimize the required recursion depth too.
|
||||
|
||||
struct minimize_trail_positive_rank {
|
||||
Internal * internal;
|
||||
minimize_trail_positive_rank (Internal * s) : internal (s) { }
|
||||
typedef int Type;
|
||||
Type operator () (const int & a) const {
|
||||
assert (internal->val (a));
|
||||
return internal->var (a).trail;
|
||||
}
|
||||
};
|
||||
|
||||
struct minimize_trail_smaller {
|
||||
Internal * internal;
|
||||
minimize_trail_smaller (Internal * s) : internal (s) { }
|
||||
bool operator () (const int & a, const int & b) const {
|
||||
return internal->var (a).trail < internal->var (b).trail;
|
||||
}
|
||||
};
|
||||
|
||||
void Internal::minimize_clause () {
|
||||
START (minimize);
|
||||
LOG (clause, "minimizing first UIP clause");
|
||||
|
||||
external->check_learned_clause (); // check 1st UIP learned clause first
|
||||
minimize_sort_clause();
|
||||
|
||||
assert (minimized.empty ());
|
||||
const auto end = clause.end ();
|
||||
auto j = clause.begin (), i = j;
|
||||
for (; i != end; i++)
|
||||
if (minimize_literal (-*i)) stats.minimized++;
|
||||
else flags (*j++ = *i).keep = true;
|
||||
LOG ("minimized %zd literals", (size_t)(clause.end () - j));
|
||||
if (j != end) clause.resize (j - clause.begin ());
|
||||
clear_minimized_literals ();
|
||||
STOP (minimize);
|
||||
}
|
||||
|
||||
// Sort the literals in reverse assignment order (thus trail order) to
|
||||
// establish the base case of the recursive minimization algorithm in the
|
||||
// positive case (where a literal with 'keep' true is hit).
|
||||
//
|
||||
void Internal::minimize_sort_clause () {
|
||||
MSORT(opts.radixsortlim, clause.begin(), clause.end(),
|
||||
minimize_trail_positive_rank(this), minimize_trail_smaller(this));
|
||||
}
|
||||
|
||||
void Internal::clear_minimized_literals () {
|
||||
LOG ("clearing %zd minimized literals", minimized.size ());
|
||||
for (const auto & lit : minimized) {
|
||||
Flags & f = flags (lit);
|
||||
f.poison = f.removable = f.shrinkable = false;
|
||||
}
|
||||
for (const auto & lit : clause)
|
||||
assert(!flags(lit).shrinkable), flags(lit).keep = flags(lit).shrinkable = false;
|
||||
minimized.clear ();
|
||||
}
|
||||
|
||||
}
|
3082
hCaD_V2/src/mobical.cpp
Normal file
3082
hCaD_V2/src/mobical.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user