使用由明确定义的归纳

时间:2015-09-02 13:16:48

标签: coq

当我使用Function在Coq中定义非结构递归函数时,在询问特定计算时,结果对象的行为很奇怪。实际上,Eval compute in ...指令不是直接给出结果,而是返回相当长的(通常为170 000行)表达式。似乎Coq无法评估所有内容,因此返回一个简化(但很长)的表达式,而不仅仅是一个值。

问题似乎来自于我证明Function所产生的义务的方式。首先,我认为问题来自我使用的不透明术语,并且我将所有的引理转换为透明常量。那么,有没有办法列出一个术语中出现的不透明术语?或者任何其他方式将不透明的引理变成透明的引理?

然后我说,问题来自于所使用的订单有充分根据的证据。但我得到了奇怪的结果。

例如,我通过重复应用log2在自然数上定义div2。这是定义:

Function log2 n {wf lt n} :=
  match n with
  | 0 => 0
  | 1 => 0
  | n => S (log2 (Nat.div2 n))
  end.

我有两个证明义务。第一个检查n是否尊重递归调用中的lt关系,并且可以很容易地证明。

forall n n0 n1 : nat, n0 = S n1 -> n = S (S n1) -> Nat.div2 (S (S n1)) < S (S n1)

intros. apply Nat.lt_div2. apply le_n_S. apply le_0_n.

第二个检查lt是否是有根据的命令。这已经在标准库中得到证明。相应的引理是Coq.Arith.Wf_nat.lt_wf

如果我使用此证明,则生成的函数表现正常。 Eval compute in log24 10.会返回3

但如果我想自己做证明,我并不总是得到这种行为。首先,如果我用Qed而不是Defined结束证明,计算结果(即使是小数字)也是一个复杂的表达式而不是单个数字。所以我使用Defined并尝试仅使用透明的引理。

Lemma lt_wf2 : well_founded lt.
Proof.
  unfold well_founded. intros n.
  apply (lemma1 n). clear n.
  intros. constructor. apply H.
Defined.

在这里,引理1证明了对自然数的有根据的归纳。在这里,我可以再次使用现有的词条,例如位于lt_wf_ind的{​​{1}},lt_wf_reclt_wf_rec1,甚至Coq.Arith.Wf_nat。第一个不起作用,似乎这是因为它是不透明的。其他三个人都在工作。

我尝试使用自然数well_founded_ind lt_wf上的标准归纳法直接证明它。这给出了:

nat_ind

使用此证明(以及它的一些变体),Lemma lemma1 : forall n (P:nat -> Prop), (forall n, (forall p, p < n -> P p) -> P n) -> P n. Proof. intros n P H. pose proof (nat_ind (fun n => forall p, p < n -> P p)). simpl in H0. apply H0 with (n:=S n). - intros. inversion H1. - intros. inversion H2. + apply H. exact H1. + apply H1. assumption. - apply le_n. Defined. 具有相同的奇怪行为。而且这个证据似乎只使用透明物体,所以也许问题就不存在了。

如何定义能够在特定值上返回可理解结果的log2

2 个答案:

答案 0 :(得分:3)

我设法指出导致麻烦的地方:inversion H2. lemma1intuition。事实证明,我们不需要进行案例分析,H2可以完成证明(它不会在Lemma lemma1 : forall n (P:nat -> Prop), (forall n, (forall p, p < n -> P p) -> P n) -> P n. Proof. intros n P H. pose proof (nat_ind (fun n => forall p, p < n -> P p)). simpl in H0. apply H0 with (n:=S n). - intros. inversion H1. - intros. intuition. - apply le_n. Defined. 上进行模式匹配):

lemma1

如果我们将log2 10与此证明一起使用,则3的计算会产生lt_wf2

顺便说一句,这是我的Lemma lt_wf2 : well_founded lt. Proof. unfold well_founded; intros n. induction n; constructor; intros k Hk. - inversion Hk. - constructor; intros m Hm. apply IHn; omega. (* OR: apply IHn, Nat.lt_le_trans with (m := k); auto with arith. *) Defined. 版本(它也可以让我们计算):

inversion H2.

我相信 Xavier Leroy撰写的Using Coq's evaluation mechanisms in anger博客文章解释了这种行为。

  

它在递归尾部之前消除了头部之间相等的证明,并最终决定是产生左边还是右边。这使得最终结果的左/右数据部分依赖于证明项,这通常不会减少!

在我们的案例中,我们在lemma1的证明中消除了不等式证明(Function),而inversion H1.机制使我们的计算依赖于证明项。因此,当n> 1时,评估者不能继续进行。 1.

引理正文中n = 0的原因并不影响计算,因为n = 1log2 nmatch定义在{{ 1}}表达式作为基本情况。

为了说明这一点,让我举例说明我们可以阻止对我们选择的任何值log2 nn进行n + 1评估,其中n > 1和< em>其他地方!

Lemma lt_wf2' : well_founded lt.
Proof.
  unfold well_founded; intros n.
  induction n; constructor; intros k Hk.
  - inversion Hk.          (* n = 0 *)
  - destruct n. intuition. (* n = 1 *)
    destruct n. intuition. (* n = 2 *)
    destruct n. intuition. (* n = 3 *)
    destruct n. inversion Hk; intuition. (* n = 4 and n = 5 - won't evaluate *)
    (* n > 5 *)
    constructor; intros m Hm; apply IHn; omega.
Defined.

如果您在log2的定义中使用此修改后的引理,则会看到它计算除n = 4n = 5之外的所有位置。好吧,几乎无处不在 - 大型nat的计算可能导致堆栈溢出或分段错误,Coq警告我们:

  

警告:使用时发生堆栈溢出或分段错误    nat中的大数(观察到的阈值可能在5000到70000之间变化)    取决于您的系统限制和执行的命令。)

并使log2适用于n = 4n = 5,即使对于上述&#34;有缺陷的&#34;证明,我们可以像这样修改log2

Function log2 n {wf lt n} :=
  match n with
  | 0 => 0
  | 1 => 0
  | 4 => 2
  | 5 => 2
  | n => S (log2 (Nat.div2 n))
  end.

在最后添加必要的证明。

<小时/> &#34;有根据的&#34;证明必须是透明的,并且不能依赖于证明对象上的模式匹配,因为Function机制实际上使用lt_wf引理来计算减少的终止守卫。如果我们查看Eval生成的术语(在评估无法生成nat的情况下),我们会看到以下内容:

fix Ffix (x : nat) (x0 : Acc (fun x0 x1 : nat => S x0 <= x1) x) {struct x0}

很容易看到x0 : Prop,因此在将功能程序log2提取到OCaml中时会被删除,但Coq的内部评估机制必须使用它确保终止。

答案 1 :(得分:2)

即使您声明您的证明是透明的,Coq中有根据的递归定义的函数的减少行为通常也不是很好。这样做的原因是,有充分理由的论据通常需要用复杂的证明术语来完成。由于这些证明条款最终出现在有根据的递归定义中,因此简化了#34;正如您所注意到的,您的函数将使所有这些证明术语出现。

依靠自定义策略和引理来减少以这种方式定义的函数更容易。首先,我建议赞成Program Fixpoint超过Function,因为后者更老,而且(我认为)维护得不太好。因此,您最终会得到如下定义:

Require Import Coq.Numbers.Natural.Peano.NPeano.
Require Import Coq.Program.Wf.
Require Import Coq.Program.Tactics.

Program Fixpoint log2 n {wf lt n} :=
  match n with
  | 0 => 0
  | 1 => 0
  | n => S (log2 (Nat.div2 n))
  end.

Next Obligation.
admit.
Qed.

现在,您只需使用program_simpl策略来简化对log2的调用。这是一个例子:

Lemma foo : log2 4 = 2.
Proof.
  program_simpl.
Qed.