Isabelle / HOL Isar中的假设证明

时间:2015-12-28 21:51:17

标签: coq proof isabelle

我试图证明一个引理,它在某个部分有一个错误的假设。在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),但这不起作用。我似乎需要添加一些规则来减少案例表达式。我错过了什么?

非常感谢你的帮助。

2 个答案:

答案 0 :(得分:6)

问题不在于伊莎贝尔很难摆脱False假设。事实上,如果假设中存在False,几乎所有Isabelle的证明方法都会立即证明任何事情。不,这里的问题是,在证据的那一点,你不再拥有你需要的假设,因为你没有将它们链接到归纳中。但首先,请允许我做一些小的评论,然后提出具体建议来修正你的证据。

一些备注

  1. 在Isabelle中写le a b = Truele a b = False有些不同寻常。只需撰写le a b¬le a b
  2. 即可
  3. 以方便的形式编写定义对于获得良好的自动化非常重要。当然,您的定义是有效的,但我建议使用以下内容,这可能更自然,并且会免费为您提供方便的归纳规则:
  4. 使用功能包:

    fun le :: "nat ⇒ nat ⇒ bool" where
      "le 0 n             = True"
    | "le (Suc k) 0       = False"
    | "le (Suc k) (Suc n) = le k n"
    
    1. 存在可能有时会隐藏重要信息,而且它们往往会自动化,因为自动化从来都不知道如何实例化它们。
    2. 如果您证明以下引理,则证明是完全自动的:

      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中进行归纳,则必须将包含归纳变量的所有必需假设链接到归纳命令中,否则它们将无法在归纳证明中使用。有问题的假设涉及ab,但如果你对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,则bcase (Suc a b)会得到修复。你应该给这些变量明确的名字;否则,伊莎贝尔会发明它们,你必须希望它所提出的那些与你使用的相同。这不是好风格。所以写一下case。请注意,在使用case时,必须修复变量或假设。 Suc命令为您处理这个问题,并将局部假设存储在一个与案例同名的定理集合中,例如: Suc.prems在这里。它们分为Suc.IHSuc.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,但下面显示的属性delonly会多次控制它到您想要的程度。要看到它没有超出你想要的数量,需要使用跟踪;下面给出了一个跟踪示例。
  • 要完全控制校对步骤,您可以使用“受控”simpruledruleerule以及其他方法。其他人需要提供详尽的清单。
  • 大多数具备专业知识的人都能够回答“simpautoblast等所做的详细证明”,很少愿意投入努力回答这个问题。调查simp正在做什么,这可能是一项简单而乏味的工作。
  • “黑匣子证明”总是可选的,据我所知,如果我们希望他们并且具备使其成为可选的专业知识。使它们成为可选的专业知识通常是一个主要的限制因素。凭借专业知识,动力成为限制因素。

最简单的是什么?它不能取悦所有人

如果你看,你会看到。人们抱怨自动化太多,或者他们抱怨Isabelle的自动化程度太低。

永远不会有太多的自动化,但这是因为使用Isabelle / HOL,自动化主要是可选的。 无自动化的可能性使证明有可能变得有趣,但只有没有自动化,在宏观计划中证明只是纯粹的乏味。

onlydel属性,可用于 控制simp。仅通过试验跟踪来说,即使simp也会调用其他证明方法,类似于auto调用simpblast和其他人的方式。

我认为你不能阻止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跟踪以查看将要执行的基本重写,例如TrueFalse的基本重写。如果你甚至不想那样,那么你必须采用像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.
*)