easysat源码解读(一)

目录

进展

主函数部分很明了:

子函数部分

读取文件,存入数据,简单判断

bcp传播函数

easysat.h

困惑:


进展

主函数部分很明了:

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是啥 

??

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值