collect.cpp

有关子句满足性判断、在watches中位置调整、是否可以撤销、撤销空间前copy垃圾收集器、释放空间等


 

包括以下internal类型的成员函数:

1 int Internal::clause_contains_fixed_literal (Clause * c);
2 void Internal::remove_falsified_literals (Clause * c);
3 void Internal::mark_satisfied_clauses_as_garbage () ;
4 void Internal::protect_reasons () ;
5 void Internal::unprotect_reasons ();
6 size_t Internal::flush_occs (int lit);
7 inline void Internal::flush_watches (int lit, Watches & saved);
8 void Internal::flush_all_occs_and_watches ();
9 void Internal::update_reason_references ();
10 void Internal::delete_garbage_clauses ();
11 void Internal::copy_clause (Clause * c);
12 void Internal::copy_non_garbage_clauses ()
13 void Internal::check_clause_stats ();
14 bool Internal::arenaing ();
15 void Internal::garbage_collection ();

 

代码解读

1.

 

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.

译文:如果给定子句满足根级别,则返回正数'1'(> 0);如果不满足根级别,但包含根级别假文字,则返回负数'-1'(< 0)。译文:否则,如果它既不包含满意的字面值,也不包含伪造的字面值,则返回'0'。

    • 子句满足     1
    • 子句不满足 -1
    • 子句未知     0
 
 1 // Returns the positive number '1' ( > 0) if the given clause is root level
 2 // satisfied or the negative number '-1' ( < 0) if it is not root level
 3 // satisfied but contains a root level falsified literal. Otherwise, if it
 4 // contains neither a satisfied nor falsified literal, then '0' is returned.
 5 
 6 int Internal::clause_contains_fixed_literal (Clause * c) {
 7   int satisfied = 0, falsified = 0;
 8   for (const auto & lit : *c) {
 9     const int tmp = fixed (lit);
10     if (tmp > 0) {
11       LOG (c, "root level satisfied literal %d in", lit);
12       satisfied++;
13     }
14     if (tmp < 0) {
15       LOG (c, "root level falsified literal %d in", lit);
16       falsified++;
17     }
18   }
19        if (satisfied) return 1;
20   else if (falsified) return -1;
21   else                return 0;
22 }

 

 

2

 

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.

假设子句没有满足根级别,但包含一个设为false的文字(根级别伪文本),所以它可以缩小。此时,子句数据并没有实际重新分配,以避免处理监视二进制子句的特殊策略或子句是否被扩展的问题。只有它的大小字段会在清空根级别伪字面值后进行相应的调整。

 
 1 void Internal::remove_falsified_literals (Clause * c) {
 2   const const_literal_iterator end = c->end ();
 3   const_literal_iterator i;
 4   int num_non_false = 0;
 5   for (i = c->begin (); num_non_false < 2 && i != end; i++)
 6     if (fixed (*i) >= 0) num_non_false++;
 7   if (num_non_false < 2) return;
 8   if (proof) proof->flush_clause (c);
 9   literal_iterator j = c->begin ();
10   for (i = j; i != end; i++) {
11     const int lit = *j++ = *i, tmp = fixed (lit);
12     assert (tmp <= 0);
13     if (tmp >= 0) continue;
14     LOG ("flushing %d", lit);
15     j--;
16   }
17   stats.collected += shrink_clause (c, j - c->begin ());
18 }

 

 

3.

 

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.

译文:如果在上次垃圾收集之后出现了新的单元(固定变量),我们将检查所有子句,将满足要求的子句标记为垃圾并刷新伪造的字面值。否则,如果自上次垃圾收集以来没有生成新的单元,则跳过此步骤。

 
 1 // If there are new units (fixed variables) since the last garbage
 2 // collection we go over all clauses, mark satisfied ones as garbage and
 3 // flush falsified literals.  Otherwise if no new units have been generated
 4 // since the last garbage collection just skip this step.
 5 
 6 void Internal::mark_satisfied_clauses_as_garbage () {
 7 
 8   if (last.collect.fixed >= stats.all.fixed) return;
 9   last.collect.fixed = stats.all.fixed;
10 
11   LOG ("marking satisfied clauses and removing falsified literals");
12 
13   for (const auto & c : clauses) {
14     if (c->garbage) continue;
15     const int tmp = clause_contains_fixed_literal (c);
16          if (tmp > 0) mark_garbage (c);
17     else if (tmp < 0) remove_falsified_literals (c);
18   }
19 }

 

 

4.

 

// 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.

 
 1 /*------------------------------------------------------------------------*/
 2 
 3 // Reason clauses can not be collected.
 4 //
 5 // We protect reasons before and release protection after garbage collection
 6 // (actually within garbage collection).
 7 //
 8 // For 'reduce' we still need to make sure that all clauses which should not
 9 // be removed are marked as such and thus we need to call it before marking
10 // clauses to be flushed.
11 
12 void Internal::protect_reasons () {
13   LOG ("protecting reason clauses of all assigned variables on trail");
14   assert (!protected_reasons);
15   size_t count = 0;
16   for (const auto & lit : trail) {
17     if (!active (lit)) continue;
18     assert (val (lit));
19     Var & v = var (lit);
20     assert (v.level > 0);
21     Clause * reason = v.reason;
22     if (!reason) continue;
23     LOG (reason, "protecting assigned %d reason %p", lit, reason);
24     assert (!reason->reason);
25     reason->reason = true;
26     count++;
27   }
28   LOG ("protected %zd reason clauses referenced on trail", count);
29   protected_reasons = true;
30 }

 

 

 

5.

 

After garbage collection we reset the 'reason' flag of the reasons
of assigned literals on the trail.

译文:在垃圾收集之后,我们在轨迹中重置了指定文本的原因的“reason”标志。

 
/*------------------------------------------------------------------------*/

// 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, reason);
    assert (reason->reason);
    reason->reason = false;
    count++;
  }
  LOG ("unprotected %zd reason clauses referenced on trail", count);
  protected_reasons = false;
}

 

 

6.

 

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.

在预处理的上下文中,在删除垃圾子句之前更新发生列表,例如在有界变量消除“elim”期间。

结果是剩余子句的数量,在这里是指非垃圾子句的数量。

 
 1 /*------------------------------------------------------------------------*/
 2 
 3 // Update occurrence lists before deleting garbage clauses in the context of
 4 // preprocessing, e.g., during bounded variable elimination 'elim'.  The
 5 // result is the number of remaining clauses, which in this context means
 6 // the number of non-garbage clauses.
 7 
 8 size_t Internal::flush_occs (int lit) {
 9   Occs & os = occs (lit);
10   const const_occs_iterator end = os.end ();
11   occs_iterator j = os.begin ();
12   const_occs_iterator i;
13   size_t res = 0;
14   Clause * c;
15   for (i = j; i != end; i++) {
16     c = *i;
17     if (c->collect ()) continue;
18     *j++ = c->moved ? c->copy : c;
19     assert (!c->redundant);
20     res++;
21   }
22   os.resize (j - os.begin ());
23   shrink_occs (os);
24   return res;
25 }

 

 

7. 8.

 

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.

在删除“reduce”上下文中的垃圾子句之前更新观察列表,在这里我们观察和没有发生列表。我们必须保护reason子句不被收集,因此我们有这个额外的检查隐藏在'子句。对于预处理的根级上下文来说,它实际上是多余的。

 
// 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);
}

 

   
 
 1 void Internal::flush_all_occs_and_watches () {
 2   if (occurring ())
 3     for (auto idx : vars)
 4       flush_occs (idx), flush_occs (-idx);
 5 
 6   if (watching ()) {
 7     Watches tmp;
 8     for (auto idx : vars)
 9       flush_watches (idx, tmp), flush_watches (-idx, tmp);
10   }
11 }

 

 

 

 

9.

 
 1 /*------------------------------------------------------------------------*/
 2 
 3 void Internal::update_reason_references () {
 4   LOG ("update assigned reason references");
 5   size_t count = 0;
 6   for (auto & lit : trail) {
 7     if (!active (lit)) continue;
 8     Var & v = var (lit);
 9     Clause * c = v.reason;
10     if (!c) continue;
11     LOG (c, "updating assigned %d reason", lit);
12     assert (c->reason);
13     assert (c->moved);
14     Clause * d = c->copy;
15     v.reason = d;
16     count++;
17   }
18   LOG ("updated %zd assigned reason references", count);
19 }

 

 
   

 

10.

 

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.

 译文:这是一个不移动子句的简单垃圾收集器

它比基于arena的子句分配器需要更少的空间,但缓存效率不高,

因为复制垃圾收集器可以将子句放在一起,这些子句可能会被依次访问。

   
 1 /*------------------------------------------------------------------------*/
 2 
 3 // This is a simple garbage collector which does not move clauses.  It needs
 4 // less space than the arena based clause allocator, but is not as cache
 5 // efficient, since the copying garbage collector can put clauses together
 6 // which are likely accessed after each other.
 7 
 8 void Internal::delete_garbage_clauses () {
 9 
10   flush_all_occs_and_watches ();
11 
12   LOG ("deleting garbage clauses");
13   int64_t collected_bytes = 0, collected_clauses = 0;
14   const auto end = clauses.end ();
15   auto j = clauses.begin (), i = j;
16   while (i != end) {
17     Clause * c = *j++ = *i++;
18     if (!c->collect ()) continue;
19     collected_bytes += c->bytes ();
20     collected_clauses++;
21     delete_clause (c);
22     j--;
23   }
24   clauses.resize (j - clauses.begin ());
25   shrink_vector (clauses);
26 
27   PHASE ("collect", stats.collections,
28     "collected %" PRId64 " bytes of %" PRId64 " garbage clauses",
29     collected_bytes, collected_clauses);
30 }

 

11.

 

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.

译文:这是使用arena复制垃圾收集器的开始。核心是以下函数,

它将一个子句复制到arena的“to”空间。如果这一条款是一个分配的原因,

要小心。在这种情况下,更新原因引用

 
 1 /*------------------------------------------------------------------------*/
 2 
 3 // This is the start of the copying garbage collector using the arena.  At
 4 // the core is the following function, which copies a clause to the 'to'
 5 // space of the arena.  Be careful if this clause is a reason of an
 6 // assignment.  In that case update the reason reference.
 7 //
 8 void Internal::copy_clause (Clause * c) {
 9   LOG (c, "moving");
10   assert (!c->moved);
11   char * p = (char*) c;
12   char * q = arena.copy (p, c->bytes ());
13   c->copy = (Clause *) q;
14   c->moved = true;
15   LOG ("copied clause[%" PRId64 "] from %p to %p", c->id, c, c->copy);
16 }

 

 

12.

 

// This is the moving garbage collector.译文:这是移动的垃圾收集器。

 

// 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.

 

// Localize according to (original) variable order.

// This is almost the version used by MiniSAT and descendants.
// Our version uses saved phases too.

 

// 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'.

 

 

 
  1 // This is the moving garbage collector.
  2 
  3 void Internal::copy_non_garbage_clauses () {
  4 
  5   size_t collected_clauses = 0, collected_bytes = 0;
  6   size_t     moved_clauses = 0,     moved_bytes = 0;
  7 
  8   // First determine 'moved_bytes' and 'collected_bytes'.
  9   //
 10   for (const auto & c : clauses)
 11     if (!c->collect ()) moved_bytes += c->bytes (), moved_clauses++;
 12     else collected_bytes += c->bytes (), collected_clauses++;
 13 
 14   PHASE ("collect", stats.collections,
 15     "moving %zd bytes %.0f%% of %zd non garbage clauses",
 16     moved_bytes,
 17     percent (moved_bytes, collected_bytes + moved_bytes),
 18     moved_clauses);
 19 
 20   // Prepare 'to' space of size 'moved_bytes'.
 21   //
 22   arena.prepare (moved_bytes);
 23 
 24   // Keep clauses in arena in the same order.
 25   //
 26   if (opts.arenacompact)
 27     for (const auto & c : clauses)
 28       if (!c->collect () && arena.contains (c))
 29         copy_clause (c);
 30 
 31   if (opts.arenatype == 1 || !watching ()) {
 32 
 33     // Localize according to current clause order.
 34 
 35     // If the option 'opts.arenatype == 1' is set, then this means the
 36     // solver uses the original order of clauses.  If there are no watches,
 37     // we can not use the watched based copying policies below.  This
 38     // happens if garbage collection is triggered during bounded variable
 39     // elimination.
 40 
 41     // Copy clauses according to the order of calling 'copy_clause', which
 42     // in essence just gives a compacting garbage collector, since their
 43     // relative order is kept, and actually already gives the largest
 44     // benefit due to better cache locality.
 45 
 46     for (const auto & c : clauses)
 47       if (!c->moved && !c->collect ())
 48         copy_clause (c);
 49 
 50   } else if (opts.arenatype == 2) {
 51 
 52     // Localize according to (original) variable order.
 53 
 54     // This is almost the version used by MiniSAT and descendants.
 55     // Our version uses saved phases too.
 56 
 57     for (int sign = -1; sign <= 1; sign += 2)
 58       for (auto idx : vars)
 59         for (const auto & w : watches (sign * likely_phase (idx)))
 60           if (!w.clause->moved && !w.clause->collect ())
 61             copy_clause (w.clause);
 62 
 63   } else {
 64 
 65     // Localize according to decision queue order.
 66 
 67     // This is the default for search. It allocates clauses in the order of
 68     // the decision queue and also uses saved phases.  It seems faster than
 69     // the MiniSAT version and thus we keep 'opts.arenatype == 3'.
 70 
 71     assert (opts.arenatype == 3);
 72 
 73     for (int sign = -1; sign <= 1; sign += 2)
 74       for (int idx = queue.last; idx; idx = link (idx).prev)
 75         for (const auto & w : watches (sign * likely_phase (idx)))
 76           if (!w.clause->moved && !w.clause->collect ())
 77             copy_clause (w.clause);
 78   }
 79 
 80   // Do not forget to move clauses which are not watched, which happened in
 81   // a rare situation, and now is only left as defensive code.
 82   //
 83   for (const auto & c : clauses)
 84     if (!c->collect () && !c->moved)
 85       copy_clause (c);
 86 
 87   flush_all_occs_and_watches ();
 88   update_reason_references ();
 89 
 90   // Replace and flush clause references in 'clauses'.
 91   //
 92   const auto end = clauses.end ();
 93   auto j = clauses.begin (), i = j;
 94   for (; i != end; i++) {
 95     Clause * c = *i;
 96     if (c->collect ()) delete_clause (c);
 97     else assert (c->moved), *j++ = c->copy, deallocate_clause (c);
 98   }
 99   clauses.resize (j - clauses.begin ());
100   if (clauses.size () < clauses.capacity ()/2) shrink_vector (clauses);
101 
102   if (opts.arenasort)
103     rsort (clauses.begin (), clauses.end (), pointer_rank ());
104 
105   // Release 'from' space completely and then swap 'to' with 'from'.
106   //
107   arena.swap ();
108 
109   PHASE ("collect", stats.collections,
110     "collected %zd bytes %.0f%% of %zd garbage clauses",
111     collected_bytes,
112     percent (collected_bytes, collected_bytes + moved_bytes),
113     collected_clauses);
114 }

 

 

13.

 

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.

译文:维护子句统计信息是复杂且容易出错的,但对于垃圾收集的适当调度是必要的,

特别是在有界变量消除期间。使用这个函数,我们可以检查这些统计信息是否被正确更新。

 
 1 /*------------------------------------------------------------------------*/
 2 
 3 // Maintaining clause statistics is complex and error prone but necessary
 4 // for proper scheduling of garbage collection, particularly during bounded
 5 // variable elimination.  With this function we can check whether these
 6 // statistics are updated correctly.
 7 
 8 void Internal::check_clause_stats () {
 9 #ifndef NDEBUG
10   int64_t irredundant = 0, redundant = 0, total = 0, irrbytes = 0;
11   for (const auto & c : clauses) {
12     if (c->garbage) continue;
13     if (c->redundant) redundant++; else irredundant++;
14     if (!c->redundant) irrbytes += c->bytes ();
15     total++;
16   }
17   assert (stats.current.irredundant == irredundant);
18   assert (stats.current.redundant == redundant);
19   assert (stats.current.total == total);
20   assert (stats.irrbytes == irrbytes);
21 #endif
22 }

 

 

14

   
 
1 /*------------------------------------------------------------------------*/
2 
3 bool Internal::arenaing () {
4   return opts.arena && (stats.collections > 1);
5 }

 

 

15.

 1 void Internal::garbage_collection () {
 2   if (unsat) return;
 3   START (collect);
 4   report ('G', 1);
 5   stats.collections++;
 6   mark_satisfied_clauses_as_garbage ();
 7   if (!protected_reasons) protect_reasons ();
 8   if (arenaing ()) copy_non_garbage_clauses ();
 9   else delete_garbage_clauses ();
10   check_clause_stats ();
11   check_var_stats ();
12   unprotect_reasons ();
13   report ('C', 1);
14   STOP (collect);
15 }

 

 
   

 

posted on 2020-07-30 10:31  海阔凭鱼跃越  阅读(119)  评论(0)    收藏  举报