Как указать уменьшение размера двух индуктивных типов Coq

Я пытаюсь определить индуктивный тип game для комбинаторных игр. Мне нужен метод сравнения, который показывает, являются ли две игры lessOrEq, greatOrEq, lessOrConf или greatOrConf. Затем я могу проверить, равны ли две игры, если они обе равны lessOrEq и greatOrEq.

Но когда я пытаюсь определить взаимно рекурсивные методы для этой проверки, я получаю:

Ошибка: невозможно угадать убывающий аргумент fix.

Я думаю, это потому, что только одна игра или другая уменьшается в размере с каждым рекурсивным вызовом (но не в обоих случаях). Как я могу указать на это Coq?

Вот мой код. Часть, которая терпит неудачу, - это взаимно рекурсивное определение gameCompare, innerGCompare, listGameCompare, gameListCompare.

Inductive game : Set :=
 | gameCons : gamelist -> gamelist -> game
with gamelist : Set :=
 | emptylist : gamelist
 | listCons : game -> gamelist -> gamelist.

Definition side :=
 game -> gamelist.

Definition leftSide (g : game) : gamelist :=
 match g with
  | gameCons gll glr => gll
 end.

Definition rightSide (g : game) : gamelist :=
 match g with
  | gameCons gll glr => glr
 end.

Inductive compare_quest : Set :=
 | lessOrEq : compare_quest
 | greatOrEq : compare_quest
 | lessOrConf : compare_quest
 | greatOrConf : compare_quest.

Definition g1side (c : compare_quest) : side :=
 match c with
  | lessOrEq => leftSide
  | greatOrEq => rightSide
  | lessOrConf => rightSide
  | greatOrConf => leftSide
 end.

Definition g2side (c : compare_quest) : side :=
 match c with 
  | lessOrEq => rightSide
  | greatOrEq => leftSide
  | lessOrConf => leftSide
  | greatOrConf => rightSide
 end.

Definition combiner :=
 Prop -> Prop -> Prop.

Definition compareCombiner (c : compare_quest) : combiner :=
 match c with
  | lessOrEq => and
  | greatOrEq => and
  | lessOrConf => or
  | greatOrConf => or
 end.

Definition nextCompare (c : compare_quest) : compare_quest :=
 match c with
  | lessOrEq => lessOrConf
  | greatOrEq => greatOrConf
  | lessOrConf => lessOrEq
  | greatOrConf => greatOrEq
 end.

Definition cbn_init (cbn : combiner) : Prop :=
 ~ (cbn False True).

Fixpoint gameCompare (c : compare_quest) (g1 : game) (g2 : game) : Prop :=
 innerGCompare (nextCompare c) (compareCombiner c) g1 (g1side c g1) g2 (g2side c g2)
with innerGCompare (next_c : compare_quest) (cbn : combiner)
      (g1 : game) (g1s : gamelist) (g2 : game) (g2s : gamelist) : Prop :=
 cbn (listGameCompare next_c cbn g1s g2) (gameListCompare next_c cbn g1 g2s)
with listGameCompare (c : compare_quest) (cbn : combiner) (g1s : gamelist) (g2 : game) : Prop :=
 match g1s with
  | emptylist => cbn_init cbn
  | listCons g1s_car g1s_cdr => cbn (gameCompare c g1s_car g2) (listGameCompare c cbn g1s_cdr g2)
 end
with gameListCompare (c : compare_quest) (cbn : combiner) (g1 : game) (g2s : gamelist) : Prop :=
 match g2s with
  | emptylist => cbn_init cbn
  | listCons g2s_car g2s_cdr => cbn (gameCompare c g1 g2s_car) (gameListCompare c cbn g1 g2s_cdr)
 end.

Definition game_eq (g1 : game) (g2 : game) : Prop :=
 (gameCompare lessOrEq g1 g2) /\ (gameCompare greatOrEq g1 g2).

person dspyz    schedule 25.06.2013    source источник
comment
Я дал ответ ниже, но если вы могли бы более подробно объяснить, что пытается сделать ваш код, я могу попытаться улучшить его!   -  person Arthur Azevedo De Amorim    schedule 25.06.2013


Ответы (2)


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

Одна вещь, которую вы можете сделать, - это определить gameCompare как (возможно, взаимно) индуктивное отношение вместо функции. Я не знаю, насколько вы знакомы с Coq, поэтому я не буду объяснять это подробно, потому что ответ будет слишком большим, но индуктивные отношения дают вам гораздо большую гибкость, чем функции при определении таких понятий, как gameCompare. Дополнительную информацию о том, как определять индуктивные отношения, можно найти в книге Бенджамина Пирса Основы программного обеспечения.

Одним из недостатков этого подхода является то, что индуктивные отношения, в отличие от функций, на самом деле ничего не вычисляют. Иногда это затрудняет их использование. В частности, вы не можете упростить индуктивное предложение, как вы можете упростить вызов функции.

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

Fixpoint gameSize (g : game) : nat :=
  match g with
    | gameCons gl1 gl2 => 1 + gameListSize gl1 + gameListSize gl2
  end

with gameListSize (gl : gamelist) : nat :=
  match gl with
    | emptylist => 1
    | listCons g gl => 1 + gameSize g + gameListSize gl
  end.

Definition gameCompareTime (g1 g2 : game) : nat :=
  gameSize g1 + gameSize g2.

Fixpoint gameCompareAux (time : nat) (c : compare_quest) (g1 : game) (g2 : game) : Prop :=
  match time with
    | O => True
    | S time =>
      compareCombiner c
                      (listGameCompare time
                                       (nextCompare c)
                                       (compareCombiner c)
                                       (g1side c g1)
                                       g2)
                      (gameListCompare time
                                       (nextCompare c)
                                       (compareCombiner c)
                                       g1
                                       (g2side c g2))
  end

with listGameCompare (time : nat) (c : compare_quest) (cbn : combiner) (g1s : gamelist) (g2 : game) : Prop :=
  match time with
    | 0 => True
    | S time =>
      match g1s with
        | emptylist => cbn_init cbn
        | listCons g1s_car g1s_cdr => cbn (gameCompareAux time c g1s_car g2) (listGameCompare time c cbn g1s_cdr g2)
      end
  end

with gameListCompare (time : nat) (c : compare_quest) (cbn : combiner) (g1 : game) (g2s : gamelist) : Prop :=
  match time with
    | 0 => True
    | S time =>
      match g2s with
        | emptylist => cbn_init cbn
        | listCons g2s_car g2s_cdr => cbn (gameCompareAux time c g1 g2s_car) (gameListCompare time c cbn g1 g2s_cdr)
      end
  end.

Definition gameCompare c g1 g2 :=
  gameCompareAux (gameCompareTime g1 g2) c g1 g2.

Definition game_eq (g1 : game) (g2 : game) : Prop :=
 (gameCompare lessOrEq g1 g2) /\ (gameCompare greatOrEq g1 g2).

Функция gameCompareTime вычисляет сумму размеров обеих игр, что кажется разумным ограничением глубины дерева вызовов gameCompareAux. Обратите внимание, что я вставил innerGCompare, так как это немного упрощает вычисление границы. Когда время заканчивается (т. Е. Ветвь 0 в сопоставлении с образцом), мы возвращаем произвольное значение (True, в данном случае), потому что мы дали функции достаточно времени для ее завершения до достижения этого случая.

Преимущество этого подхода в том, что его относительно легко реализовать. Недостатком является то, что для доказательства чего-либо о gameCompare вам потребуется явно аргументировать gameCompareAux и время завершения, что может быть очень утомительным.

person Arthur Azevedo De Amorim    schedule 25.06.2013
comment
Спасибо. Я просмотрел предоставленную вами ссылку, и это было очень полезно. Я попытался реализовать индуктивное отношение, но столкнулся с совершенно другой проблемой. Не могли бы вы взглянуть на это? stackoverflow.com/q/17308978/403875 Спасибо - person dspyz; 26.06.2013
comment
При подходе с уменьшающимся временем, нет ли способа доказать компилятору, что сумма (gameSize g1) + (gameSize g2) строго убывает? Так что он примет функцию Fixpoint без дополнительных аргументов - person dspyz; 27.06.2013
comment
Есть способ доказать, что эта мера строго уменьшается, но для этого требуется больше оборудования. Если вам интересно, вы можете поискать обоснованную рекурсию в Coq и команде Function. - person Arthur Azevedo De Amorim; 28.06.2013
comment
Function не работает с несколькими убывающими аргументами, если вы не кортежете их. - person ; 18.07.2013

Один из способов указать уменьшение аргументов функции - использовать специальный предикат, который описывает область действия этой функции.

Inductive gameCompareDom : compare_quest ->  game -> game -> Prop :=
  | gameCompareDom1 : forall c g1 g2, innerGCompareDom (nextCompare c) (compareCombiner c) g1 (g1side c g1) g2 (g2side c g2) -> gameCompareDom c g1 g2

with innerGCompareDom : compare_quest -> combiner -> game -> gamelist -> game -> gamelist -> Prop :=
  | innerGCompareDom1 : forall next_c cbn g1 g1s g2 g2s, listGameCompareDom next_c cbn g1s g2 -> gameListCompareDom next_c cbn g1 g2s -> innerGCompareDom next_c cbn g1 g1s g2 g2s

with listGameCompareDom : compare_quest -> combiner -> gamelist -> game -> Prop :=
  | listGameCompareDom1 : forall c cbn g1s g2, g1s = emptylist -> listGameCompareDom c cbn g1s g2
  | listGameCompareDom2 : forall c cbn g1s g2 g1s_car g1s_cdr, g1s = listCons g1s_car g1s_cdr -> gameCompareDom c g1s_car g2 -> listGameCompareDom c cbn g1s_cdr g2 -> listGameCompareDom c cbn g1s g2

with gameListCompareDom : compare_quest -> combiner -> game -> gamelist -> Prop :=
  | gameListCompareDom1 : forall c cbn g1 g2s, g2s = emptylist -> gameListCompareDom c cbn g1 g2s
  | gameListCompareDom2 : forall c cbn g1 g2s g2s_car g2s_cdr, g2s = listCons g2s_car g2s_cdr -> gameCompareDom c g1 g2s_car -> gameListCompareDom c cbn g1 g2s_cdr -> gameListCompareDom c cbn g1 g2s.

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

Lemma gameCompareDom1Inv : forall c g1 g2, gameCompareDom c g1 g2 ->
  innerGCompareDom (nextCompare c) (compareCombiner c) g1 (g1side c g1) g2 (g2side c g2).

Lemma innerGCompareDom1Inv1 : forall next_c cbn g1 g1s g2 g2s,
  innerGCompareDom next_c cbn g1 g1s g2 g2s ->
  listGameCompareDom next_c cbn g1s g2.

Lemma innerGCompareDom1Inv2 : forall next_c cbn g1 g1s g2 g2s,
  innerGCompareDom next_c cbn g1 g1s g2 g2s ->
  gameListCompareDom next_c cbn g1 g2s.

Lemma listGameCompareDom2Inv1 : forall c cbn g1s g2 g1s_car g1s_cdr,
  listGameCompareDom c cbn g1s g2 -> g1s = listCons g1s_car g1s_cdr ->
  gameCompareDom c g1s_car g2.

Lemma listGameCompareDom2Inv2 : forall c cbn g1s g2 g1s_car g1s_cdr,
  listGameCompareDom c cbn g1s g2 -> g1s = listCons g1s_car g1s_cdr ->
  listGameCompareDom c cbn g1s_cdr g2.

Lemma gameListCompareDom2Inv1 : forall c cbn g1 g2s g2s_car g2s_cdr,
  gameListCompareDom c cbn g1 g2s -> g2s = listCons g2s_car g2s_cdr ->
  gameCompareDom c g1 g2s_car.

Lemma gameListCompareDom2Inv2 : forall c cbn g1 g2s g2s_car g2s_cdr,
  gameListCompareDom c cbn g1 g2s -> g2s = listCons g2s_car g2s_cdr ->
  gameListCompareDom c cbn g1 g2s_cdr.

Fixpoint gameCompareAux (c : compare_quest) (g1 : game) (g2 : game) (H1 : gameCompareDom c g1 g2) : Prop :=
  innerGCompareAux (nextCompare c) (compareCombiner c) g1 (g1side c g1) g2 (g2side c g2) (gameCompareDom1Inv _ _ _ H1)

with innerGCompareAux (next_c : compare_quest) (cbn : combiner) (g1 : game) (g1s : gamelist) (g2 : game) (g2s : gamelist) (H1 : innerGCompareDom next_c cbn g1 g1s g2 g2s) : Prop :=
  cbn (listGameCompareAux next_c cbn g1s g2 (innerGCompareDom1Inv1 _ _ _ _ _ _ H1)) (gameListCompareAux next_c cbn g1 g2s (innerGCompareDom1Inv2 _ _ _ _ _ _ H1))

with listGameCompareAux (c : compare_quest) (cbn : combiner) (g1s : gamelist) (g2 : game) (H1 : listGameCompareDom c cbn g1s g2) : Prop :=
  match g1s as gl1 return g1s = gl1 -> Prop with
  | emptylist                => fun H2 => cbn_init cbn
  | listCons g1s_car g1s_cdr => fun H2 => cbn (gameCompareAux c g1s_car g2 (listGameCompareDom2Inv1 _ _ _ _ _ _ H1 H2)) (listGameCompareAux c cbn g1s_cdr g2 (listGameCompareDom2Inv2 _ _ _ _ _ _ H1 H2))
  end eq_refl

with gameListCompareAux (c : compare_quest) (cbn : combiner) (g1 : game) (g2s : gamelist) (H1 : gameListCompareDom c cbn g1 g2s) : Prop :=
  match g2s as gl1 return g2s = gl1 -> Prop with
  | emptylist                => fun H2 => cbn_init cbn
  | listCons g2s_car g2s_cdr => fun H2 => cbn (gameCompareAux c g1 g2s_car (gameListCompareDom2Inv1 _ _ _ _ _ _ H1 H2)) (gameListCompareAux c cbn g1 g2s_cdr (gameListCompareDom2Inv2 _ _ _ _ _ _ H1 H2))
  end eq_refl.

Lemma gameCompareTot : forall c g1 g2, gameCompareDom c g1 g2.

Lemma innerGCompareTot : forall next_c cbn g1 g1s g2 g2s,
  innerGCompareDom next_c cbn g1 g1s g2 g2s.

Lemma listGameCompareTot : forall g2 g1s c cbn,
  listGameCompareDom c cbn g1s g2.

Lemma gameListCompareTot : forall g1 g2s c cbn,
  gameListCompareDom c cbn g1 g2s.

Definition gameCompare (c : compare_quest) (g1 : game) (g2 : game) : Prop :=
  gameCompareAux c g1 g2 (gameCompareTot _ _ _).

Definition innerGCompare (next_c : compare_quest) (cbn : combiner) (g1 : game) (g1s : gamelist) (g2 : game) (g2s : gamelist) : Prop :=
  innerGCompareAux next_c cbn g1 g1s g2 g2s (innerGCompareTot _ _ _ _ _ _).

Definition listGameCompare (c : compare_quest) (cbn : combiner) (g1s : gamelist) (g2 : game) : Prop :=
  listGameCompareAux c cbn g1s g2 (listGameCompareTot _ _ _ _).

Definition gameListCompare (c : compare_quest) (cbn : combiner) (g1 : game) (g2s : gamelist) : Prop :=
  gameListCompareAux c cbn g1 g2s (gameListCompareTot _ _ _ _).

Доказательства лемм об обращении должны заканчиваться Defined. вместо Qed., чтобы их содержание было прозрачным и доступным для вычислений. Они также не должны ссылаться на какие-либо непрозрачные теоремы.

После этого вы сможете определить следующие леммы об уравнениях, прибегая к аксиоме о несоответствии доказательства:

Lemma gameCompareEq : forall c g1 g2, gameCompare c g1 g2 =
  innerGCompare (nextCompare c) (compareCombiner c) g1 (g1side c g1) g2 (g2side c g2).

Lemma innerGCompareEq : forall next_c cbn g1 g1s g2 g2s, innerGCompare next_c cbn g1 g1s g2 g2s =
  cbn (listGameCompare next_c cbn g1s g2) (gameListCompare next_c cbn g1 g2s).

Lemma listGameCompareEq : forall c cbn g1s g2, listGameCompare c cbn g1s g2 =
  match g1s with
  | emptylist                => cbn_init cbn
  | listCons g1s_car g1s_cdr => cbn (gameCompare c g1s_car g2) (listGameCompare c cbn g1s_cdr g2)
  end.

Lemma gameListCompareEq : forall c cbn g1 g2s, gameListCompare c cbn g1 g2s =
  match g2s with
  | emptylist                => cbn_init cbn
  | listCons g2s_car g2s_cdr => cbn (gameCompare c g1 g2s_car) (gameListCompare c cbn g1 g2s_cdr)
  end.

Вы можете использовать Extraction Inline для вспомогательных функций, чтобы ваши основные функции выглядели так, как вы ожидаете от них при извлечении. Но здесь это не применимо, потому что ваши функции возвращают Props вместо bools.

Полная разработка здесь.

person Community    schedule 16.07.2013