Исследование пространства состояний в исходном коде NuSMV

Я работаю над проектом коррекции/синтеза программы. Моя задача — получить трассировку ошибки (контрпример), найти ее в полном пространстве состояний и восстановить модель в этом месте. Я хочу реализовать это как расширение NuSMV.

Я занимался отладкой NuSMV, чтобы понять и изучить его исходный код. Пока что я нашел свой путь к созданию BDD FSM (строка 520 compile.c). Я пытаюсь найти способ обойти bdd, чтобы получить программный доступ к пространству состояний и, таким образом, выполнить корректирующую работу над моделью. Я еще не смог понять функции рекурсивного исследования, которые NuSMV использует для проверки свойств через bdd fsm.

Я хотел бы знать, как я могу пройти через структуру bdd, чтобы я мог визуализировать ее с помощью таких инструментов, как точка. Я также хотел бы знать, были ли уже сделаны такие или подобные визуализации (я искал, но ничего не нашел). Во-вторых, я хотел бы проверить, является ли направление, которое я выбрал, правильным, или есть ли лучший способ получить полное пространство состояний данной модели и изучить его, особенно в отношении полученного контрпримера. через НюСМВ.


person Brishna Batool    schedule 11.12.2017    source источник


Ответы (1)


Это ответ о способах работы с бинарными диаграммами решений (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 достаточно для небольших задач, а CUDD полезен для более крупных задач).

введите здесь описание изображения

Есть два вида обходов, имеющих отношение к вопросу:

  1. обход графа BDD
  2. обход графа, который имеет состояния в качестве узлов и шаги в качестве ребер.

Они обсуждаются отдельно ниже.

Обход графа 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 является эффективным подходом, который приводит к читабельному коду и более четким алгоритмам.

person Ioannis Filippidis    schedule 05.09.2020