目录
进展
主函数部分很明了:
int main(int argc, char **argv) {
Solver S;
int res = S.parse("C:\\Users\\woaixiaomaomi\\Desktop\\ccnf.cnf");//argv[1]);
if (res == 20) printf("s UNSATISFIABLE\n");
else {
res = S.solve();
if (res == 10) {
printf("s SATISFIABLE\n");
S.printModel();
}
else if (res == 20) printf("s UNSATISFIABLE\n");
}
return 0;
}
1.通过parse函数,将文件导入,先简单判断是否不满足(比如存在两个子句 A 和 A非,则必然不满足),之后将读到的子句存入clause_DB(子句数据库中),bcp一波看能否判断不满足,最后如果确定不满足,将-20传递出来赋值给res
2,如果res!=20,说明还不确定是否满足,
这时需要求解solve,如果结果为10就满足,打印满足同时将对应的输入打印出来;如果结果为-20则不满足。
子函数部分
读取文件,存入数据,简单判断
#define value(lit) (lit > 0 ? value[lit] : -value[-lit]) // Get the value of a literal字面量
#define watch(id) (watches[vars + id]) // Remapping a literal [-maxvar, +maxvar] to its watcher.
char *read_whitespace(char *p) { // Aid function for parser解析器
while ((*p >= 9 && *p <= 13) || *p == 32) ++p;//32是空格,9-13是一些奇奇怪怪的键,键盘上找不到
return p;
}
char *read_until_new_line(char *p) { // Aid function for parser
while (*p != '\n') {
if (*p++ == '\0') exit(1);//表示异常强制退出
}
return ++p;
}
char *read_int(char *p, int *i) { // Aid function for parser
bool sym = true; *i = 0;
p = read_whitespace(p);
if (*p == '-') sym = false, ++p;//如果有符号,之后i要取反
while (*p >= '0' && *p <= '9') {//读取数字,读入到i中
if (*p == '\0') return p;
*i = *i * 10 + *p - '0', ++p;//字符转数字,然后加上一位读到的数字乘权重
}
if (!sym) *i = -(*i);
return p;
}
int Solver::add_clause(std::vector<int> &c) { // -1 2 watches(3)
clause_DB.push_back(Clause(c.size())); //lit字面量(int类容器) 重置大小为c.size 这样初始化一个子句,然后追加在db后面 // Add a clause c into database.
int id = clause_DB.size() - 1; //id表示子句的数量-1 即当前子句的标号 // Getting clause index.
for (int i = 0; i < (int)c.size(); i++) clause_DB[id][i] = c[i]; // Copy literals 将各个字面量存入进来,比如 -1 2
watches[vars-c[0]].push_back(Watcher(id, c[1])); //std::vector<Watcher> *watches; watches[id]表示第id个watches,每个watches都是一个watcher的容器 // Watch this clause by literal -c[0]
watch(-c[1]).push_back(Watcher(id, c[0])); //id 表示索引,c[0]用于快速判断是否sat // Watch this clause by literal -c[1]
return id; //子句的数量 -1
}
int Solver::parse(char *filename) {//return 20表示不满足:空语句,
//将文件读入data数组中
std::ifstream fin(filename); // Fast load begin
fin.seekg(0, fin.end);//设定输入流中文件指针的位置(偏移量,方式)结尾
size_t file_len = fin.tellg();//读取输入流中文件指针的位置,返回值可转化为 int。
fin.seekg(0, fin.beg);//设定指针在开头
char *data = new char[file_len + 1], *p = data;
fin.read(data, file_len);//将文件读入到data数组中
fin.close(); // Fast load end
data[file_len] = '\0';
std::vector<int> buffer; // Save the clause that waiting to push
while (*p != '\0') {
p = read_whitespace(p);//如果是空格之类的就跳过
if (*p == '\0') break;
if (*p == 'c') p = read_until_new_line(p);//读到下一行,c相当于是cnf文件里的注释行标志
else if (*p == 'p') { // Deal with 'p cnf' line.
if (*(p + 1) == ' ' && *(p + 2) == 'c' && *(p + 3) == 'n' && *(p + 4) == 'f') {
p += 5, p = read_int(p, &vars), p = read_int(p, &clauses);//将读到的数字存入 vars 和 clauses分别对应变量数,子句数
alloc_memory();
}
else printf("PARSE ERROR! Unexpected char\n"), exit(2); // Wrong 'p ' line.
}
else {
int32_t dimacs_lit;
p = read_int(p, &dimacs_lit);
if (*p == '\0' && dimacs_lit != 0) // Unexpected EOF
printf("c PARSE ERROR! Unexpected EOF\n"), exit(1);
if (dimacs_lit == 0) { //读到了行尾 // Finish read a clause.
if (buffer.size() == 0) return 20; //空 // Read an empty clause.
if (buffer.size() == 1 && value(buffer[0]) == -1) return 20; //-1表示false // Found confliction in origin clauses
if (buffer.size() == 1 && !value(buffer[0])) assign(buffer[0], 0, -1); //如果未定义,那就把读到的数字是原变量还是反变量存到value(|buffer[0]|) 1或-1 // Found an unit clause.
else if (buffer.size() > 1) add_clause(buffer);//添加数据到db // Found a clause who has more than 1 literals.
buffer.clear(); // For the next clause.
}
else buffer.push_back(dimacs_lit); //buffer int类容器 在Vector最后添加一个元素 // read a literal
}//buffer[i]表示当前行读到的第i+1个数字
}
origin_clauses = clause_DB.size();//子句的数量
return (propagate() == -1 ? 0 : 20); //不等于-1表示一定不sat // Simplify by BCP.
}
void Solver::alloc_memory() {//用于给各个变量分配内存,各参数的描述在.h文件
value = new int[vars + 1];
reason = new int[vars + 1];
level = new int[vars + 1];
mark = new int[vars + 1];
local_best = new int[vars + 1];
saved = new int[vars + 1];
activity = new double[vars + 1];
watches = new std::vector<Watcher>[vars * 2 + 1];
conflicts = time_stamp = propagated = restarts = rephases = reduces = threshold = 0;
fast_lbd_sum = lbd_queue_size = lbd_queue_pos = slow_lbd_sum = 0;
var_inc = 1, rephase_limit = 1024, reduce_limit = 8192;
vsids.setComp(GreaterActivity(activity));
for (int i = 1; i <= vars; i++)
value[i] = reason[i] = level[i] = mark[i] = local_best[i] = activity[i] = saved[i] = 0, vsids.insert(i);
}
void Solver::bump_var(int var, double coeff) {
if ((activity[var] += var_inc * coeff) > 1e100) { // Update score and prevent float overflow
for (int i = 1; i <= vars; i++) activity[i] *= 1e-100;
var_inc *= 1e-100;}
if (vsids.inHeap(var)) vsids.update(var); // update heap
}
void Solver::assign(int lit, int l, int cref) {
int var = abs(lit);//代表第几个变量
value[var] = lit > 0 ? 1 : -1;//原变量赋值1,反变量赋值-1 可以保证最终字面量为1,
level[var] = l, reason[var] = cref;
trail.push_back(lit);
}
bcp传播函数
int Solver::propagate() {//传播
while (propagated < (int)trail.size()) {
int p = trail[propagated++]; // Pick an unpropagated literal in trail.
std::vector<Watcher> &ws = watch(p); // Fetch the watcher for this literal.第var+p个 watcher容器
int i, j, size = ws.size(); //这个容器中含有几个watcher
for (i = j = 0; i < size; ) {
int blocker = ws[i].blocker; //这个容器中的第i个watcher的blocker
if (value(blocker) == 1) { // Pre-judge whether the clause is already SAT
ws[j++] = ws[i++]; continue;
}
int cref = ws[i].idx_clause, k, sz;
Clause& c = clause_DB[cref]; // Fetch a clause from watcher
if (c[0] == -p) c[0] = c[1], c[1] = -p; // Make sure c[1] is the false literal (-p).
Watcher w = Watcher(cref, c[0]); // Prepare a new watcher for c[1]
i++;
if (value(c[0]) == 1) { // Check whether another lit is SAT.
ws[j++] = w; continue;
}
for (k = 2, sz = c.lit.size(); k < sz && value(c[k]) == -1; k++); // Find a new watch literal.
if (k < sz) { // Move the watch literal to the second place
c[1] = c[k], c[k] = -p;
watch(-c[1]).push_back(w);
}
else { // Can not find a new watch literl
ws[j++] = w;
if (value(c[0]) == -1) { // There is a confliction
while (i < size) ws[j++] = ws[i++];
ws.resize(j);
return cref;
}
else assign(c[0], level[abs(p)], cref);// Find a new unit clause and assign it.
}
}
ws.resize(j);
}
return -1; // Meet a convergence
}
读到这个传播搞不懂就拐去配置调试,看论文理解watch了,,,虽然还没理解/(ㄒoㄒ)/~~
easysat.h
#include "heap.hpp"
class Clause {
public:
int lbd; // Literal Block Distance (Gilles & Laurent, IJCAI 2009)
std::vector<int> lit; // Literals in this clause 字面量
Clause(int sz): lbd(0) { lit.resize(sz); }//对类成员初始化
int& operator [] (int index) { return lit[index]; }//&不是取地址符号,而是引用符号,[]运算符重载,
//Clause cla; cla[3]就表示,cla.lit[3]
};
class Watcher {
public:
int idx_clause // The clause index in clause database.
, blocker; // Used to fast guess whether a clause is already satisfied.
Watcher(): idx_clause(0), blocker(0) {}
Watcher(int c, int b): idx_clause(c), blocker(b) {}
};
struct GreaterActivity { // A compare function used to sort the activities.
const double *activity;
bool operator() (int a, int b) const { return activity[a] > activity[b]; }
//重载括号运算符,这样当GreatActivity gr;gr(2,3)表示的是gr.activity[1]>gr.activity[2]的结果1或0
GreaterActivity(): activity(NULL) {}
GreaterActivity(const double *s): activity(s) {}
};
class Solver {
public:
std::vector<int> learnt, // The clause indices of the learnt clauses.学过的从句的分句索引
trail, // Save the assigned literal sequence.保存指定的文字序列
pos_in_trail, // Save the decision variables' position in trail.保存决策变量在trail中的位置
reduce_map; // Auxiliary data structure for clause management.子句管理的辅助数据结构
std::vector<Clause> clause_DB; // clause database.
std::vector<Watcher> *watches; // A mapping from literal to clauses.
int vars, clauses, origin_clauses, conflicts; // the number of variables, clauses, conflicts.
int restarts, rephases, reduces; // the number of conflicts since the last ... .rephases好像是回溯的意思,phases阶段
int rephase_limit, reduce_limit; // parameters for when to conduct rephase and reduce.
int threshold; // A threshold for updating the local_best phase.更新当前最好值的门槛
int propagated; // The number of propagted literals in trail.literal字面量,是一个变量或它的反变量
int time_stamp; // Aid parameter for conflict analyzation and LBD calculation. 辅助参数用于冲突分析和lbd计算 ,Literal Block Distance 字面量块的距离
int lbd_queue[50], // circled queue saved the recent 50 LBDs.
lbd_queue_size, // The number of LBDs in this queue
lbd_queue_pos; // The position to save the next LBD.
double fast_lbd_sum, slow_lbd_sum; // Sum of the Global and recent 50 LBDs.
int *value, // The variable assignement (1:True; -1:False; 0:Undefine)
*reason, //隐含变量赋值语句的索引 // The index of the clause that implies the variable assignment.
*level, // The decision level of a variable 变量的决策级别
*mark, // Aid for conflict analyzation. 协助冲突分析
*local_best, // A phase with a local deepest trail. 具有局部最深的阶段
*saved; // Phase saving.
double *activity; // The variables' score for VSIDS.
double var_inc; // Parameter for VSIDS.
Heap<GreaterActivity> vsids; // Heap to select variable.//堆来选择变量
void alloc_memory(); // Allocate memory for EasySAT
void assign(int lit, int level, int cref); // Assigned a variable.
int propagate(); // BCP
void backtrack(int backtrack_level); // Backtracking
int analyze(int cref, int &backtrack_level, int &lbd); // Conflict analyzation.
int parse(char *filename); // Read CNF file.
int solve(); // Solving.
int decide(); // Pick desicion variable.
int add_clause(std::vector<int> &c); // add new clause to clause database.
void bump_var(int var, double mult); // update activity
void restart(); // do restart.
void reduce(); // do clause management.
void rephase(); // do rephase.
void printModel(); // print model when the result is SAT.
};
中文注释在英文注释后。
困惑:
- watches到底是什么东西,还得认真看看论文,还有vsids是什么东东...看论文吧/(ㄒoㄒ)/~~
- debug看变量的时候,为什么这个clause_DB没法看每一个子句存入的字面量,可能要认真学一下vector,,,,
- 如何给main传参数,,,,命令行easysat "路径"不行,launch.json里也不行
用clion?会变简单吗/(ㄒoㄒ)/~~,不过目前可以直接将main里参数在的地方换成字符串,但是不知道为什么同一个cnf,minisat是可解的,这个是不可解的,,,可能还是没传对参数?
- lbd是啥
- reason是啥
??