Skip to content

Commit e1c84ed

Browse files
committed
Initial implementation
Update to add re-minfill calc up to dec lev 4 Cleaner Update
1 parent db77f24 commit e1c84ed

File tree

4 files changed

+158
-10
lines changed

4 files changed

+158
-10
lines changed

src/counter.cpp

Lines changed: 138 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,126 @@ void Counter::td_decompose() {
437437
verb_print(1, "[td] decompose time: " << cpu_time() - my_time);
438438
}
439439

440+
void Counter::compute_minfill_score() {
441+
auto nodes = opt_indep_support_end-1;
442+
if (!conf.do_td_use_opt_indep) nodes = indep_support_end-1;
443+
444+
// Compute minfill scores for all variables
445+
// Score is based on the degree and potential fill-in when eliminating the variable
446+
// Takes into account current assignments: satisfied clauses are skipped,
447+
// and only unassigned variables contribute to the graph
448+
449+
// Build adjacency lists for each variable
450+
vector<set<uint32_t>> adj(nodes + 1);
451+
452+
// Add edges from binary clauses
453+
all_lits(i) {
454+
Lit l(i/2, i%2 == 0);
455+
for(const auto& l2: watches[l].binaries) {
456+
if (l2.red()) continue;
457+
if (l.var() > nodes || l2.lit().var() > nodes) continue;
458+
459+
// Skip if clause is already satisfied
460+
if (val(l) == T_TRI || val(l2.lit()) == T_TRI) continue;
461+
462+
// Only add edge if both variables are unassigned
463+
if (val(l.var()) == X_TRI && val(l2.lit().var()) == X_TRI && l.var() != l2.lit().var()) {
464+
adj[l.var()].insert(l2.lit().var());
465+
adj[l2.lit().var()].insert(l.var());
466+
}
467+
}
468+
}
469+
470+
// Add edges from long clauses
471+
for(const auto& off: long_irred_cls) {
472+
Clause& cl = *alloc->ptr(off);
473+
474+
// Check if clause is satisfied and collect unassigned variables
475+
bool satisfied = false;
476+
vector<uint32_t> unassigned_vars;
477+
478+
for(uint32_t i = 0; i < cl.sz; i++) {
479+
if (val(cl[i]) == T_TRI) {
480+
satisfied = true;
481+
break;
482+
}
483+
if (cl[i].var() <= nodes && val(cl[i].var()) == X_TRI) {
484+
unassigned_vars.push_back(cl[i].var());
485+
}
486+
}
487+
488+
// Skip if clause is satisfied
489+
if (satisfied) continue;
490+
491+
// Add edges between all pairs of unassigned variables
492+
for(size_t i = 0; i < unassigned_vars.size(); i++) {
493+
for(size_t j = i+1; j < unassigned_vars.size(); j++) {
494+
uint32_t v1 = unassigned_vars[i];
495+
uint32_t v2 = unassigned_vars[j];
496+
adj[v1].insert(v2);
497+
adj[v2].insert(v1);
498+
}
499+
}
500+
}
501+
502+
// Compute minfill score for each variable
503+
vector<uint32_t> fill_scores(nodes + 1, 0);
504+
uint32_t max_fill = 0;
505+
uint32_t num_unassigned = 0;
506+
507+
for(uint32_t v = 1; v <= nodes; v++) {
508+
// Skip assigned variables
509+
if (val(v) != X_TRI) continue;
510+
511+
num_unassigned++;
512+
513+
// Count how many edges would need to be added between neighbors of v
514+
uint32_t fill_count = 0;
515+
vector<uint32_t> neighbors(adj[v].begin(), adj[v].end());
516+
517+
for(size_t i = 0; i < neighbors.size(); i++) {
518+
for(size_t j = i+1; j < neighbors.size(); j++) {
519+
uint32_t n1 = neighbors[i];
520+
uint32_t n2 = neighbors[j];
521+
// If edge doesn't exist between neighbors, it would need to be added
522+
if (adj[n1].find(n2) == adj[n1].end()) {
523+
fill_count++;
524+
}
525+
}
526+
}
527+
528+
fill_scores[v] = fill_count;
529+
if (fill_count > max_fill) max_fill = fill_count;
530+
}
531+
532+
// Normalize scores: lower fill-in gets higher score
533+
// We invert so that variables with low fill-in get high scores
534+
for(uint32_t v = 1; v <= nodes; v++) {
535+
// Skip assigned variables
536+
if (val(v) != X_TRI) {
537+
minfill_score[v] = 0.0;
538+
continue;
539+
}
540+
541+
if (max_fill > 0) {
542+
minfill_score[v] = (double)(max_fill - fill_scores[v]) / (double)max_fill;
543+
} else {
544+
minfill_score[v] = 1.0;
545+
}
546+
}
547+
548+
// Compute weight similar to TD, using unassigned variables
549+
double ratio = num_unassigned > 0 ? (double)max_fill / (double)num_unassigned : 0.0;
550+
minfill_weight = exp(ratio * conf.minfill_exp_mult) / conf.minfill_divider;
551+
if (minfill_weight < conf.minfill_minweight) minfill_weight = conf.minfill_minweight;
552+
if (minfill_weight > conf.minfill_maxweight) minfill_weight = conf.minfill_maxweight;
553+
554+
verb_print(1, "[minfill] max fill: " << max_fill
555+
<< " unassigned: " << num_unassigned
556+
<< " ratio: " << std::fixed << std::setprecision(3) << ratio
557+
<< " weight: " << minfill_weight);
558+
}
559+
440560
// Self-check count without restart with CMS only
441561
FF Counter::check_count_norestart_cms(const Cube& c) {
442562
verb_print(1, "Checking cube count with CMS (no verb, no restart)");
@@ -1007,6 +1127,10 @@ vector<Cube> Counter::one_restart_count() {
10071127
tdscore.resize(nVars()+1, 0);
10081128
td_decompose();
10091129
}
1130+
if (conf.do_minfill) {
1131+
minfill_score.clear();
1132+
minfill_score.resize(nVars()+1, 0);
1133+
}
10101134
count_loop();
10111135
if (conf.verb >= 3) stats.print_short(this, comp_manager->get_cache());
10121136
return mini_cubes;
@@ -1190,8 +1314,10 @@ void Counter::decide_lit() {
11901314
// The decision literal is now ready. Deal with it.
11911315
uint32_t v = 0;
11921316
switch (conf.decide) {
1193-
case 0: v = find_best_branch(false, !conf.do_use_sat_solver); break;
1194-
case 1: v = find_best_branch(true, !conf.do_use_sat_solver); break;
1317+
case 0: v = find_best_branch(false, true, !conf.do_use_sat_solver); break; // TD only
1318+
case 1: v = find_best_branch(true, true, !conf.do_use_sat_solver); break; // ignore TD and minfill
1319+
case 2: v = find_best_branch(true, false, !conf.do_use_sat_solver); break; // minfill only
1320+
case 3: v = find_best_branch(false, false, !conf.do_use_sat_solver); break; // TD+minfill
11951321
default: assert(false);
11961322
}
11971323

@@ -1215,25 +1341,28 @@ void Counter::decide_lit() {
12151341
}
12161342

12171343
// The higher, the better. It is never below 0.
1218-
double Counter::score_of(const uint32_t v, bool ignore_td) const {
1344+
double Counter::score_of(const uint32_t v, bool ignore_td, bool ignore_minfill) const {
12191345
bool print = false;
12201346
/* if (stats.decisions % 40000 == 0) print = 1; */
12211347
/* print = true; */
12221348
/* print = false; */
12231349
double act_score = 0;
12241350
double td_score = 0;
1351+
double minfill_sc = 0;
12251352
double freq_score = 0;
12261353

12271354
if (!tdscore.empty() && !ignore_td) td_score = td_weight*tdscore[v];
1355+
if (!minfill_score.empty() && !ignore_minfill) minfill_sc = minfill_weight*minfill_score[v];
12281356
act_score = var_act(v)/conf.act_score_divisor;
12291357
freq_score = (double)comp_manager->freq_score_of(v)/conf.freq_score_divisor;
1230-
double score = act_score+td_score+freq_score;
1358+
double score = act_score+td_score+minfill_sc+freq_score;
12311359
if (print) cout << "v: " << setw(4) << v
12321360
<< setw(3) << " conflK: " << stats.conflicts/1000
12331361
<< setw(5) << " decK: " << stats.decisions/1000
12341362
<< setw(6) << " act_score: " << act_score/score
12351363
<< setw(6) << " freq_score: " << freq_score/score
12361364
<< setw(6) << " td_score: " << td_score/score
1365+
<< setw(6) << " minfill_score: " << minfill_sc/score
12371366
<< setw(6) << " total: " << score
12381367
<< setw(6) << endl;
12391368

@@ -1269,7 +1398,7 @@ double Counter::td_lookahead_score(const uint32_t v, const uint32_t base_comp_tw
12691398
return -1*std::max<int32_t>({w[0],w[1]})*w[0]*w[1];
12701399
}
12711400

1272-
uint32_t Counter::find_best_branch(const bool ignore_td, const bool also_nonindep) {
1401+
uint32_t Counter::find_best_branch(const bool ignore_td, const bool ignore_minfill, const bool also_nonindep) {
12731402
last_dec_candidates = 0;
12741403
bool only_optional_indep = true;
12751404
uint32_t best_var = 0;
@@ -1278,6 +1407,9 @@ uint32_t Counter::find_best_branch(const bool ignore_td, const bool also_noninde
12781407
is_indep = false;
12791408
bool couldnt_find_indep = false; // only used when also_nonindep is true
12801409

1410+
if (!ignore_minfill && conf.do_minfill && (dec_level() < 4 || decisions.empty()))
1411+
compute_minfill_score();
1412+
12811413
VERBOSE_DEBUG_DO(cout << "decision level: " << dec_level() << " var options: ");
12821414
if (weighted()) {
12831415
if (vars_act_dec.size() < (dec_level()+1) * (nVars()+1)) {
@@ -1315,7 +1447,7 @@ uint32_t Counter::find_best_branch(const bool ignore_td, const bool also_noninde
13151447
if (dec_level() < conf.td_lookahead &&
13161448
tw > conf.td_lookahead_tw_cutoff)
13171449
score = td_lookahead_score(v, tw);
1318-
else score = score_of(v, ignore_td) ;
1450+
else score = score_of(v, ignore_td, ignore_minfill) ;
13191451
if (best_var == 0 || score > best_var_score) {
13201452
best_var = v;
13211453
best_var_score = score;

src/counter.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,8 @@ class Counter {
341341
return watches[Lit(v, false)].activity + watches[Lit(v, true)].activity; }
342342
DecisionStack decisions;
343343
void decide_lit();
344-
uint32_t find_best_branch(const bool ignore_td = false, const bool also_nonindep = false);
345-
double score_of(const uint32_t v, bool ignore_td = false) const;
344+
uint32_t find_best_branch(const bool ignore_td = false, const bool ignore_minfill = false, const bool also_nonindep = false);
345+
double score_of(const uint32_t v, bool ignore_td = false, bool ignore_minfill = false) const;
346346
void vsads_readjust();
347347
void compute_td_score(TWD::TreeDecomposition& tdec, const uint32_t nodes, bool print = true);
348348
void compute_td_score_using_adj(const uint32_t nodes,
@@ -355,12 +355,15 @@ class Counter {
355355
uint32_t td_decompose_component(bool update_score);
356356
double td_lookahead_score(const uint32_t v, const uint32_t base_comp_tw);
357357
int td_width = 10000;
358+
void compute_minfill_score();
358359
void inc_act(const Lit lit);
359360
Heap<VarOrderLt> order_heap; // Only active during SAT solver mode
360361
bool standard_polarity(const uint32_t var) const;
361362
bool get_polarity(const uint32_t var) const;
362363
vector<double> tdscore;
363364
double td_weight = 1.0;
365+
vector<double> minfill_score;
366+
double minfill_weight = 1.0;
364367
uint64_t tstamp = 10;
365368
const Lit &top_dec_lit() const { return *top_declevel_trail_begin(); }
366369
vector<Lit>::const_iterator top_declevel_trail_begin() const;

src/counter_config.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct CounterConfiguration {
4545

4646
int cache_time_update = 2;
4747

48-
int decide = 0; // 0 = TD, 1 = ignore TD
48+
int decide = 0; // 0 = TD only, 1 = ignore TD, 2 = minfill only, 3 = TD+minfill
4949
uint32_t rdb_cls_target = 10000;
5050
int rdb_keep_used = 0; // quite a bit faster on lower time cut-off
5151
// but loses the edge after ~2000s
@@ -96,6 +96,12 @@ struct CounterConfiguration {
9696
int td_do_use_adj = 1;
9797
std::string td_read_file = "";
9898

99+
bool do_minfill = 1;
100+
double minfill_maxweight = 60;
101+
double minfill_minweight = 7;
102+
double minfill_divider = 1e3;
103+
double minfill_exp_mult = 1.1;
104+
99105
int do_use_sat_solver = 1;
100106
int sat_restart_mult = 300;
101107
int do_sat_restart = 1;

src/main.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@ void add_ganak_options()
192192
add_arg("--tdreadfile", conf.td_read_file, string, "Read TD scores from this file");
193193
add_arg("--tdvis", conf.td_visualize_dot_file, string, "Visualize the TD into this file in DOT format");
194194

195+
// Minfill options
196+
add_arg("--minfill", conf.do_minfill, atoi, "Run minfill heuristic");
197+
add_arg("--minfillmaxw", conf.minfill_maxweight, atof, "Minfill max weight");
198+
add_arg("--minfillminw", conf.minfill_minweight, atof, "Minfill min weight");
199+
add_arg("--minfilldiv", conf.minfill_divider, atof, "Minfill divider");
200+
add_arg("--minfillexpmult", conf.minfill_exp_mult, atof, "Minfill exponential multiplier");
201+
195202
// Clause DB options
196203
add_arg("--rdbclstarget", conf.rdb_cls_target, atoi, "RDB clauses target size (added to this are LBD 3 or lower)");
197204
add_arg("--rdbeveryn", conf.reduce_db_everyN, atoi, "Reduce the clause DB every N conflicts");
@@ -202,7 +209,7 @@ void add_ganak_options()
202209

203210
// Decision options
204211
add_arg("--polar", conf.polar_type, atoi, "0=standard_polarity, 1=polar cache, 2=false, 3=true");
205-
add_arg("--decide", conf.decide, atoi, "ignore or not ignore TD");
212+
add_arg("--decide", conf.decide, atoi, "0=TD only, 1=ignore TD, 2=minfill only, 3=TD+minfill");
206213
add_arg("--initact", conf.do_init_activity_scores, atoi, "Init activity scores to var freq");
207214
add_arg("--vsadsadjust", conf.vsads_readjust_every, atoi, "VSADS ajust activity every N");
208215
add_arg("--actscorediv", conf.act_score_divisor, atof, "Activity score divisor");

0 commit comments

Comments
 (0)