как доказать, что превращение множества в последовательность и обратно тождество в дафни

Привет Относительно новичок в Dafny и определил методы set2Seq и seq2Set для преобразования между наборами и последовательностями. Но только нашел как написать функцию fseq2Set из наборов в последовательности. Не могу найти как определить fseq2Set. Поскольку леммы не могут ссылаться на методы, это делает доказательство тождества невозможным для меня. Любая помощь очень ценится?

Код:

function method fseq2Set(se: seq<int>) :set<int>
    { set x:int | x in se :: x }
method seq2Set(se: seq<int>) returns (s:set<int>) 
    { s := set x:int | x in se :: x; }
method  set2Seq(s: set<int>)  returns (se:seq<int>)
  requires s != {}
  ensures s == fseq2Set(se)
  decreases |s|
{
    var y :| y in s;
    var tmp ;
    if (s=={y}) {tmp := [];} else {tmp := set2Seq(s-{y});}
    se := [y] + tmp;
    assert (s-{y}) + {y} == fseq2Set([y] + tmp);
}  
/* below fails */
function fset2Seq(s:set<int>):seq<int> 
decreases s { var y :| y in s ; [y] + fset2Seq(s-{y})    }
lemma cycle(s:set<int>) ensures forall s:set<int> ::  fseq2Set(fset2Seq(s)) == s {   }

person david streader    schedule 17.07.2020    source источник


Ответы (1)


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

Говоря о функциях, Danfy сообщает об ошибке в вашем определении fset2Seq, потому что s может быть пустым. В этом случае мы должны вернуть пустую последовательность, поэтому я изменил ваше определение на:

function fset2Seq(s:set<int>):seq<int> 
  decreases s
{
  if s == {} then []
  else 
    var y := Pick(s);
    [y] + fset2Seq(s - {y})
}

function Pick(s: set<int>): int
  requires s != {}
{
  var x :| x in s; x
}

который исправляет эту ошибку. Обратите внимание, что я также обернул оператор let-some-that :| в функцию с именем Pick. Это важно, но трудно объяснить. Просто доверься мне сейчас.

Теперь к лемме. Ваша лемма сформулирована немного странно, потому что она принимает параметр s, но тогда в предложении ensures не упоминается s. (Вместо этого там упоминается совершенно другая переменная, также называемая s, которая связана квантификатором forall!) Поэтому я изменил ее, чтобы избавиться от квантификатора, следующим образом:

lemma cycle(s:set<int>)
  ensures fseq2Set(fset2Seq(s)) == s

Затем я следую Моей Любимой Эвристике™ в верификации программы, которая состоит в том, что структура доказательства соответствует структуре программы. В данном случае речь идет о программе fseq2Set(fset2Seq(s)). Начиная с нашего ввода s, он сначала рекурсивно обрабатывается fset2Seq, а затем через понимание множества в fseq2Set. Итак, я ожидаю доказательства по индукции по s, которое следует структуре fset2Seq. Эта структура должна разветвляться в зависимости от того, пусто ли s, поэтому давайте сделаем это и в лемме:

lemma cycle(s:set<int>)
  ensures fseq2Set(fset2Seq(s)) == s
{
  if s == {} {
  } else {
  }
...

Dafny сообщает об ошибке в ветке else, но не в ветке if. Другими словами, Дафни доказал базовый случай, но ему нужна помощь с индуктивным случаем. Следующее, что делает fset2Seq(s), это звонит Pick(s). Давайте сделаем это тоже.

lemma cycle(s:set<int>)
  ensures fseq2Set(fset2Seq(s)) == s
{
  if s == {} {
  } else {
    var y := Pick(s);
...

Теперь мы знаем из его определения, что fset2Seq(s) вернет [y] + fset2Seq(s - {y}), поэтому мы можем скопировать и вставить наше предложение ensures и вручную подставить это выражение.

lemma cycle(s:set<int>)
  ensures fseq2Set(fset2Seq(s)) == s
{
  if s == {} {
  } else {
    var y := Pick(s);
    assert fseq2Set([y] + fset2Seq(s - {y})) == s;
...

Дафни сообщает об ошибке в этом утверждении, что неудивительно, поскольку это всего лишь слегка отредактированная версия пункта ensures, которую мы пытаемся доказать. Но важно то, что Dafny больше не сообщает об ошибке в самом предложении ensures. Другими словами, если мы сможем доказать это утверждение, мы закончим.

Глядя на это утверждение, мы видим, что fseq2Set применяется к двум спискам, добавленным вместе. И мы ожидаем, что это будет эквивалентно отдельному преобразованию двух списков в наборы, а затем их объединению. Мы могли бы доказать лемму на этот счет, или мы могли бы просто спросить Дафни, знает ли она уже этот факт, например:

lemma cycle(s:set<int>)
  ensures fseq2Set(fset2Seq(s)) == s
{
  if s == {} {
  } else {
    var y := Pick(s);
    assert fseq2Set([y] + fset2Seq(s - {y})) == fseq2Set([y]) + fseq2Set(fset2Seq(s - {y}));
    assert fseq2Set([y] + fset2Seq(s - {y})) == s;
...

(Обратите внимание, что новое добавленное утверждение перед последним.)

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

lemma cycle(s:set<int>)
  ensures fseq2Set(fset2Seq(s)) == s
{
  if s != {} {
    var y := Pick(s);
    assert fseq2Set([y] + fset2Seq(s - {y})) == fseq2Set([y]) + fseq2Set(fset2Seq(s - {y}));
  }
}

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

Я не объяснил Pick. По сути, как правило, вы всегда должны заключать :| в функцию всякий раз, когда вы ее используете. Чтобы понять почему, см. сообщения опытного пользователя Dafny на перебор коллекции и функции над элементами множества. Также см. статью Растана Компиляция оператора эпсилон Гильберта.

person James Wilcox    schedule 28.07.2020