mirror of
https://github.com/YosysHQ/yosys
synced 2025-08-07 11:41:23 +00:00
smtbmc: Add --track-assumes and --minimize-assumes options
The --track-assumes option makes smtbmc keep track of which assumptions were used by the solver when reaching an unsat case and to output that set of assumptions. This is particularly useful to debug PREUNSAT failures. The --minimize-assumes option can be used in addition to --track-assumes which will cause smtbmc to spend additional solving effort to produce a minimal set of assumptions that are sufficient to cause the unsat result.
This commit is contained in:
parent
e4f11eb0a0
commit
42122e240e
3 changed files with 219 additions and 24 deletions
|
@ -114,6 +114,7 @@ class SmtModInfo:
|
|||
self.clocks = dict()
|
||||
self.cells = dict()
|
||||
self.asserts = dict()
|
||||
self.assumes = dict()
|
||||
self.covers = dict()
|
||||
self.maximize = set()
|
||||
self.minimize = set()
|
||||
|
@ -141,6 +142,7 @@ class SmtIo:
|
|||
self.recheck = False
|
||||
self.smt2cache = [list()]
|
||||
self.smt2_options = dict()
|
||||
self.smt2_assumptions = dict()
|
||||
self.p = None
|
||||
self.p_index = solvers_index
|
||||
solvers_index += 1
|
||||
|
@ -602,6 +604,12 @@ class SmtIo:
|
|||
else:
|
||||
self.modinfo[self.curmod].covers["%s_c %s" % (self.curmod, fields[2])] = fields[3]
|
||||
|
||||
if fields[1] == "yosys-smt2-assume":
|
||||
if len(fields) > 4:
|
||||
self.modinfo[self.curmod].assumes["%s_u %s" % (self.curmod, fields[2])] = f'{fields[4]} ({fields[3]})'
|
||||
else:
|
||||
self.modinfo[self.curmod].assumes["%s_u %s" % (self.curmod, fields[2])] = fields[3]
|
||||
|
||||
if fields[1] == "yosys-smt2-maximize":
|
||||
self.modinfo[self.curmod].maximize.add(fields[2])
|
||||
|
||||
|
@ -785,8 +793,13 @@ class SmtIo:
|
|||
return stmt
|
||||
|
||||
def check_sat(self, expected=["sat", "unsat", "unknown", "timeout", "interrupted"]):
|
||||
if self.smt2_assumptions:
|
||||
assume_exprs = " ".join(self.smt2_assumptions.values())
|
||||
check_stmt = f"(check-sat-assuming ({assume_exprs}))"
|
||||
else:
|
||||
check_stmt = "(check-sat)"
|
||||
if self.debug_print:
|
||||
print("> (check-sat)")
|
||||
print(f"> {check_stmt}")
|
||||
if self.debug_file and not self.nocomments:
|
||||
print("; running check-sat..", file=self.debug_file)
|
||||
self.debug_file.flush()
|
||||
|
@ -800,7 +813,7 @@ class SmtIo:
|
|||
for cache_stmt in cache_ctx:
|
||||
self.p_write(cache_stmt + "\n", False)
|
||||
|
||||
self.p_write("(check-sat)\n", True)
|
||||
self.p_write(f"{check_stmt}\n", True)
|
||||
|
||||
if self.timeinfo:
|
||||
i = 0
|
||||
|
@ -868,7 +881,7 @@ class SmtIo:
|
|||
|
||||
if self.debug_file:
|
||||
print("(set-info :status %s)" % result, file=self.debug_file)
|
||||
print("(check-sat)", file=self.debug_file)
|
||||
print(check_stmt, file=self.debug_file)
|
||||
self.debug_file.flush()
|
||||
|
||||
if result not in expected:
|
||||
|
@ -945,6 +958,48 @@ class SmtIo:
|
|||
def bv2int(self, v):
|
||||
return int(self.bv2bin(v), 2)
|
||||
|
||||
def get_raw_unsat_assumptions(self):
|
||||
self.write("(get-unsat-assumptions)")
|
||||
exprs = set(self.unparse(part) for part in self.parse(self.read()))
|
||||
unsat_assumptions = []
|
||||
for key, value in self.smt2_assumptions.items():
|
||||
# normalize expression
|
||||
value = self.unparse(self.parse(value))
|
||||
if value in exprs:
|
||||
exprs.remove(value)
|
||||
unsat_assumptions.append(key)
|
||||
return unsat_assumptions
|
||||
|
||||
def get_unsat_assumptions(self, minimize=False):
|
||||
if not minimize:
|
||||
return self.get_raw_unsat_assumptions()
|
||||
required_assumptions = {}
|
||||
|
||||
while True:
|
||||
candidate_assumptions = {}
|
||||
for key in self.get_raw_unsat_assumptions():
|
||||
if key not in required_assumptions:
|
||||
candidate_assumptions[key] = self.smt2_assumptions[key]
|
||||
|
||||
while candidate_assumptions:
|
||||
|
||||
candidate_key, candidate_assume = candidate_assumptions.popitem()
|
||||
|
||||
self.smt2_assumptions = {}
|
||||
for key, assume in candidate_assumptions.items():
|
||||
self.smt2_assumptions[key] = assume
|
||||
for key, assume in required_assumptions.items():
|
||||
self.smt2_assumptions[key] = assume
|
||||
result = self.check_sat()
|
||||
|
||||
if result == 'unsat':
|
||||
candidate_assumptions = None
|
||||
else:
|
||||
required_assumptions[candidate_key] = candidate_assume
|
||||
|
||||
if candidate_assumptions is not None:
|
||||
return list(required_assumptions)
|
||||
|
||||
def get(self, expr):
|
||||
self.write("(get-value (%s))" % (expr))
|
||||
return self.parse(self.read())[0][1]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue