Модель Z3 для правильного метода Дафни

Может ли Z3 для правильного метода найти модель условия проверки метода?

Я думал, что нет, но вот пример, где метод правильный

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

однако проверка находит модель.

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

Это было с Dafny 1.9.7.


person Theodore Norvell    schedule 08.10.2016    source источник
comment
Я не очень хорошо знаком с подключаемым модулем Dafny для Visual Studio, но разве красная точка не указывает на сбой проверки? Если это так, отладчик должен представить контрпример (для условия непройденной проверки), а не модель.   -  person Malte Schwerhoff    schedule 12.10.2016
comment
Да, красная точка указывает на сбой проверки. Отладчик представляет пример. (Это то, что я имел в виду под моделью.) Однако пример не является контрпримером, поскольку лемма верна. В частности, Pow(2, 902) действительно равен Pow(2*2, 902/2).   -  person Theodore Norvell    schedule 12.10.2016


Ответы (2)


То, что говорит Мальте, верно (и я также нашел, что это хорошо объяснено).

Dafny надежен в том смысле, что проверяет только правильные программы. Другими словами, если программа неверна, верификатор Dafny никогда не скажет, что она верна. Однако лежащие в основе проблемы принятия решений, как правило, неразрешимы. Поэтому неизбежно будут случаи, когда программа соответствует своим спецификациям, а верификатор все равно выдает сообщение об ошибке. Действительно, в таких случаях верификатор может даже показать предполагаемый контрпример. Это может быть ложный контрпример (как в приведенном выше примере) — это просто означает, что, насколько может сказать верификатор, это контрпример. Если проверяющий просто потратил немного больше времени или был достаточно умен, чтобы развернуть больше определений функций, применить индукционные гипотезы или сделать множество других хороших вещей, можно определить, что контрпример является поддельным. . Таким образом, любое полученное вами сообщение об ошибке (включая любой контрпример, который может сопровождать такое сообщение об ошибке) следует интерпретировать как возможную ошибку (и возможный контрпример).

Подобные ситуации часто возникают, если вы пытаетесь проверить правильность цикла и не предоставляете достаточно сильный инвариант цикла. Затем верификатор Dafny может показать некоторые значения переменных при входе в цикл, которые никогда не могут возникнуть в действительности. Затем контрпример пытается дать вам представление о том, как соответствующим образом усилить ваш инвариант цикла.

Наконец, позвольте мне добавить два замечания к тому, что сказал Мальте.

Во-первых, в этом примере есть по крайней мере еще один источник неполноты, а именно нелинейная арифметика. Иногда бывает сложно ориентироваться.

Во-вторых, трюк с использованием функции Dummy можно упростить. Достаточно (хотя бы в этом примере) упомянуть вызов Pow, например так:

lemma EvenPowerLemma(a: int, b: nat)
  requires Even(b)
  ensures Pow(a, b) == Pow(a*a, b/2)
{
  if b != 0 {
    var dummy := Pow(a, b - 2);
  }
}

Тем не менее, мне больше нравятся два других ручных доказательства, потому что они лучше объясняют пользователю, что такое доказательство.

Рустан

person Rustan Leino    schedule 17.10.2016
comment
Спасибо. Запросы Z3 приводят к одному из 4 результатов: unsatisfiable, satisfiable, unknown и time-out. Мое заблуждение заключалось в том, что я думал, что наличие модели подразумевает, что результат от Z3 был satisfiable. Чего я не понял, так это того, что когда результат равен unkown, Z3 также создает модель. На самом деле запросы Дафни возвращаются только как unsatisfiable, unknown или time-out, а модель в BVD подразумевает неизвестность. - person Theodore Norvell; 19.10.2016

Дафни не может доказать лемму из-за комбинации двух возможных источников неполноты: рекурсивных определений (здесь Pow) и индукции. Доказательство фактически терпит неудачу из-за слишком малого количества информации, то есть из-за того, что проблема недостаточно ограничена, что, в свою очередь, объясняет, почему можно найти контрпример.

Введение

Автоматизировать индукцию сложно, поскольку для этого требуется вычисление гипотезы индукции, что не всегда возможно. Однако у Дафни есть некоторые эвристики для применения индукции (которые могут работать или не работать), которые можно отключить, как в следующем коде:

lemma {:induction false} EvenPowerLemma_manual(a: int, b: nat)
  requires Even(b);
  ensures Pow(a, b) == Pow(a*a, b/2);
{
  if (b != 0) {
    EvenPowerLemma_manual(a, b - 2);
  }
}

При выключенной эвристике нужно вручную "вызвать" лемму, т.е. использовать предположение индукции (здесь только в случае, когда b >= 2), чтобы провести доказательство.

В вашем случае эвристики были активированы, но они не были «достаточно хороши», чтобы провести доказательство. Я объясню, почему дальше.

Рекурсивные определения

Статические рассуждения о рекурсивных определениях путем их развертывания склонны к бесконечному спуску, потому что в общем случае невозможно решить, когда остановиться. Следовательно, Dafny по умолчанию развертывает определения функций только один раз. В вашем примере развертывания определения Pow только один раз недостаточно, чтобы заставить работать эвристику индукции, потому что гипотеза индукции должна быть применена к Pow(a, b-2), что не «появляется» в доказательстве (поскольку развертывание только один раз приводит вас к Pow(a, b - 1)) . Однако явное упоминание Pow(a, b-2) в доказательстве, даже в бессмысленной формуле, запускает эвристику индукции:

function Dummy(a: int): bool
{ true }

lemma EvenPowerLemma(a: int, b: nat)
  requires Even(b);
  ensures Pow(a, b) == Pow(a*a, b/2);
{
  if (b != 0) {
    assert Dummy(Pow(a, b - 2));
  }
}

Функция Dummy предназначена для того, чтобы убедиться, что утверждение не предоставляет никакой информации, кроме синтаксического включения Pow(a, b-2). Менее странным было бы утверждение assert Pow(a, b) == a * a * Pow(a, b - 2).

Расчетное доказательство

К вашему сведению: вы также можете сделать шаги доказательства явными и попросить Дафни их проверить:

lemma {:induction false} EvenPowerLemma_manual(a: int, b: nat)
  requires Even(b);
  ensures Pow(a, b) == Pow(a*a, b/2);
{
  if (b != 0) {
    calc {
         Pow(a, b);
      == a * Pow(a, b - 1);
      == a * a * Pow(a, b - 2);
      == {EvenPowerLemma_manual(a, b - 2);}
         a * a * Pow(a*a, (b-2)/2);
      == Pow(a*a, (b-2)/2 + 1);
      == Pow(a*a, b/2);
    }
  }
}
person Malte Schwerhoff    schedule 13.10.2016
comment
Отличный ответ. Я многому научился благодаря этому. Меня удивило не то, что Dafny/Z3 не мог доказать теорему без некоторого кода в теле леммы; на самом деле меня бы удивило, если бы проверка прошла без кода в теле. Чего я не понимал и до сих пор не понимаю, так это почему найден контрпример, который на самом деле контрпримером не является. Обычно, когда BVD дает цифры, он на самом деле нашел контрпример, т. е. ввод, где в коде есть ошибка. В данном случае бага нет, так откуда берутся цифры 902 и 2? - person Theodore Norvell; 13.10.2016
comment
Мое понимание таково: Дафни не может доказать свойство, потому что оно разворачивает только Pow конечное число раз. Следовательно, существует приложение Pow, о котором ничего не известно/не предполагается и чье значение практически не ограничено. Таким образом, базовый решатель SMT может выбрать модель для Pow, которая не соответствует фактическому определению функции и, следовательно, может нарушить лемму. Конкретное число — здесь 902 и 2 — определяются эвристически, т. е. как бы выбираются наугад. - person Malte Schwerhoff; 14.10.2016