-
Notifications
You must be signed in to change notification settings - Fork 1
Finalize FLP #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finalize FLP #48
Changes from all commits
733d9f6
22f2542
ee96542
dfe0224
23f9e20
ad19bb2
f0ea5af
8d5db5e
0a61658
ffc877e
6dd617f
ef3114f
1b03347
4c7a8cc
6fbd570
edffce5
23fb61e
c528202
e8cfaf1
f9da662
3ee9642
5cf3f23
cdf2bb2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -127,6 +127,12 @@ class CDCL { | |
polarity.add(LBool.UNDEF) | ||
} | ||
|
||
/** | ||
* Return a value of the given literal, assuming it is not substituted. | ||
* It is a shortcut for [Assignment.value]. | ||
* | ||
* @see Assignment.value | ||
*/ | ||
private fun value(lit: Lit): LBool { | ||
return assignment.value(lit) | ||
} | ||
|
@@ -169,7 +175,9 @@ class CDCL { | |
} | ||
} | ||
|
||
else -> attachClause(clause) | ||
// Clauses of size >2 are added to the database. | ||
// We don't add externally provided clauses to the proof. | ||
else -> attachClause(clause, addToDrat = false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line requires a comment, since other two branches have them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually a workaround, and a pretty ugly one. I don't like how current There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did that initially, but it turns out it only makes things difficult. During preprocessing we often need to modify non-learnt clauses, which means it has to be recorded in the proof. However, we cannot make new clause learnt because it is irredundant, so non-learnts have to be added to the proof manually. Even worse, this means that there is no way to distinguish irredundant and initially given clauses later, so those added irredundant clauses are permanently staying in the proof. I can either store separate flags in |
||
} | ||
} | ||
|
||
|
@@ -419,10 +427,12 @@ class CDCL { | |
} | ||
|
||
for (varIndex in 0 until assignment.numberOfVariables) { | ||
cachedModel!!.add(when (assignment.valueAfterSubstitution(Var(varIndex))) { | ||
LBool.TRUE, LBool.UNDEF -> true | ||
LBool.FALSE -> false | ||
}) | ||
cachedModel!!.add( | ||
when (assignment.valueAfterSubstitution(Var(varIndex))) { | ||
LBool.TRUE, LBool.UNDEF -> true | ||
LBool.FALSE -> false | ||
} | ||
) | ||
} | ||
|
||
return cachedModel!! | ||
|
@@ -495,7 +505,7 @@ class CDCL { | |
if (watched.deleted) continue | ||
if (watched.size != 2) continue | ||
|
||
val other = watched.otherWatch(lit.neg) | ||
val other = Lit(watched[0].inner xor watched[1].inner xor lit.neg.inner) | ||
|
||
check(value(other) != LBool.FALSE) | ||
if (value(other) != LBool.UNDEF) continue | ||
|
@@ -672,9 +682,6 @@ class CDCL { | |
// We simply remove such clauses. | ||
if (!containsComplementary) { | ||
val newClause = Clause(newLits, clause.learnt) | ||
// Only learnt, non-unit clauses are added to the proof automatically, | ||
// so we have to add the rest manually | ||
if (newClause.size == 1 || !clause.learnt) dratBuilder.addClause(newClause) | ||
if (newClause.size == 1) { | ||
check(assignment.enqueue(newClause[0], null)) | ||
} else { | ||
|
@@ -836,27 +843,29 @@ class CDCL { | |
} else if (firstNotFalse == -1) { | ||
// we deduced this literal from a non-binary clause, | ||
// so we can learn a new clause | ||
val newBinary = hyperBinaryResolve(clause) | ||
var newBinary = hyperBinaryResolve(clause) | ||
|
||
// Check that lit in either at index 0 of the clause and negated, | ||
// or not in the clause at all. | ||
check( | ||
newBinary[0] == lit.neg || | ||
lit.variable != newBinary[0].variable && lit.variable != newBinary[1].variable | ||
) | ||
|
||
// The new clause may subsume the old one, rendering it useless | ||
// TODO: However, we don't know if this clause is learned or given, | ||
// so we can't let it be deleted. On the other hand, we can't | ||
// allow just adding it to the list of clauses to keep, because | ||
// this may result in too many clauses being kept. | ||
// This should be reworked with the new clause storage mechanism, | ||
// and new flags for clauses. Won't fix for now. | ||
// TODO: this turns out to be more difficult than I expected and | ||
// requires further investigation. | ||
// if (newBinary[0] in clause.lits) { | ||
// clause.deleted = true | ||
// clausesToKeep.removeLast() | ||
// newBinary.learnt = clause.learnt | ||
// } | ||
|
||
attachClause(newBinary) | ||
if (newBinary[0] in clause.lits) { | ||
// We don't need to keep the clause in the watcher list | ||
clausesToKeep.removeLast() | ||
newBinary = newBinary.copy(learnt = clause.learnt) | ||
attachClause(newBinary) | ||
markDeleted(clause) | ||
} else { | ||
// If not, simply add the new clause | ||
attachClause(newBinary) | ||
} | ||
|
||
// Make sure watch is not overwritten at the end of the loop | ||
if (lit == newBinary[0]) clausesToKeep.add(newBinary) | ||
if (lit.neg == newBinary[0]) clausesToKeep.add(newBinary) | ||
|
||
assignment.uncheckedEnqueue(clause[0], newBinary) | ||
// again, we try to only use binary clauses first | ||
|
@@ -892,7 +901,7 @@ class CDCL { | |
if (clause.deleted) continue | ||
if (clause.size != 2) continue | ||
|
||
val other = clause.otherWatch(lit.neg) | ||
val other = Lit(clause[0].inner xor clause[1].inner xor lit.neg.inner) | ||
|
||
when (value(other)) { | ||
// if the other literal is true, the | ||
|
@@ -995,11 +1004,11 @@ class CDCL { | |
if (assignment.trailIndex(lcaVar) > assignment.trailIndex(litVar)) { | ||
check(assignment.reason(lcaVar)!!.size == 2) | ||
val (a, b) = assignment.reason(lcaVar)!!.lits | ||
lca = lca.differentAmong2(a, b).neg | ||
lca = Lit(lca.inner xor a.inner xor b.inner).neg | ||
} else { | ||
check(assignment.reason(litVar)!!.size == 2) | ||
val (a, b) = assignment.reason(litVar)!!.lits | ||
lit = lit.differentAmong2(a, b).neg | ||
lit = Lit(lit.inner xor a.inner xor b.inner).neg | ||
} | ||
} | ||
} | ||
|
@@ -1011,13 +1020,13 @@ class CDCL { | |
/** | ||
* Add [clause] into the database and attach watchers for it. | ||
*/ | ||
private fun attachClause(clause: Clause) { | ||
private fun attachClause(clause: Clause, addToDrat: Boolean = true) { | ||
require(clause.size >= 2) { clause } | ||
check(ok) | ||
db.add(clause) | ||
watchers[clause[0]].add(clause) | ||
watchers[clause[1]].add(clause) | ||
if (clause.learnt) dratBuilder.addClause(clause) | ||
if (addToDrat) dratBuilder.addClause(clause) | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is a part of public API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it should be :)
How else are you going to extract values of literals after solving the SAT? "Build a model" is not a right answer to this: (1) model might be too large (consider the situation when you really need the value of only one literal out of 1kk variables), (2) in the model you replace
LBool.Undef
withtrue
, basically losing the information about the literal state.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While it is possible to correctly return
LBool.Undef
right now, things might get interesting soon.Hilariously, "build a model" might be the right answer to this. I don't know the details of how this is implemented in MiniSat, but in Cadical, before every call to
External::ival
, it checks if the assignment have been "extended" (External::extended
), and if not, it "extends" it. By "extension" authors mean "reconstruction". This operation is pretty much identical to building the model in our solver.Consider BVE. The only way to get the value of eliminated variable is to traverse back the clauses which got resolved and check if those are satisfied. This is process takes O(length of all resolved clauses), worse case, and there is no way around it. It is true that "model might be too large", but reconstruction takes O(see above), a pretty trivial one, actually, while the solving takes however long it needs. The current implementation with substitution is potentially even faster (but I'm going to get rid of it soon). I think there is no reason in trying to optimize out reconstruction.
True, but this only triggers if the state of the solver is trivial (i.e. all clauses got simplified away). If we got to the search phase, we need the entire trail of assignments. I am willing to ignore that. From an API standpoint, I don't
see a point of exposing values of partial assignments: those are private to the solver. Finally, in Cadical,
Solver::ival
also returns eithertrue
orfalse
(kind of).