使用由明确定义的归纳定义的递归函数进行计算

2023-11-26

当我使用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.

这里,lemma1 是自然数上有根据的归纳法的证明。在这里,我再次可以使用已经存在的引理,例如lt_wf_ind, lt_wf_rec, lt_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有同样奇怪的行为。而且这个证明似乎只使用了透明物体,所以也许问题并不存在。

我如何定义一个Function返回特定值的可理解结果?


我已经成功地找出了造成麻烦的地方:它inversion H2. in lemma1。事实证明我们不需要案例分析intuition可以完成证明(它不进行模式匹配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.

我相信在愤怒中使用 Coq 的评估机制Xavier Leroy 的博客文章解释了这种行为。

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

在我们的例子中,我们消除了不平等的证明(inversion H2.) 的证明lemma1Function机制使我们的计算依赖于证明项。因此,当 n > 1 时,评估器无法继续。

以及原因inversion H1.在引理的主体中不影响计算的是n = 0 and n = 1, log2 n被定义在match表达式作为基本情况。

为了说明这一点,让我举一个例子,当我们可以阻止评估log2 n对任何值n and n + 1我们的选择,其中n > 1 and 无处!

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 = 4 and n = 5。好吧,几乎无处不在——大型计算nats 可以导致堆栈溢出或分段错误,正如 Coq 警告我们的那样:

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

并让log2为。。。工作n = 4 and n = 5即使对于上述“有缺陷”的证明,我们也可以修改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.

最后添加必要的证明。


The "well-founded" proof must be transparent and can't rely on pattern-matching on proof objects because the Function mechanism actually uses the lt_wf lemma to compute the decreasing termination guard. If we look at the term produced by Eval (in a case where evaluation fails to produce a nat), we'll see something along these lines:
fix Ffix (x : nat) (x0 : Acc (fun x0 x1 : nat => S x0 <= x1) x) {struct x0}

很容易看出x0 : Prop,所以在提取功能程序时它会被删除log2比如说 OCaml,但是 Coq 的内部评估机制必须使用它来确保终止。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用由明确定义的归纳定义的递归函数进行计算 的相关文章

  • Coq:变量参数列表上的 Ltac 定义?

    在尝试创建循环可变长度参数列表的 Ltac 定义时 我在 Coq 8 4pl2 上遇到了以下意外行为 谁能给我解释一下吗 Ltac ltac loop X match X with 0 gt idtac done gt fun Y gt i
  • 使用由明确定义的归纳定义的递归函数进行计算

    当我使用Function在 Coq 中定义一个非结构递归函数 当要求进行特定计算时 生成的对象会表现得很奇怪 事实上 不是直接给出结果 而是Eval compute in 指令返回一个相当长 通常为 170 000 行 的表达式 Coq 似
  • 如何从外部软件调用证明助手Coq

    如何从外部软件调用证明助手Coq Coq 有一些 API 吗 Coq 命令行界面是否足够丰富 可以在文件中传递参数并在文件中接收响应 我对 Java 或 C 桥感兴趣 这是合理的问题 Coq 并不是一种常见的商业软件 人们可以从中获得开发人
  • 如何证明 Coq 中的两个 Fibonacci 实现相等?

    我有两个斐波那契实现 如下所示 我想证明它们在功能上是等效的 我已经证明了自然数的性质 但是这个练习需要另一种我无法弄清楚的方法 我使用的教科书介绍了 Coq 的以下语法 因此应该可以使用这种表示法来证明相等性
  • 如何在 Coq 中将一条线的公理定义为两个点

    我想找一个例子axiom in Coq类似于几何中的线公理 如果给定两个点 则这两点之间存在一条线 我想看看如何在 Coq 中定义它 本质上选择这个简单的直线公理来看看如何定义一些非常原始的东西 因为我很难在自然语言之外定义它 具体来说 我
  • 证明唯一的零长度向量为零

    我有一个类型定义为 Inductive bits nat gt Set bitsNil bits 0 bitsCons forall l bool gt bits l gt bits S l 我试图证明 Lemma emptyIsAlway
  • Coq 平等实现

    我正在编写一种玩具语言 其中 AST 中的节点可以有任意数量的子节点 Num has 0 Arrow有 2 个 等等 您可以致电这些接线员 此外 AST 中可能只有一个节点被 聚焦 我们对数据类型进行索引Z如果它有焦点 或者H如果没有 我需
  • Ltac:通过回溯重复策略 n 次

    假设我有一个像这样的策略 取自 HaysTac 它搜索一个参数来专门化一个特定的假设 Ltac find specialize in H multimatch goal with v gt specialize H v end 然而 我想写
  • 对术语...进行抽象会导致术语...类型错误

    这是我想证明的 A Type i nat index f nat nat n nat ip n lt i partial index f nat option nat L partial index f index f n Some n V
  • 有没有办法禁用 Coq 中的特定符号?

    我希望在 Coqide 中 证明状态不使用某种符号 但仍使用所有其他符号 这可能吗 据我在文档中的理解 这是不可能的 您也许可以使用打开 关闭范围 但我不确定它是否有效 因为明确指出只要有可能 符号将用于打印
  • 在 Coq 中,重写适用于 = 但不适用于 <-> (iff)

    我在证明期间有以下内容 我需要替换normal form step t with value t因为有一个已证明的定理存在等价 H1 t1 gt t1 normal form step t1 t2 tm H2 t2 gt t2 normal
  • F# 中的命令式多态性

    OCaml 的 Hindley Milner 类型系统不允许命令式多态性 类似于 System F 除非通过最近对记录类型的扩展 这同样适用于 F 然而 有时需要将用命令式多态性 例如 Coq 编写的程序翻译成此类语言 Coq 的 OCam
  • 如何暂时禁用 Coq 中的符号

    当您熟悉项目时 符号很方便 但当您刚刚开始使用代码库时 符号可能会令人困惑 我知道你可以用白话关闭所有符号Set Printing All 但是 我想保留一些打印 例如隐式参数 全部打印如下 Require Import Utf8 core
  • 逻辑:tr_rev_ Correct 的辅助引理

    在逻辑章节中 介绍了反向列表函数的尾递归版本 我们需要证明它可以正确工作 Fixpoint rev append X l1 l2 list X list X match l1 with gt l2 x l1 gt rev append l1
  • Coq - 在不丢失信息的情况下归纳函数

    当尝试对函数的结果 返回归纳类型 执行案例分析时 我在 Coq 中遇到了一些麻烦 当使用通常的策略时 比如elim induction destroy等等 信息就会丢失 我举个例子 我们首先有一个像这样的函数 Definition f n
  • Coq :> 符号

    这可能是非常微不足道的 但我找不到任何关于 gt 符号在 Coq 中含义的信息 有什么区别 U 类型 和 W gt 类型 这取决于符号出现的位置 例如 如果它位于记录声明内 它会指示 Coq 添加相应的记录投影作为强制 具体来说 假设我们有
  • Coq QArith 除以零就是零,为什么?

    我注意到在 Coq 的有理数定义中 零的倒数被定义为零 通常 除以零是没有明确定义 合法 允许的 Require Import QArith Lemma inv zero is zero 0 0 Proof unfold Qeq refle
  • Coq:添加“强归纳”策略

    对自然数的 强 或 完全 归纳意味着当证明 n 上的归纳步骤时 您可以假设该属性对于任何 k 都成立 Theorem strong induction forall P nat gt Prop forall n nat forall k n
  • Coq案例分析和函数返回子集类型的重写

    我正在做一个关于使用子集类型编写经过认证的函数的简单练习 想法是先写一个前驱函数 pred forall n n nat n gt 0 m nat S m n 1 然后使用这个定义给定一个函数 pred2 forall n n nat n
  • Coq:承认断言

    有没有办法在 Coq 中承认断言 假设我有一个这样的定理 Theorem test forall m n nat m n n m Proof intros n m assert H1 m m n m S n Admitted Abort 上

随机推荐