简体   繁体   English

Isabelle / HOL:“简单”的证明很慢,而“价值”是瞬时的

[英]Isabelle/HOL: proof by 'simp' is slow while 'value' is instantaneous

I am new to Isabelle/HOL, still in the study of the prog-prov exercises. 我是Isabelle / HOL的新手,仍在学习prog-prov练习。 In the meantime, I am exercising by applying these proof techniques to questions of combinatorial words. 同时,我正在通过将这些证明技术应用于组合词问题来进行锻炼。 I observe a very different behavior (in terms of efficiency), between 'value' and 'lemma'. 我观察到“价值”和“引理”之间的行为(就效率而言)截然不同。

Can one explain the different evaluation/search strategies between the two commands? 可以解释这两个命令之间不同的评估/搜索策略吗?

Is there a way to have the speed of 'value' used inside a proof of a 'lemma'? 有没有办法在“引理”证明中使用“价值”的速度?

Of course, I am asking because I have not found the answer in the documentation (so far). 当然,我之所以问是因为到目前为止(在文档中)我还没有找到答案。 What is the manual where this difference of efficiency would be documented and explained? 如何记录和解释这种效率差异的手册是什么?

Here is a minimal piece of source to reproduce the problem. 这是重现该问题的最小资源。

theory SlowLemma
imports Main
begin

(* Alphabet for Motzkin words. *)
datatype alphabet = up | lv | dn

(* Keep the [...] notation for lists. *)
no_notation Cons (infixr "#" 65) and append (infixr "@" 65)

primrec count :: "'a ⇒ 'a list ⇒ nat" where
"count _ Nil = 0" |
"count s (Cons h q) = (if h = s then Suc (count s q) else count s q)"

(* prefix n l simply returns undefined if n > length l. *)
fun prefix :: "'a list ⇒ nat ⇒ 'a list" where
"prefix _ 0 = []" |
"prefix (Cons h q) (Suc n) = Cons h (prefix q n)"

definition M_ex_7 :: "alphabet list" where
"M_ex_7 ≡ [lv, lv, up, up, lv, dn, dn]"
definition M_ex_19 :: "alphabet list" where
"M_ex_19 ≡ [lv, lv, up, up, lv, up, lv, dn, lv, dn, lv, up, dn, dn, lv, up, dn, lv, lv]"

fun height :: "alphabet list ⇒ int" where
"height w = (int (count up w + count up w)) - (int (count dn w + count dn w))"

primrec is_pre_M :: "alphabet list ⇒ nat ⇒ bool" where
"is_pre_M _ (0 :: nat) = True"
| "is_pre_M w (Suc n) = (let w' = prefix w (Suc n) in is_pre_M w' n ∧ height w' ≥ 0)"

fun is_M :: "alphabet list ⇒ bool" where
"is_M w = (is_pre_M w (length w) ∧ height w = 0)"

(* These two calls to value are fast. *)
value "is_M M_ex_7"
value "is_M M_ex_19"

(* This first lemma goes fast. *)
lemma is_M_M_ex_7: "is_M M_ex_7"
by (simp add: M_ex_7_def)

(* This second lemma takes five minutes. *)
lemma is_M_M_ex_19: "is_M M_ex_19"
by (simp add: M_ex_19_def)

end

simp is a proof method that goes through the proof kernel, ie, every step has to be justified. simp是经过证明内核的证明方法,即,必须证明每个步骤都是合理的。 For long rewriting chains, this may be quite expensive. 对于较长的重写链,这可能会非常昂贵。

On the other hand, value uses the code generator where possible. 另一方面, value在可能的情况下使用代码生成器 All used constants are translated into ML code, which is then executed. 将所有使用的常量转换为ML代码,然后执行该代码。 You have to trust the result, ie, it didn't go through the kernel and may be wrong. 您必须相信结果,即结果没有通过内核,可能是错误的。

The equivalent of value as a proof method is eval . 相当于value的证明方法是eval Thus, an easy way to speed up your proofs is to use this: 因此,一种加快证明速度的简单方法是使用以下方法:

lemma is_M_M_ex_19: "is_M M_ex_19"
by eval

Opinions in the Isabelle community about whether or not this should be used differ. Isabelle社区中关于是否应使用此方法的意见有所不同。 Some say it's similar to axiomatization (because you have to trust it), others consider it a reasonable way if going through the kernel is prohibitively slow. 有人说它与axiomatization类似(因为必须信任它),而另一些人则认为这是通过内核的速度过慢时的一种合理方法。 Everyone agrees though that you have to be really careful about custom setup of the code generator (which you haven't done, so it should be fine). 每个人都同意,尽管您必须非常小心代码生成器的自定义设置(您尚未完成,所以应该没问题)。

There's middle ground: the code_simp method will set up simp to use only the equations that would otherwise be used by eval . 有一个中间立场: code_simp方法将设置simp使用eval否则将使用的方程式。 That means: a much smaller set of rules for simp , while still going through the kernel. 这意味着: simp的规则要小得多,同时仍要通过内核。 In your case, it is actually the same speed as by eval , so I would highly recommend doing that: 在您的情况下,它的速度实际上与by eval相同,因此,我强烈建议您这样做:

lemma is_M_M_ex_19: "is_M M_ex_19"
by code_simp

In your case, the reason why code_simp is much faster than simp is because of a simproc that has exponential runtime in the number of nested let expressions. 在您的情况下, code_simpsimp快得多的原因是因为simproc的嵌套let表达式数量具有指数运行时间。 Hence, another solution would be to use simp add: Let_def to just unfold let expressions. 因此,另一种解决方案是使用simp add: Let_def仅展开let表达式。


Edited to reflect comment by Andreas Lochbihler 编辑以反映Andreas Lochbihler的评论

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM