bdd_to_formula: add CNF variant
* spot/twa/formula2bdd.hh, spot/twa/formula2bdd.cc (bdd_to_cnf_formula): New function. * python/spot/__init__.py: Add a default dictionary for convenience. * tests/python/bdditer.py: Add test cases. * NEWS: Mention it.
This commit is contained in:
parent
8a5b86521c
commit
66839b1a29
5 changed files with 111 additions and 16 deletions
|
|
@ -1,5 +1,5 @@
|
|||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017, 2018, 2021, 2022 Laboratoire de Recherche et
|
||||
# Copyright (C) 2017, 2018, 2021, 2022, 2023 Laboratoire de Recherche et
|
||||
# Développement de l'Epita (LRDE).
|
||||
#
|
||||
# This file is part of Spot, a model checking library.
|
||||
|
|
@ -27,6 +27,19 @@ import sys
|
|||
from unittest import TestCase
|
||||
tc = TestCase()
|
||||
|
||||
# CPython use reference counting, so that automata are destructed
|
||||
# when we expect them to be. However other implementations like
|
||||
# PyPy may call destructors latter, causing different output.
|
||||
from platform import python_implementation
|
||||
if python_implementation() == 'CPython':
|
||||
def gcollect():
|
||||
pass
|
||||
else:
|
||||
import gc
|
||||
def gcollect():
|
||||
gc.collect()
|
||||
|
||||
|
||||
run = spot.translate('a & !b').accepting_run()
|
||||
b = run.prefix[0].label
|
||||
c = buddy.bdd_satone(b)
|
||||
|
|
@ -43,12 +56,15 @@ while c != buddy.bddtrue:
|
|||
c = h
|
||||
|
||||
tc.assertEqual(res, [0, -1])
|
||||
del res
|
||||
|
||||
res2 = []
|
||||
for i in run.aut.ap():
|
||||
res2.append((str(i), run.aut.register_ap(i)))
|
||||
tc.assertEqual(str(res2), "[('a', 0), ('b', 1)]")
|
||||
|
||||
del res2
|
||||
del c
|
||||
gcollect()
|
||||
|
||||
f = spot.bdd_to_formula(b)
|
||||
tc.assertTrue(f._is(spot.op_And))
|
||||
|
|
@ -56,9 +72,50 @@ tc.assertTrue(f[0]._is(spot.op_ap))
|
|||
tc.assertTrue(f[1]._is(spot.op_Not))
|
||||
tc.assertTrue(f[1][0]._is(spot.op_ap))
|
||||
tc.assertEqual(str(f), 'a & !b')
|
||||
del f
|
||||
gcollect()
|
||||
|
||||
try:
|
||||
f = spot.bdd_to_formula(b, spot.make_bdd_dict())
|
||||
sys.exit(2)
|
||||
except RuntimeError as e:
|
||||
tc.assertIn("not in the dictionary", str(e))
|
||||
|
||||
f = spot.bdd_to_cnf_formula(b)
|
||||
tc.assertEqual(str(f), 'a & !b')
|
||||
|
||||
del run
|
||||
del f
|
||||
|
||||
gcollect()
|
||||
|
||||
f = spot.bdd_to_cnf_formula(buddy.bddtrue)
|
||||
tc.assertEqual(str(f), '1')
|
||||
del f
|
||||
gcollect()
|
||||
|
||||
f = spot.bdd_to_cnf_formula(buddy.bddfalse)
|
||||
tc.assertEqual(str(f), '0')
|
||||
del f
|
||||
gcollect()
|
||||
|
||||
aut = spot.translate('(a & b) <-> c')
|
||||
# With pypy, running GC here will destroy the translator object used
|
||||
# by translate(). That object has temporary automata that reference
|
||||
# the BDDs variables and those affect the order in which the
|
||||
# bdd_to_formula() result is object is presented. The different order
|
||||
# is not wrong, but it makes it diffuclt to write tests.
|
||||
gcollect()
|
||||
|
||||
for e in aut.out(aut.get_init_state_number()):
|
||||
b = e.cond
|
||||
break
|
||||
|
||||
f1 = spot.bdd_to_formula(b)
|
||||
tc.assertEqual(str(f1), '(!a & !c) | (a & b & c) | (!b & !c)')
|
||||
f2 = spot.bdd_to_cnf_formula(b)
|
||||
tc.assertEqual(str(f2), '(a | !c) & (!a | !b | c) & (b | !c)')
|
||||
|
||||
b1 = spot.formula_to_bdd(f1, spot._bdd_dict, aut)
|
||||
b2 = spot.formula_to_bdd(f2, spot._bdd_dict, aut)
|
||||
tc.assertEqual(b1, b2)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue