Исследование государственного пространства в источнике NuSMV
Я работаю над программой коррекции / синтеза проекта. Моя задача - получить трассировку ошибок (контрпример), найти ее в полном пространстве состояний и восстановить модель в этом месте. Я хочу реализовать это как расширение NuSMV.
Я отлаживал NuSMV, чтобы понять и изучить его исходный код. До сих пор я нашел свой путь к созданию BDD FSM (строка compile.c 520). Я пытаюсь найти способ пройти через bdd, чтобы получить программный доступ к пространству состояний и, таким образом, выполнить свою корректирующую работу над моделью. Я еще не смог понять функции рекурсивного исследования, которые NuSMV использует для проверки свойств через bdd fsm.
Я хотел бы знать, как я могу пройти через структуру BDD, чтобы я мог визуализировать ее с помощью инструментов, таких как точка. Я также хотел бы знать, были ли уже сделаны такие или смиларные визуализации (я искал, но оказался пустым). Во-вторых, я хотел бы проверить, является ли правильное направление, которое я выбрал, или есть ли лучший способ получить полное пространство состояний данной модели, и исследовать его, особенно в отношении контрпримеров, полученных через NuSMV.
0 ответов
Это ответ о способах работы с двоичными диаграммами решений (BDD) с использованием CUDD, отличных от NuSMV, поэтому сосредоточимся на второй части вопроса.
Что касается символьных алгоритмов исследования пространства состояний, то хорошим введением является статья "Алгоритмическая проверка спецификаций линейной темпоральной логики" Кестена, Пнуэли и Равива (ICALP '98, DOI: 10.1007 / BFb0055036), в которой рассматривается построение контрпримеров.
Одна из возможностей визуализации BDD - это работа на Python с использованием привязок Cython к CUDD:
from dd import cudd
def dump_pdf_cudd():
bdd = cudd.BDD()
bdd.declare('x', 'y', 'z')
u = bdd.add_expr('(x /\ y) \/ ~ z')
bdd.dump('foo.pdf', [u])
if __name__ == '__main__':
dump_pdf_cudd()
Этот подход использует dd
, который можно установить с помощью pip
, с pip install dd
. Документацию можно найти здесь. Глядя на (внутренний) модуль dd._abc
также может быть информативным (это служит спецификацией; имя "abc" относится к абстрактным базовым классам в Python).
(Чистый Python подходит для небольших задач, а CUDD полезен для более крупных).
К вопросу относятся два вида обходов:
- обход графа BDD
- обход графа, который имеет состояния как узлы и шаги как ребра.
Они обсуждаются отдельно ниже.
Обход графа BDD
При работе с BDD обход в глубину более распространен, чем в первую очередь на вдохе. Для интерфейсаdd.cudd
а также dd.autoref
такой обход:
from dd import autoref as _bdd
def demo_bdd_traversal():
bdd = _bdd.BDD()
bdd.declare('x', 'y')
u = bdd.add_expr('x /\ y')
print_bdd_nodes(u)
def print_bdd_nodes(u):
visited = set()
_print_bdd_nodes(u, visited)
def _print_bdd_nodes(u, visited):
# terminal ?
if u.var is None:
print('terminal reached')
return
# non-terminal
# already visited ?
if u in visited:
return
# recurse
v = u.low
w = u.high
# DFS pre-order
print('found node {u}'.format(u=u))
_print_bdd_nodes(v, visited)
# DFS in-order
print('back to node {u}'.format(u=u))
_print_bdd_nodes(w, visited)
# DFS post-order
print('leaving node {u}'.format(u=u))
# memoize
visited.add(u)
if __name__ == '__main__':
demo_bdd_traversal()
Дополненные края также необходимо учитывать при работе с BDD (с использованием CUDD или подобных библиотек). Атрибутu.negated
предоставляет эту информацию.
Функция dd.bdd.copy_bdd
- это чистый Python-пример обхода BDD. Эта функция управляет BDD напрямую через "низкоуровневый" интерфейс, который обернутdd.autoref
выглядеть так же, как dd.cudd
.
Обход графа состояний
Сценарий dd/examples/reachability.py
показывает, как вычислить, из каких состояний можно достичь заданного набора состояний за конечное число шагов.
Пакет omega
удобнее чем dd
для разработки алгоритмов на основе BDD, связанных с поведением системы. Сценарий omega/examples/reachability_solver
демонстрирует вычисление достижимости с использованием omega
.
Базовый пример прямой достижимости с использованиемomega == 0.3.1
следует:
from omega.symbolic import temporal as trl
from omega.symbolic import prime as prm
def reachability_example():
"""How to find what states are reachable."""
aut = trl.Automaton()
vrs = dict(
x=(0, 10),
y=(3, 50))
aut.declare_variables(**vrs)
aut.varlist = dict(
sys=['x', 'y'])
aut.prime_varlists()
s = r'''
Init ==
/\ x = 0
/\ y = 45
Next ==
/\ (x' = IF x < 10 THEN x + 1 ELSE 0)
/\ (y' = IF y > 5 THEN y - 1 ELSE 45)
'''
aut.define(s)
init = aut.add_expr('Init', with_ops=True)
action = aut.add_expr('Next', with_ops=True)
reachable = reachable_states(init, action, vrs, aut)
n = aut.count(reachable, care_vars=['x', 'y'])
print('{n} states are reachable'.format(n=n))
def reachable_states(init, action, vrs, aut):
"""States reachable by `action` steps, starting from `init`."""
operator = lambda y: image(y, action, vrs, aut)
r = least_fixpoint(operator, init)
assert prm.is_state_predicate(r)
return r
def image(source, action, vrs, aut):
"""States reachable from `source` in one step that satisfies `action`."""
u = source & action
u = aut.exist(vrs, u)
return aut.replace_with_unprimed(vrs, u)
def least_fixpoint(operator, target):
"""Least fixpoint of `operator`, starting from `target`."""
y = target
yold = None
while y != yold:
yold = y
y |= operator(y)
return y
if __name__ == '__main__':
reachability_example()
Сравнение omega
с dd
:
omega
поддерживает переменные и константы, а также целочисленные значения для них (соответствующий модульomega.symbolic.temporal
). Переменные представляют изменения состояния, напримерx
а такжеx'
. Константы остаются неизменными в зависимости от поведения системы.dd
поддерживает только переменные с логическими значениями (omega
использует логические переменные для представления целочисленных переменных и, таким образом, представляет предикаты как BDD черезdd
; битбластинг выполняется в модулеomega.logic.bitvector
).
Несколько операторов фиксированных точек реализованы в omega.symbolic.fixpoint
. Эти операторы могут использоваться для проверки модели или временного синтеза. Модульomega.logic.past
включает переводы темпоральных операторов, которые имеют отношение к символьной проверке модели (также известные как временные тестеры).
Документация omega
можно найти здесь.
Дальнейшие комментарии
Выше я использовал термин "шаг" для обозначения пары последовательных состояний, которые представляют изменение состояния, разрешенное спецификацией. Язык и шаги TLA+, гибкие и жесткие переменные, а также другие полезные концепции описаны в книге Лесли Лэмпорта " Спецификация систем".
Набор программного обеспечения для формальной проверки приведен по адресу https://github.com/johnyf/tool_lists/.
По моему опыту, работа на уровне Python с использованием только менеджера BDD на уровне C - это эффективный подход, который приводит к читаемому коду и более четким алгоритмам.