我试图证明一个引理,它在某个部分有一个错误的假设。在Coq,我曾经写过“一致”,它会摆脱目标。但是,我不知道如何继续Isabelle Isar。我试图证明我的le
函数的一个引理:
primrec le::"nat ⇒ nat ⇒ bool" where
"le 0 n = True" |
"le (Suc k) n = (case n of 0 ⇒ False | Suc j ⇒ le k j)"
lemma def_le: "le a b = True ⟷ (∃k. a + k = b)"
proof
assume H:"le a b = True"
show "∃k. a + k = b"
proof (induct a)
case 0
show "∃k. 0 + k = b"
proof -
have "0 + b = b" by simp
thus ?thesis by (rule exI)
qed
case Suc
fix n::nat
assume HI:"∃k. n + k = b"
show "∃k. (Suc n) + k = b"
proof (induct b)
case 0
show "∃k. (Suc n) + k = 0"
proof -
have "le (Suc n) 0 = False" by simp
oops
请注意,我的le
功能“少于或等于”。在证据的这一点上,我发现我有假设H
表明le a b = True
,或者在这种情况下le (Suc n) 0 = True
是假的。我该如何解决这个问题?
另一个小问题:我想写have "le (Suc n) 0 = False" by (simp only:le.simps)
,但这不起作用。我似乎需要添加一些规则来减少案例表达式。我错过了什么?
非常感谢你的帮助。
答案 0 :(得分:6)
问题不在于伊莎贝尔很难摆脱False
假设。事实上,如果假设中存在False
,几乎所有Isabelle的证明方法都会立即证明任何事情。不,这里的问题是,在证据的那一点,你不再拥有你需要的假设,因为你没有将它们链接到归纳中。但首先,请允许我做一些小的评论,然后提出具体建议来修正你的证据。
le a b = True
或le a b = False
有些不同寻常。只需撰写le a b
或¬le a b
。使用功能包:
fun le :: "nat ⇒ nat ⇒ bool" where
"le 0 n = True"
| "le (Suc k) 0 = False"
| "le (Suc k) (Suc n) = le k n"
如果您证明以下引理,则证明是完全自动的:
lemma def_le': "le a b ⟷ a + (b - a) = b"
by (induction a arbitrary: b) (simp_all split: nat.split)
使用我的函数定义,它是:
lemma def_le': "le a b ⟷ (a + (b - a) = b)"
by (induction a b rule: le.induct) simp_all
你的引理然后从那个简单的:
lemma def_le: "le a b ⟷ (∃k. a + k = b)"
using def_le' by auto
这是因为存在主义使搜索空间爆炸。给自动化一些具体的东西可以帮助很多。
有很多问题。首先,您可能需要执行induct a arbitrary: b
,因为b
会在您的归纳期间发生变化(对于le (Suc a) b
,您必须对b
进行案例分析,然后在b = Suc b'
的情况下,您将从le (Suc a) (Suc b')
转到le a b'
。
其次,在最顶端,你有assume "le a b = True"
,但你没有把这个事实链接到归纳。如果您在Isabelle中进行归纳,则必须将包含归纳变量的所有必需假设链接到归纳命令中,否则它们将无法在归纳证明中使用。有问题的假设涉及a
和b
,但如果你对a
进行归纳,则必须推理一些与a'
无关的任意变量a
assume H:"le a b = True"
thus "∃k. a + k = b"
。例如:
b
(对next
的第二次归纳相同)
第三,当您在Isar中有多个案例时(例如在归纳或案例分析期间),如果他们有不同的假设,您必须将它们与next
分开。 next
基本上抛弃了所有固定变量和局部假设。根据我之前提到的更改,您需要在case Suc
之前case
,否则Isabelle会抱怨。
第四,Isar中的Suc
命令可以修复变量。在a
案例中,归纳变量arbitrary: b
已修复;如果更改为a
,则b
和case (Suc a b)
会得到修复。你应该给这些变量明确的名字;否则,伊莎贝尔会发明它们,你必须希望它所提出的那些与你使用的相同。这不是好风格。所以写一下case
。请注意,在使用case
时,不必须修复变量或假设。 Suc
命令为您处理这个问题,并将局部假设存储在一个与案例同名的定理集合中,例如: Suc.prems
在这里。它们分为Suc.IH
,Suc.hyps
,?case
。此外,当前案例的证明义务存储在?thesis
(不是lemma def_le: "le a b ⟷ (∃k. a + k = b)"
proof
assume "le a b"
thus "∃k. a + k = b"
proof (induct a arbitrary: b)
case 0
show "∃k. 0 + k = b" by simp
next
case (Suc a b)
thus ?case
proof (induct b)
case 0
thus ?case by simp
next
case (Suc b)
thus ?case by simp
qed
qed
next
!)。
有了这个(以及一点点清理),你的证据看起来像这样:
lemma def_le: "le a b ⟷ (∃k. a + k = b)"
proof
assume "le a b"
thus "∃k. a + k = b"
proof (induct a arbitrary: b)
case (Suc a b)
thus ?case by (induct b) simp_all
qed simp
next
可以浓缩为
le a b ⟷ a + (b - a) = b
但实际上,我建议你先简单地证明一个具体的结果,比如{{1}},然后用它来证明存在主义的陈述。
答案 1 :(得分:2)
Manuel Eberl做了很多努力,我只回答你关于如何试图控制simp
等问题。
在继续之前,我会离开主题并澄清在另一个网站上说的内容。 “小费”这个词用来表达对M.E.的信任,但应该是“2个答案提供了3个解释”。邮件列表上的电子邮件无法在不向列表发送垃圾邮件的情况下进行更正。
这些简短的答案是:
simp
,但下面显示的属性del
和only
会多次控制它到您想要的程度。要看到它没有超出你想要的数量,需要使用跟踪;下面给出了一个跟踪示例。simp
,rule
,drule
和erule
以及其他方法。其他人需要提供详尽的清单。simp
,auto
,blast
等所做的详细证明”,很少愿意投入努力回答这个问题。调查simp
正在做什么,这可能是一项简单而乏味的工作。如果你看,你会看到。人们抱怨自动化太多,或者他们抱怨Isabelle的自动化程度太低。
永远不会有太多的自动化,但这是因为使用Isabelle / HOL,自动化主要是可选的。 无自动化的可能性使证明有可能变得有趣,但只有没有自动化,在宏观计划中证明只是纯粹的乏味。
有only
和del
属性,可用于 控制simp
。仅通过试验跟踪来说,即使simp
也会调用其他证明方法,类似于auto
调用simp
,blast
和其他人的方式。
我认为你不能阻止simp
调用线性算术方法。但线性算法并不适用于大部分时间。
我在这里的回答是一般性的,也是为了确定auto
到底是什么。 auto
所采用的最大方法之一是blast
。
如果您不关心attribute_setup
何时使用blast
或直接调用,则不需要auto
。 Makarius Wenzel接受了爆炸追踪,但后来很好地展示了如何实现它的代码。
没有爆炸部分,只使用declare
。在证明中,您可以使用using
代替declare
。拿出你不想要的东西。请务必查看PIDE Simplifier Trace面板中的新simp_trace_new
信息。
attribute_setup blast_trace = {*
Scan.lift
(Parse.$$$ "=" -- Args.$$$ "true" >> K true ||
Parse.$$$ "=" -- Args.$$$ "false" >> K false ||
Scan.succeed true) >>
(fn b => Thm.declaration_attribute (K (Config.put_generic Blast.trace b)))
*}
attribute_setup blast_stats = {*
Scan.lift
(Parse.$$$ "=" -- Args.$$$ "true" >> K true ||
Parse.$$$ "=" -- Args.$$$ "false" >> K false ||
Scan.succeed true) >>
(fn b => Thm.declaration_attribute (K (Config.put_generic Blast.stats b)))
*}
declare[[simp_trace_new mode=full]]
declare[[linarith_trace,rule_trace,blast_trace,blast_stats]]
only
& del
我不想在你的问题中使用公式来努力工作。使用simp,您正在寻找only
的内容,并且跟踪的是没有使用您没有预料到的规则。
查看simp
跟踪以查看将要执行的基本重写,例如True
和False
的基本重写。如果你甚至不想那样,那么你必须采用像rule
这样的方法。
查看您是否可以完全关闭simp
的起点是apply(simp only:)
。
以下是一些例子。我必须更加努力地找到一个示例,以显示何时自动使用线性算法:
lemma
"a = 0 --> a + b = (b::'a::comm_monoid_add)"
apply(simp only:) (*
ERROR: simp can't apply any magic whatsoever.
*)
oops
lemma
"a = 0 --> a + b = (b::'a::comm_monoid_add)"
apply(simp only: add_0) (*
ERROR: Still can't. Rule 'add_0' is used, but it can't be used first.
*)
oops
lemma
"a = 0 --> a + b = (b::'a::comm_monoid_add)"
apply(simp del: add_0) (*
A LITTLE MAGIC:
It applied at least one rule. See the simp trace. It tried to finish
the job automatically, but couldn't. It says "Trying to refute subgoal 1,
etc.".
Don't trust me about this, but it looks typical of blast. I was under
the impressions that simp doesn't call blast.*)
oops
lemma
"a = 0 --> a + b = (b::'a::comm_monoid_add)"
by(simp) (*
This is your question. I don't want to step through the rules that simp
uses to prove it all.
*)