CaDiCal2019学习笔记(2)
初步心得
1.将很多技术模块设计为结构体和类型,这些结构体和类型分别由相应的.hpp文件给出;与之相对应的同名.cpp并不都是类型的函数实现代码所在的文件。
结构体或类中的成员函数的实现代码通常在internal.cpp; (少数除外,如version.hpp对应的version.cpp是与传统方式一致的)
同名的.cpp文件给出的是internal.hpp声明的函数,这些函数与目前标识名称所在类型关联性很强。
同名的.cpp文件头部没用如通常情况#include自己同名文件,而是#include "internal.hpp"
2.子句管理是硬核技术,所以有几个类型(Tracer、Checker)都是派生于observer类。
该类用于处理添加、派生或删除的子句的类。Proof observer class used to act on added, derived or deleted clauses.
提供一众虚函数作为统一的函数接口。
virtual void add_original_clause (const vector<int> &) { }
virtual void add_derived_clause (const vector<int> &) { }
virtual void delete_clause (const vector<int> &) { }
3.编程指导思想创新之处:
- 使用相应mode来标识使用的前提状态;如void Internal::probe_propagate2 ()的函数体第一行: require_mode (PROBE);
- 大量使用assert,确保每个函数的运行条件时正确的,便于纠错;如:assert (opts.chrono);
- 使用通用宏,实现统一操作。如选项、打印等。详见:options.hpp与options.cpp;
//-------------------------------------------------------------------------------------
Option Options::table [] = {
#define OPTION(N,V,L,H,O,D) \
{ #N, (int) V, (int) L, (int) H, (int) O, D },
OPTIONS
#undef OPTION
};
//-------------------------------------------------------------------------------------
//在指数移动平均类型定义ema.hpp文件中定义了如下两个宏:
/*------------------------------------------------------------------------*/
// 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)
/*------------------------------------------------------------------------*/
- 将技术模块分为internal和external。external中定义有internal类型的指针。
- 函数在调用时,其前面会加上命名空间、所属类名、成员类\结构体(或子对象)名等,被调用函数出现多个圆点.组成的前导,功能和来源一目了然。
- 有专门的统计数据类型,见stats.hpp,各个环节的数据信息以结构体形式组合,非常适合观测。为今后深入利用这些信息开展大数据处理及统计分析奠定了物质基础。——根据需要,可以不断增加。
- 采用了迭代器类:基类ClauseIterator和派生类ClauseWriter、ClauseCopier、基类WitnessIterator和派生类 WitnessWriter 、WitnessCopier、 。(声明在solver.cpp Line1023-1043)
- 采用装饰器(wrapper)技术简化操作函数和类型使用:详见cadical.h、 cadical.hpp、cadical.cpp;
4.主函数在:src文件夹提供cadical库,编译后可交由外部测试和使用
(1)src文件夹外部并列的test文件夹之中,test/api之中多个文件均有main函数,通过主函数使用CaDiCal库。
标准的调用流程参见文件ctest.c和terminate.cpp,代码如下:
|
//定义一个鸽笼问题并求解---terminate.cpp 文件
1 #include "../../src/cadical.hpp" 2 3 #ifdef NDEBUG 4 #undef NDEBUG 5 #endif 6 7 extern "C" { 8 #include <assert.h> 9 #include <unistd.h> 10 #include <signal.h> 11 } 12 13 static int n = 11; 14 15 static int ph (int p, int h) { 16 assert (0 <= p), assert (p < n + 1); 17 assert (0 <= h), assert (h < n); 18 return 1 + h * (n+1) + p; 19 } 20 21 static CaDiCaL::Solver solver; 22 23 static void handler (int) { solver.terminate (); } 24 25 int main () { 26 27 // Construct a pigeon hole formula for 'n+1' pigeons in 'n' holes. 28 // 29 for (int h = 0; h < n; h++) 30 for (int p1 = 0; p1 < n + 1; p1++) 31 for (int p2 = p1 + 1; p2 < n + 1; p2++) 32 solver.add (-ph (p1, h)), 33 solver.add (-ph (p2, h)), 34 solver.add (0); 35 36 for (int p = 0; p < n + 1; p++) { 37 for (int h = 0; h < n; h++) 38 solver.add (ph (p, h)); 39 solver.add (0); 40 } 41 42 (void) signal (SIGALRM, handler); 43 ualarm (1e5, 0); 44 int res = solver.solve (); 45 assert (!res); 46 solver.statistics (); 47 48 return 0; 49 } //定义一个旅行商问题并求解---traverse.cpp
1 #include "../../src/cadical.hpp" 2 3 #ifdef NDEBUG 4 #undef NDEBUG 5 #endif 6 7 #include <cassert> 8 #include <cstdlib> 9 #include <iostream> 10 #include <string> 11 12 using namespace std; 13 using namespace CaDiCaL; 14 15 static string path (const char * suffix) { 16 const char * prefix = getenv ("CADICALBUILD"); 17 string res = prefix ? prefix : "."; 18 res += "/test-api-traverse."; 19 res += suffix; 20 return res; 21 } 22 23 struct WitnessChecker : WitnessIterator { 24 bool match (int a, int b) { 25 if (a == -3 && b == 1) return true; 26 if (a == 1 && b == -3) return true; 27 if (a == -3 && b == 2) return true; 28 if (a == 2 && b == -3) return true; 29 return false; 30 } 31 bool match (int a, int b, int c) { 32 if (a == 3 && b == -1 && c == -2) return true; 33 if (a == 3 && b == -2 && c == -1) return true; 34 if (a == -1 && b == 3 && c == -2) return true; 35 if (a == -2 && b == 3 && c == -1) return true; 36 if (a == -1 && b == -2 && c == 3) return true; 37 if (a == -2 && b == -1 && c == 3) return true; 38 return false; 39 } 40 public: 41 bool witness (const vector<int> & c, const vector<int> & w) { 42 for (const auto & lit : w) cout << lit << ' '; 43 cout << "0 "; 44 for (const auto & lit : c) cout << lit << ' '; 45 cout << '0' << endl; 46 if (c.size () == 1) { 47 assert (c[0] == 5); 48 assert (w.size () == 1); 49 assert (w[0] == 5); 50 } else { 51 assert (w.size () == 1); 52 assert (w[0] != -3); 53 assert (w[0] != 3); 54 assert (abs (w[0]) == 1 || abs (w[0]) == 2); 55 if (c.size () == 2) assert (match (c[0], c[1])); 56 else assert (c.size () == 3), assert (match (c[0], c[1], c[2])); 57 } 58 return true; 59 } 60 }; 61 62 struct ClauseChecker : ClauseIterator { 63 bool clause (const vector<int> & c) { 64 for (const auto & lit : c) cout << lit << ' '; 65 cout << '0' << endl; 66 assert (c.size () == 1); 67 assert (c[0] == 4); 68 return true; 69 } 70 }; 71 72 int main () { 73 74 Solver cadical; 75 76 // And gate 3 = 1 & 2> 77 78 cadical.add (-3); 79 cadical.add (1); 80 cadical.add (0); 81 82 cadical.add (-3); 83 cadical.add (2); 84 cadical.add (0); 85 86 cadical.add (3); 87 cadical.add (-1); 88 cadical.add (-2); 89 cadical.add (0); 90 91 // Force 4 to true. 92 93 cadical.add (4); 94 cadical.add (1); 95 cadical.add (2); 96 cadical.add (0); 97 98 cadical.add (4); 99 cadical.add (-1); 100 cadical.add (2); 101 cadical.add (0); 102 103 cadical.add (4); 104 cadical.add (1); 105 cadical.add (-2); 106 cadical.add (0); 107 108 cadical.add (4); 109 cadical.add (-1); 110 cadical.add (-2); 111 cadical.add (0); 112 113 // Force 5 to true too. 114 115 cadical.add (5); 116 cadical.add (1); 117 cadical.add (0); 118 119 cadical.add (5); 120 cadical.add (-1); 121 cadical.add (0); 122 123 cadical.freeze (3); 124 cadical.freeze (4); 125 126 cadical.simplify (1); 127 128 cadical.write_dimacs (path ("clauses").c_str (), 5); 129 cadical.write_extension (path ("extensions").c_str ()); 130 131 cout << "clauses" << endl; 132 ClauseChecker clause_checker; 133 cadical.traverse_clauses (clause_checker); 134 135 cout << "witnesses" << endl; 136 WitnessChecker witness_checker; 137 cadical.traverse_witnesses (witness_checker); 138 139 return 0; 140 }
|
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int main (int argc, char ** argv) {
CaDiCaL::App app;
return app.main (argc, argv);
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(2)src文件夹内部提供了用于测试的main——在mobical.cpp文件中的main
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// mobical.cpp文件中定义 Mobical类有名为main的函数;并且文件中有很多不属于函数的全局量;最后在文件后有全局main函数的调用。
int Mobical::main (int argc, char ** argv) {...}
int main (int argc, char ** argv) {
return CaDiCaL::mobical.main (argc, argv);
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5.化简不仅在初始阶段——求解中很多中间环节都加上了化简。
6.许多中间环节增加check函数。在external.cpp和solution.cpp中有很多类似 void External::check_solution_on_learned_clause 的函数专门执行检查任务。
7.专门的App?
代码研读
1.Var类型
Var类型的声明——关注的是传播进行是var在trail队列的对应信息(所在的层、蕴含来源的子句等)和局部搜索信息(walk阶段)等。
//----------------------------------------------------------------------------------------------------------------
struct Var {
// Note that none of these members is valid unless the variable is
// assigned. During unassigning a variable we do not reset it.
int level; // decision level
int trail; // trail height at assignment
union {
Clause * reason; // implication graph edge during search
int parent; // implication graph parent during probing
};
};
//----------------------------------------------------------------------------------------------------------------
2. Clause类型及与子句相关用于比较的类型
最重要的优化是在子句中“嵌入”实际的文字。
struct Clause {
#ifdef LOGGING
long id; // Only useful for debugging.
#endif
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 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
bool used:1; // 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 2 can
// propagate after one decision, one with glue 3 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'.
//
int glue;
int size; // actual size of 'literals' (at least 2)
int pos; // position of last watch replacement
union {
int literals[2]; // of variadic 'size' (not just 2) in general
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, that is 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; }
size_t bytes () const { return (size - 2) * sizeof (int) + sizeof *this; }
// 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);
}
};
//-------------------------------------------------------------------------------------------------------------