В настоящее время я пишу автоматический верификатор для моего языка программирования поверх Z3 как забавный проект, и я пытаюсь использовать его, чтобы доказать, что реализация фибоначчи с использованием цикл эквивалентен рекурсии.Z3 не может найти удовлетворяющее назначение для простой формулы с квантификаторами и шаблонами
Кажется, что работает, если программа ввода правильная, т. Е. Генерирует разумный ввод для Z3, а Z3 говорит, что он неудовлетворителен, что означает в моем контексте, что программа верна. Но когда я изменил одну инициализацию переменной и, таким образом, сделал программу неправильной, мой верификатор придумал следующую формулу Z3, которая не кажется мне слишком сложной, но Z3 говорит «неизвестно».
(set-option :smt.auto-config false)
(set-option :smt.mbqi false)
(declare-fun fib (Int) Int)
(declare-fun fakeFib (Int) Int)
(declare-fun n() Int)
(assert (forall ((n Int))
(! (= (fib n)
(ite (= n 0) 0
(ite (= n 1) 1
(+ (fakeFib (- n 1)) (fakeFib (- n 2))))))
:pattern ((fib n)))))
(assert (forall ((n Int))
(! (= (fib n) (fakeFib n)) :pattern ((fib n)))))
(assert (>= n 0))
(assert (not (and (<= 0 n) (= 1 (fib 1)) (= 1 (fib 0)))))
(check-sat)
Обратите внимание, что два квантора представляют собой рекурсивное определение фибоначчи. Один мой друг сказал мне этот трюк, чтобы избежать совпадающего цикла: вместо определения fib как рекурсивной функции я представляю еще одну функцию fakeFib, для которой я не предоставляю определение, и использую его в рекурсивном определении fib. Кроме того, я говорю верификатору, что они равны, но этот квантификатор получает только шаблон для fib, а не для fakeFib. Из-за ограничительных паттернов он является неполным, т. Е. Он может доказать только правильность программы, если достаточно проверить один уровень рекурсии, чтобы доказать это (но его можно легко расширить до k уровней). Но я могу жить с этим ограничением, чтобы избежать совпадения циклов.
«Ошибка» кода заключалась в том, что я инициализировал переменную неправильно, и это в конечном итоге приведет к неправильному компоненту (= 1 (fib 0))
в последнем утверждении. Для правильной программы это будет (= 0 (fib 0))
.
Некоторые наблюдения:
- Если я заменяю
(= 1 (fib 0))
на(= 0 (fib 0))
, то Z3 сразу находит, что это невыполнимо. - Раньше у меня не было опций
(set-option :smt.auto-config false)
и(set-option :smt.mbqi false)
, а затем у меня закончилась память на моей машине и вне времени на подъеме4fun. - Установка
(set-option :smt.macro-finder true)
, которая, похоже, работала для людей с подобными вопросами, не работала для меня. Я предполагаю, что это вызвано тем, что у меня есть два квантора дляfib
, а не только один. - Я попытался использовать cvc4 для сравнения, и он сразу же сказал «неизвестно». Поэтому мои проблемы не кажутся специфичными для Z3.
- Формула явно выполнима.
(= 1 (fib 0))
-false
, поэтому все последнее утверждение автоматически верное.(>= n 0)
также легко удовлетворить.
Hm. ОК. Думаю, это имеет смысл. Я предполагаю, что с MBQI он, возможно, продолжил функцию fib навсегда и вышел из памяти/времени таким образом. Это означает, что я вообще не могу использовать модели и просто должен полагаться на «unsat => correct program» и «sat => неправильную программу, и я знаю, что пошло не так» и «unknown => вероятно неправильная программа, но не знаю, что пошло не так « – Lykos
Я отредактировал свой ответ, надеюсь, это поможет. – iago
Прохладный, спасибо за это объяснение. Я думаю, в моей первой версии я просто отвергнув все, что заканчивается «неизвестно», и скажу, что пользователь «не смог доказать x», а в более поздних версиях я добавлю индукцию k. :) Ключевое слово само по себе было уже очень полезно для меня, так как я нашел несколько интересных статей об этом. – Lykos