luet/vendor/github.com/crillab/gophersat/solver/learn.go
Ettore Di Giacinto d7a04465fd update vendor/
2020-11-03 17:21:32 +01:00

127 lines
3.7 KiB
Go

package solver
// computeLbd computes and sets c's LBD (Literal Block Distance).
func (c *Clause) computeLbd(model Model) {
c.setLbd(1)
curLvl := abs(model[c.Get(0).Var()])
for i := 0; i < c.Len(); i++ {
lit := c.Get(i)
if lvl := abs(model[lit.Var()]); lvl != curLvl {
curLvl = lvl
c.incLbd()
}
}
}
// addClauseLits is a helper function for learnClause.
// It deals with lits from the conflict clause.
func (s *Solver) addClauseLits(confl *Clause, lvl decLevel, met, metLvl []bool, lits *[]Lit) int {
nbLvl := 0
for i := 0; i < confl.Len(); i++ {
l := confl.Get(i)
v := l.Var()
if s.litStatus(l) != Unsat {
// In clauses where cardinality > 1, some lits might be true in the conflict clause: ignore them
continue
}
met[v] = true
s.varBumpActivity(v)
if abs(s.model[v]) == lvl {
metLvl[v] = true
nbLvl++
} else {
*lits = append(*lits, l)
}
}
return nbLvl
}
var bufLits = make([]Lit, 10000) // Buffer for lits in learnClause. Used to reduce allocations.
// learnClause creates a conflict clause and returns either:
// - the clause itself, if its len is at least 2,
// - a nil clause and a unit literal, if its len is exactly 1,
// - a nil clause and -1, if the empty clause was learned.
func (s *Solver) learnClause(confl *Clause, lvl decLevel) (learned *Clause, unit Lit) {
s.clauseBumpActivity(confl)
lits := bufLits[:1] // Not 0: make room for asserting literal
buf := make([]bool, s.nbVars*2) // Buffer for met and metLvl; reduces allocs/deallocs
met := buf[:s.nbVars] // List of all vars already met
metLvl := buf[s.nbVars:] // List of all vars from current level to deal with
// nbLvl is the nb of vars in lvl currently used
nbLvl := s.addClauseLits(confl, lvl, met, metLvl, &lits)
ptr := len(s.trail) - 1 // Pointer in propagation trail
for nbLvl > 1 { // We will stop once we only have one lit from current level.
for !metLvl[s.trail[ptr].Var()] {
if abs(s.model[s.trail[ptr].Var()]) == lvl { // This var was deduced afterwards and was not a reason for the conflict
met[s.trail[ptr].Var()] = true
}
ptr--
}
v := s.trail[ptr].Var()
if s.assumptions[v] {
// Now we only have assumed lits: this is a top-level conflict
return nil, -1
}
ptr--
nbLvl--
if reason := s.reason[v]; reason != nil {
s.clauseBumpActivity(reason)
for i := 0; i < reason.Len(); i++ {
lit := reason.Get(i)
if v2 := lit.Var(); !met[v2] {
if s.litStatus(lit) != Unsat { // In clauses where cardinality > 1, some lits might be true in the conflict clause: ignore them
continue
}
met[v2] = true
s.varBumpActivity(v2)
if abs(s.model[v2]) == lvl {
metLvl[v2] = true
nbLvl++
} else {
lits = append(lits, lit)
}
}
}
}
}
for _, l := range s.trail { // Look for last lit from lvl and use it as asserting lit
if metLvl[l.Var()] {
lits[0] = l.Negation()
break
}
}
s.varDecayActivity()
s.clauseDecayActivity()
sortLiterals(lits, s.model)
sz := s.minimizeLearned(met, lits)
if sz == 1 {
return nil, lits[0]
}
learned = NewLearnedClause(alloc.newLits(lits[0:sz]...))
learned.computeLbd(s.model)
return learned, -1
}
// minimizeLearned reduces (if possible) the length of the learned clause and returns the size
// of the new list of lits.
func (s *Solver) minimizeLearned(met []bool, learned []Lit) int {
sz := 1
for i := 1; i < len(learned); i++ {
if reason := s.reason[learned[i].Var()]; reason == nil {
learned[sz] = learned[i]
sz++
} else {
for k := 0; k < reason.Len(); k++ {
lit := reason.Get(k)
if !met[lit.Var()] /*&& abs(s.model[lit.Var()]) > 1*/ {
learned[sz] = learned[i]
sz++
break
}
}
}
}
return sz
}