Isabelle / HOL:'simp'证明是缓慢的,而'值'是瞬时的

时间:2017-04-04 12:34:04

标签: performance isabelle

我是Isabelle / HOL的新手,仍然参与了编程练习的研究。与此同时,我通过将这些证明技术应用于组合词的问题来进行练习。我观察到一种非常不同的行为(在效率方面),在'价值'和'引理'之间。

可以解释两个命令之间的不同评估/搜索策略吗?

有没有办法在'引理'的证明中使用'价值'的速度?

当然,我在问,因为我在文档中找不到答案(到目前为止)。什么是手册中记录和解释这种效率差异?

这是重现问题的最小资源。

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

1 个答案:

答案 0 :(得分:2)

simp是一种通过证明内核的证明方法,即必须证明每一步都是合理的。对于长期重写链,这可能非常昂贵。

另一方面,value尽可能使用code generator。所有使用的常量都被转换为ML代码,然后执行。你必须相信结果,即它没有通过内核而且可能是错误的。

作为证明方法的value相当于eval。因此,加速证明的简便方法是使用:

lemma is_M_M_ex_19: "is_M M_ex_19"
by eval

Isabelle社区关于是否应该使用这种情况的意见有所不同。有人说它类似于axiomatization(因为你必须信任它),其他人认为如果通过内核的速度非常慢,这是一种合理的方法。每个人都同意,你必须非常小心代码生成器的自定义设置(你没有做过,所以应该没问题。)

存在中间立场:code_simp方法将simp设置为仅使用 eval将使用的等式。这意味着:simp的规则要小得多,同时仍然通过内核。在你的情况下,它实际上与by eval的速度相同,所以我强烈建议这样做:

lemma is_M_M_ex_19: "is_M M_ex_19"
by code_simp

在您的情况下,code_simpsimp快得多的原因是因为simproc在嵌套let表达式的数量中具有指数运行时。因此,另一种解决方案是使用simp add: Let_def来展开let表达式。

编辑反映Andreas Lochbihler的评论