Тот же возврат, но одна функция не может правильно утверждать значения, а другая - нет в Дафни

У меня есть 2 функции, все для проверки того, сколько значений являются одинаковыми значениями и одинаковым индексом в 2 последовательностях, например:

  • требует, чтобы в последовательности не было дубликатов с:= [1,3,2,5,6] и:= [2,3,4,5,1]

==> Bullspec(s,u) = 2

Итак, обе мои 2 функции возвращают правильное значение, но утверждение одного истинно, другое ложно. Вот мои 2 функции:

function bullspec(s:seq<nat>, u:seq<nat>): nat
  requires 0 < |s| <= 10
  requires 0 < |u| <= 10
  requires |s| <= |u|
  // Remove duplicates
  requires forall i, j | 0 <= i < |s| && 0 <= j < |s| && i != j :: s[i] != s[j] && s[i] <= 10
  requires forall i, j | 0 <= i < |u| && 0 <= j < |u| && i != j :: u[i] != u[j] && u[i] <= 10
{
  if |s| == 1 then (
    if s[0] in u && s[0] == u[0] 
    then 1 else 0
  ) else (
    if s[|s|-1] in u && s[|s|-1]==u[|s|-1] 
    then (1 + bullspec(s[..|s|-1], u))
    else bullspec(s[..|s|-1],u)
  )
}

и

function bullspec2(s:seq<nat>, u:seq<nat>): nat
  requires 0 < |s| <= 10
  requires 0 < |u| <= 10
  requires |s| <= |u|
  // Remove duplicates
  requires forall i, j | 0 <= i < |s| && 0 <= j < |s| && i != j :: s[i] != s[j] && s[i] <= 10
  requires forall i, j | 0 <= i < |u| && 0 <= j < |u| && i != j :: u[i] != u[j] && u[i] <= 10
{
  if |s| == 1 then (
    if s[0] in u && s[0] == u[0] 
    then 1 else 0
  ) else (
    if s[0] in u && s[0] == u[0]
    then (1 + bullspec2(s[1..], u))
    else bullspec2(s[1..], u)
  )
}

У меня есть метод Main ниже:

method Main()
{
  var sys:seq<nat> := [4,2,9,3,1];
  var usr:seq<nat> := [1,2,3,4,5];

  assert bullspec(sys, usr) == 1; //Assertion might not hold
  assert bullspec2(sys, usr) == 1; //This is good
}

Разница между двумя функциями заключается в рекурсивном цикле, одна начинается с начала, другая начинается с конца, и каким-то образом обратное утверждение make работает хорошо.

Я пытаюсь написать несколько утверждений, но ничего не получается.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
72
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

Что вы можете сделать на данный момент:

Придумайте лемму и докажите ее (сложно)

lemma bullspec2equalsBullspec(s:seq<nat>, u:seq<nat>): nat
  ensures  bullspec2(sys, usr) == bullspec(sys, usr)
{
  // TODO
}

а затем напишите:

assert bullspec2(sys, usr) == 1;
assert bullspec(sys, usr) == 1 by {
  bullspec2equalsBullspec(sys, usr);
}

Передайте длину последовательности в качестве параметра-призрака в Bullspec

function bullspec(s:seq<nat>, u:seq<nat>, length: nat): nat
  requires |s| == length
  requires 0 < |s| <= 10
  requires 0 < |u| <= 10
  requires |s| <= |u|
  // Remove duplicates
  requires forall i, j | 0 <= i < |s| && 0 <= j < |s| && i != j :: s[i] != s[j] && s[i] <= 10
  requires forall i, j | 0 <= i < |u| && 0 <= j < |u| && i != j :: u[i] != u[j] && u[i] <= 10
{
  if length == 1 then (
    if s[0] in u && s[0] == u[0] 
    then 1 else 0
  ) else (
    if s[length-1] in u && s[length-1]==u[length-1] 
    then (1 + bullspec(s[..length-1], u,length-1))
    else bullspec(s[..length-1],u,length-1)
  )
}
/// ...
method Main()
{
  var sys:seq<nat> := [4,2,9,3,1];
  var usr:seq<nat> := [1,2,3,4,5];

  assert bullspec2(sys, usr) == 1; //This is good
  assert bullspec(sys, usr, 5) == 1; //This is good
}

проверяет, потому что теперь он может развернуть функцию, применяемую к литералам, без топлива.

Разверните bullspec с помощью проверочной отладки, чтобы увидеть, где он блокируется.

Если это утверждение должно быть верным, то что должно быть верным раньше? Вы можете развернуть определение функции и утвердить промежуточные результаты. Если вы утверждаете что-то нетривиальное, что Дафни сможет наконец доказать, все остальное будет доказано. Я развернул функцию всего за 3 шага.


method Main()
{
  var sys:seq<nat> := [4,2,9,3,1];
  var usr:seq<nat> := [1,2,3,4,5];

  assert bullspec2(sys, usr) == 1; //This is good

  var sys1 := sys[..|sys|-1];
  var sys2 := sys1[..|sys1|-1];
  var sys3 := sys2[..|sys2|-1];
  var sys4 := sys3[..|sys3|-1];
  var sys5 := sys4[..|sys4|-1];

  assert bullspec(sys3, usr) == 1; 
  assert bullspec(sys2, usr) == 1; 
  assert bullspec(sys1, usr) == 1; 
  assert bullspec(sys, usr) == 1; //Assertion might not hold
}

Дайте больше топлива вашей функции (лучше всего в вашем случае)

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

function {:fuel 10} bullspec(s:seq<nat>, u:seq<nat>): nat

Спасибо за ваш ответ, я думаю, что создам лемму, потому что мое требование не изменяет какую-либо подпись функции.

Giang Hoa Tran 18.11.2022 19:36

Другие вопросы по теме