在Coq中规范计算理论

14

我尝试通过形式化推理来证明我熟悉的数学定理:停机问题的不可判定性,以及其他计算理论中的各种定理。由于我对计算模型(如图灵机、寄存器机、λ演算等)的细节不感兴趣,因此我试图通过假设 "Axiom" 来表述Coq认为可计算的函数(即类型为“nat -> nat”的可定义函数)的性质。

例如,如果我想告诉Coq部分可计算函数有一个有效的枚举,我可以这样说:

Definition partial := nat -> nat -> option nat.
Axiom Phi : nat -> partial.

在这里,“部分可计算函数”被认为是(总的)可计算函数,给定第一个参数 s后,模拟原始部分可计算函数的计算过程,运行 s 步。我还可以添加其他公理,如“填充引理”,并且可能能够证明停机问题的不可判定性以及一些其他的可计算性理论定理。

我的第一个问题是,我到目前为止是否走在了正确的道路上。难道我试图做的事情显然是不可能完成的,因为不完备现象或 Coq 的类型系统的本质吗?

我的第二个问题涉及到相对化。如果我试图在可计算性理论中证明更严肃的东西,我想考虑在 Oracle 中进行计算。由于通常情况下,Oracles 被构造为部分二值函数的极限,因此似乎(至少是天真地)自然地使 Oracles 具有类型nat -> bool。同时,为了使 Oracle 不平凡,它们必须是不可计算的。考虑到这一点,具有类型nat -> bool的 Oracle 是否有意义?

这里是关于 Oracles 的另一个问题:如果对于每个 Oracle,都存在相对于该特定 Oracle 的部分可计算函数的类型,那将非常好。我能否通过利用 Coq 中的依赖类型系统来实现这一点?这种可能性是否取决于如上段所讨论的形式化 Oracle 的选择?

编辑:上述方法肯定不起作用,因为我需要一个额外的公理:

Axiom Phi_inverse: partial -> nat.

对于oracle来说,这个结论不应该成立。是否存在像我上面描述的方法(我是指那种不涉及计算模型形式化的方法)?

编辑:为了澄清我的意图,我编辑了上面的问题陈述。此外,为了展示我想要的形式化风格,我在这里呈现了停机问题无解的不完整证明:

Require Import Arith.
Require Import Classical.
Definition ext_eq (A B : Set) (f g : A -> B) := forall (x : A), f x = g x.
Definition partial := nat -> nat -> option nat.
Axiom Phi : nat -> partial.
Axiom Phi_inverse : partial -> nat.
Axiom effective_enumeration :
  forall (f : partial) (e : nat),
    Phi e = f <-> Phi_inverse f = e.
Axiom modulus : partial -> nat -> nat.
Axiom persistence :
  forall (f : partial) n s,
    s >= modulus f n -> f s n = f (modulus f n) n.
Definition limit (f : partial) n := f (modulus f n) n.
Definition total (f : partial)
  := forall n, exists s, exists m, f s n = Some m.
Definition flip n := match n with O => 1 | S _ => 0 end.
Definition K e := exists n, limit (Phi e) e = Some n.
Theorem K_is_undecidable :
  ~ exists e,
      total (Phi e)
      /\ forall e', limit (Phi e) e' = Some 0 <-> ~K e'. 
Proof.
  intro.
  destruct H as [e].
  destruct H.
  pose proof (H0 (Phi_inverse (fun s e' =>
                                match (Phi e s e') with
                                  | Some n => Some (flip n)
                                  | None => None end))).
(* to be continued *)

2
那是一种非常不正统的学习Coq的方式。我通过完成“Coq in a hurry”教程,然后是“Software Foundations”自学了Coq(我仍在努力学习中)。我预计在Coq中插入公理相对而言具有较小的教育价值。最可能的情况是你插入Coq的特定公理比你试图表达的定理更加强大。此外,我强烈怀疑C-T命题并不能被显著地形式化,尽管停机问题几乎可以肯定地被形式化。 - Atsby
实际上,通过一点试错,您的公理 Phi 可以在 Coq 中被证明。 "Lemma Phi:nat-> partial。展开部分;介绍;应用(Some 0)"。话虽如此,我不知道它证明了什么,也许是给定类型 nat 是有元素的,则类型 partial 是有元素的? - Atsby
我更新了我的答案,现在包含了明斯基的证明。此外,我并不是逻辑系统(事实上也不是可计算性理论)方面的专家,但是假设“模数”是可计算的,难道没有潜在的矛盾吗?如果你定义一个函数,专门调用自身的模数以改变其行为,那会怎样呢? - Atsby
好的,对于停机问题,你不需要这样做。一般来说,应该引入尽可能弱的公理。但是,对于非可计算的预言机,如果可能的话,似乎需要引入相当多的机制来处理它们。 (续) - Atsby
类似于允许过程输出结果或输入到Oracle和继续(或保存状态)。Oracle不应被公理化为(可计算的)函数。相反,必须以某种方式定义这种过程的评估方式,以量词来定义Oracle的行为,而不是将其作为函数。如果有必要将这些过程转换为“程序”,例如为了利用它们是可数的事实,那么事情可能会变得更加棘手。 - Atsby
显示剩余3条评论
3个回答

4
首先,需要澄清一些问题:
Coq认为可计算的函数(即类型为nat -> nat的可定义函数)
严格来说,Coq并不认为函数是可计算的。Coq的逻辑断言存在某些函数可以被定义,并且您可以使用它们做某些事情,但对于Coq而言,任意函数都是黑盒子。特别地,假设非可计算函数的存在是一致的。
现在,到实际问题。这里提供了一个解决方案,大致上遵循了Atsby的答案。我们将通过Coq函数表示一个图灵机函数,函数类型为nat -> nat -> option bool。想法是第一个参数是输入,第二个参数是我们将运行的最大步数。如果无法产生答案,则输出None,如果计算以产生b作为答案终止,则输出Some b。我利用Ssreflect使代码变得更简单。
Require Import Ssreflect.ssreflect Ssreflect.ssrfun Ssreflect.ssrbool.
Require Import Ssreflect.ssrnat Ssreflect.choice.

Section Halting.

(* [code f c] holds if [f] is representable by some
   Turing machine code [c]. Notice that we don't assume that
   [code] is computable, nor do we assume that all functions 
   [nat -> nat -> option bool] can be represented by some code, 
   which means that we don't rule out the existence of
   non-computable functions. *)
Variable code : (nat -> nat -> option bool) -> nat -> Prop.

(* We assume that we have a [decider] for the halting problem, with
   its specification given by [deciderP]. Specifically, when running
   on a number [m] that represents a pair [(c, n)], where [c] is the
   code for some Turing machine [f] and [n] some input for [f], we
   know that [decider m] will halt at some point, producing [true] iff
   [f] halts on input [n].

   This definition uses a few convenience features from Ssreflect to
   make our lives simpler, namely, the [pickle] function, that
   converts from [nat * nat] to [nat], and the implicit coercion from
   [option] to [bool] ([Some] is mapped to [true], [None] to [false]) *)
Variable decider : nat -> nat -> option bool.
Hypothesis deciderP :
  forall f c, code f c ->
  forall (n : nat),
     (forall s,
        match decider (pickle (c, n)) s with
        | Some true  => exists s', f n s'
        | Some false => forall s', negb (f n s')
        | None => True
        end) /\
     exists s, decider (pickle (c, n)) s.

(* Finally, we define the usual diagonal function, and postulate that
   it is representable by some code [f_code]. *)
Definition f (n : nat) s :=
  match decider (pickle (n, n)) s with
  | Some false => Some false
  | _ => None
  end.
Variable f_code : nat.
Hypothesis f_codeP : code f f_code.

(* The actual proof is straightforward (8 lines long). 
   I'm omitting it to avoid spoiling the fun. *)
Lemma pandora : False.
Proof. (* ... *) Qed.

End Halting.

总之,使用Coq函数谈论停机问题是完全合理的,而且结果非常简单。请注意,上述定理并不强制code与图灵机相关--例如,您可以将上述定理解释为说oracle图灵机的停机问题不能由oracle图灵机解决。最终,使这些结果彼此不同的是底层计算模型的形式化,这正是您想避免的。
至于您尝试启动的模板,假设Phi已经存在并且具有逆,则会导致矛盾。这是通常的对角线论证:
Require Import Ssreflect.ssreflect Ssreflect.ssrfun Ssreflect.ssrbool.
Require Import Ssreflect.ssrnat Ssreflect.choice.

Definition partial := nat -> nat -> option nat.
Axiom Phi : nat -> partial.
Axiom Phi_inverse : partial -> nat.
Axiom effective_enumeration :
  forall (f : partial) (e : nat),
    Phi e = f <-> Phi_inverse f = e.

Lemma pandora : False.
Proof.
pose f n (m : nat) :=
  if Phi n n n is Some p then None
  else Some 0.
pose f_code := Phi_inverse f.
move/effective_enumeration: (erefl f_code) => P.
move: (erefl (f f_code f_code)).
rewrite {1}/f P.
by case: (f _ _).
Qed.

问题在于,即使在外部我们知道Coq可定义的函数与nat之间存在双射,但我们不能在内部断言这个事实。声明非有效的code关系可以解决这个问题。
至于神谕(oracles),将一个类型为nat -> bool的函数作为神谕是有意义的,但您需要确保这样做不违反您在证明中拥有的其他假设。例如,您可以假定所有nat -> nat函数都是可计算的,通过公理化您拥有一个像Phi的函数,但这意味着您的神谕也是可计算的。像上面的code关系一样使用关系允许您在某种程度上拥有最好的两个世界。
最后,在相对化结果方面,它很大程度取决于您想要证明什么。例如,如果您只想显示可以编写特定的神谕函数,则可以编写参数化函数,并在神谕具有特定行为时显示其具有某些属性,而不需要依赖类型。例如:
Definition foo (oracle : nat -> bool) (n : nat) : bool :=
  (* some definition ... *).

Definition oracle_spec (oracle : nat -> bool) : Prop :=
  (* some definition ... *).

Lemma fooP oracle :
  oracle_spec oracle ->
  (* some property of [foo oracle]. *)

最后,这是一个有趣的链接,讨论了依赖类型理论中的Church's thesis,你可能会感兴趣:https://existentialtype.wordpress.com/2012/08/09/churchs-law/


是的,“Coq”默认情况下可能不会假设函数是可计算的(我说“可能”是因为我没有验证)。然而,在将modulus作为函数诸如program_of_procedure或OP的persistence等公理结合使用时,必然会导致不一致性。实质上,这些后面的公理假定了在Coq中定义的所有函数的附加属性。这样做是为了能够在Coq的内置语言中构建程序构造,同时“避免计算模型的形式化”,正如OP所希望的那样。 - Atsby
你的方法似乎更数学化,但我不确定它是否提供了一个允许在其中实现构造的框架。所谓的构造,例如编写一个反转图灵机输出的适配器等,在我记忆中可计算性理论证明中有很多这样的例子。另一方面,链接的文章让我对向Coq添加公理如“program_of_procedure”的安全性产生了一些疑虑。 - Atsby
@Atsby(和@Pteromys),是的,结合不同的公理可能会很棘手,并且可能导致不一致性。但问题甚至在“模数”之前就出现了;我刚刚添加了一个证明,表明“Phi”和“Phi_inverse”会导致不一致性。 - Arthur Azevedo De Amorim
@Atsby 实际上,这种方法的关键在于 恰好 避免实现这些结构并对它们进行推理。通常我们需要执行这些操作的原因是我们还要形式化给定的计算模型并证明它足够表达,这就是问题想要避免的。至于你的公设,我同意它并不明显,但这可能并不成问题,因为你将其放在Prop中,这可以防止你直接编写对角化函数。 - Arthur Azevedo De Amorim
感谢您提供这个有前途的正式化方案。我将尝试使用您的预言机公式来解决我所考虑的问题。有一个问题:是否有一般的经验法则可以避免添加不一致(但似乎合理)的公理?我可能需要后续真实可计算函数的附加公理。 - Pteromys
显示剩余2条评论

3
这里是如何引入一个有意义的公理(我自己还在学习Coq,所以如果我做错了,希望有人能纠正我)。
使用Parameter,我们规定了一个名为compute的函数的存在,但没有给出定义。使用Axiom,我们确定了它的一些属性(希望不会引入矛盾)。据说,参数和公理在内部执行相同的操作。
类似于compute的声明,您的公理Phi规定了从natpartial的函数Phi的存在,但是在Coq中您无法对其进行任何操作,因为它没有已知的属性。请注意,Phi的存在并不意味着“存在可部分计算函数的有效枚举”。
这里的公理表明,在允许更多步骤的计算时,compute永远不会将ACCEPT更改为REJECT或反之亦然,也不会回退到NOTYET。通过这些定义,我已经检查过可以证明一个简单的引理test作为起点。
显然,我没有进行下去以查看是否可以证明停机问题的不可判定性,但是通过添加一个公理来断言存在一个nat,表示与停机问题证明所需构造等效的程序,应该是可能的。当然,通过这种方式做事情,基本上证明的全部意义都失去了,但仍然有一点需要证明。
Inductive result :=
| ACCEPT : result
| REJECT : result
| NOTYET : result.

Definition narrowing a b : Prop :=
(match a with
| ACCEPT => b = ACCEPT
| REJECT => b = REJECT
| NOTYET => True
end).

Parameter compute : nat (* program *) -> nat (* argument *) -> nat (* steps *) -> result.

Axiom compute_narrowing:
forall program input steps steps',
(steps' >= steps) ->
(narrowing (compute program input steps) (compute program input steps')).

Lemma test: ~ exists program input steps, (compute program input steps) = ACCEPT /\ (compute program input (steps + 1)) = NOTYET
编辑:这里有更多内容。经过一番思考,我意识到我肯定是错的,认为这样的证明必须公理化有趣的构造。人们可以插入仅允许简单低级构造并在其上构建更高级别构造的公理。我假设目标是遵循明斯基的证明,因为它似乎更容易形式化:http://www.cs.odu.edu/~toida/nerzic/390teched/computability/unsolv-1.html

在这里,额外的公理断言1)存在一个始终接受的程序2)存在一个始终拒绝的程序3)对于任何三个程序条件语句 当接受时 当拒绝时,存在一个程序首先运行条件语句,然后基于此运行当接受时当拒绝时(所有子程序调用都在给定给组合程序的相同输入上进行)。有了这些公理,就可以证明对于任何程序目标,存在一个程序运行目标,然后输出相反的结果(但如果目标永远循环,则也会永远循环)。这个“否定”构造只是一个简单的例子,而不是明斯基证明中使用的构造之一。

要遵循明斯基的证明,需要添加进一步公理以存在一个循环程序和复制机构造。然后添加一个定义,当一个程序是一个决策者时,并证明不存在一个决策者来解决停机问题。codec公理可以避免在Coq中定义函数encode_pairdecode_pair并将codec证明为引理。我认为encode_pairdecode_pair的属性将需要在复制机构造和最终证明中使用(当然,停机问题的决策者定义将需要它,因为机器输入是一对)。

Require Import List.
Require Import Arith.
Require Import Omega.

Ltac mp_cancel :=
  repeat match goal with
  | [ H2 : ?P -> ?Q , H1 : ?P |- _ ] => specialize (H2 H1)
  end.

Ltac mp_cancel_reflexivity :=
  repeat match goal with
  | [ H1 : ?P = ?P -> ?Q |- _ ] => assert (H_mp_cancel_reflexivity : P = P) by reflexivity; specialize (H1 H_mp_cancel_reflexivity); clear H_mp_cancel_reflexivity
  end.

Inductive result :=
| ACCEPT : result
| REJECT : result
| NOTYET : result
.

Definition narrowing a b : Prop :=
  (match a with
  | ACCEPT => b = ACCEPT
  | REJECT => b = REJECT
  | NOTYET => True
  end)
.

Parameter encode_pair : (nat * nat) -> nat.
Parameter decode_pair : nat -> (nat * nat).

Axiom codec:
  forall a b,
  (decode_pair (encode_pair (a, b))) = (a, b).

Parameter compute : nat (* program *) -> nat (* input *) -> nat (* steps *) -> result.

Axiom compute_narrowing:
  forall program input steps steps',
  (steps' >= steps) -> (narrowing (compute program input steps) (compute program input steps')).

Axiom exists_always_accept:
  exists program_always_accept,
  forall input,
  exists steps,
  (compute program_always_accept input steps) = ACCEPT.

Axiom exists_always_reject:
  exists program_always_reject,
  forall input,
  exists steps,
  (compute program_always_reject input steps) = REJECT.

Definition result_compose_conditional (result_conditional : result) (result_when_accept : result) (result_when_reject : result) : result :=
  (match result_conditional with
  | ACCEPT => result_when_accept
  | REJECT => result_when_reject
  | NOTYET => NOTYET
  end).

Axiom exists_compose_conditional:
  forall program_conditional program_when_accept program_when_reject,
  exists program_composition,
  forall input steps_control steps_when result_conditional result_when_accept result_when_reject,
  (
    ((compute program_conditional input steps_control) = result_conditional) ->
    ((compute program_when_accept input steps_when) = result_when_accept) ->
    ((compute program_when_reject input steps_when) = result_when_reject) ->
    (exists steps_composition, (compute program_composition input steps_composition) = (result_compose_conditional result_conditional result_when_accept result_when_reject))
  ).

Definition result_negation (result_target : result) : result :=
  (match result_target with
  | ACCEPT => REJECT
  | REJECT => ACCEPT
  | NOTYET => NOTYET
  end).

Lemma exists_negation:
  forall program_target,
  exists program_negation,
  forall input steps_target result_target,
  (
    ((compute program_target input steps_target) = result_target) ->
    (exists steps_negation, (compute program_negation input steps_negation) = (result_negation result_target))
  ).
intros.
elim exists_always_accept; intros program_always_accept H_always_accept.
elim exists_always_reject; intros program_always_reject H_always_reject.
elim exists_compose_conditional with (program_conditional := program_target) (program_when_accept := program_always_reject) (program_when_reject := program_always_accept); intros program_negation H_program_negation.
exists program_negation.
intros.
specialize H_always_accept with input. elim H_always_accept; clear H_always_accept; intros steps_accept H_always_accept.
specialize H_always_reject with input. elim H_always_reject; clear H_always_reject; intros steps_reject H_always_reject.
pose (steps_when := (steps_accept + steps_reject)).
specialize H_program_negation with input steps_target steps_when result_target (compute program_always_reject input steps_when) (compute program_always_accept input steps_when).
mp_cancel.
mp_cancel_reflexivity.
elim H_program_negation; clear H_program_negation; intros steps_negation H_program_negation.
exists (steps_negation).
rewrite H_program_negation; clear H_program_negation.
replace (compute program_always_reject input steps_when) with REJECT; symmetry.
replace (compute program_always_accept input steps_when) with ACCEPT; symmetry.
unfold result_compose_conditional.
unfold result_negation.
reflexivity.
assert (T := (compute_narrowing program_always_accept input steps_accept steps_when)).
assert (steps_when >= steps_accept).
unfold steps_when.
omega.
mp_cancel.
unfold narrowing in T.
rewrite H_always_accept in T.
assumption.
assert (T := (compute_narrowing program_always_reject input steps_reject steps_when)).
assert (steps_when >= steps_reject).
unfold steps_when.
omega.
mp_cancel.
unfold narrowing in T.
rewrite H_always_reject in T.
assumption.
Qed.
编辑#2:我有点理解你想要能够在Coq中编程构造,但我之前的编辑时还没有想出如何做到。在下面的代码片段中,使用公理exists_program_of_procedure来完成,该公理断言存在一个表示程序的nat,其行为与任何给定的过程相同(至少对于计算“缩小”的良好过程)。包括Minsky证明的直接形式化。您的方法肯定具有更简洁的公理,并且由于使用modulus将步骤转换为预测器而不是处理narrowing,因此可能会导致更短的证明。最有趣的是看看您的方法是否可以在不使用Classical的情况下执行。更新:似乎您的公理引入了一个矛盾,因为modulus不应该是可计算的(?)。
Require Import List.
Require Import Arith.
Require Import Omega.

Ltac mp_cancel :=
  repeat match goal with
  | [ H2 : ?P -> ?Q , H1 : ?P |- _ ] => specialize (H2 H1)
  end.

Ltac mp_cancel_reflexivity :=
  repeat match goal with
  | [ H1 : ?P = ?P -> ?Q |- _ ] => assert (H_mp_cancel_reflexivity : P = P) by reflexivity; specialize (H1 H_mp_cancel_reflexivity); clear H_mp_cancel_reflexivity
  end.

Parameter encode_pair: (nat * nat) -> nat.
Parameter decode_pair: nat -> (nat * nat).

Axiom decode_encode: forall a b, (decode_pair (encode_pair (a, b))) = (a, b).

Inductive result :=
| ACCEPT : result
| REJECT : result
| NOTYET : result.

Definition result_narrowing (a : result) (b : result) : Prop :=
  (match a with
  | ACCEPT => b = ACCEPT
  | REJECT => b = REJECT
  | NOTYET => True
  end).

Lemma result_narrowing_trans: forall a b c, result_narrowing a b -> result_narrowing b c -> result_narrowing a c.
intros until 0.
destruct a; destruct b; destruct c;
  unfold result_narrowing;
  intros;
  try discriminate;
  reflexivity.
Qed.

Parameter compute: nat (* program *) -> nat (* input *) -> nat (* steps *) -> result.

Axiom compute_narrowing:
  forall program input steps steps',
  (steps' >= steps) -> (result_narrowing (compute program input steps) (compute program input steps')).

Require Import Classical.

Lemma compute_non_divergent:
  forall program input steps steps',
  (compute program input steps) = ACCEPT ->
  (compute program input steps') = REJECT ->
  False.
intros.
assert (T := (classic (steps' >= steps))).
destruct T.
assert (T := (compute_narrowing program input steps steps')).
mp_cancel.
rewrite H, H0 in T.
unfold result_narrowing in T.
discriminate T.
unfold not in H1.
assert (T := (classic (steps' = steps))).
destruct T.
rewrite H2 in H0.
rewrite H in H0.
discriminate.
assert (steps >= steps').
omega.
assert (T := (compute_narrowing program input steps' steps)).
mp_cancel.
rewrite H, H0 in T.
unfold result_narrowing in T.
discriminate.
Qed.

Definition procedure_type := nat (* input *) -> nat (* depth *) -> result.

Definition procedure_narrowing (procedure : procedure_type) : Prop :=
  forall input depth depth',
  (depth' >= depth) -> (result_narrowing (procedure input depth) (procedure input depth')).

Axiom exists_program_of_procedure:
  forall procedure : procedure_type,
  (procedure_narrowing procedure) ->
  exists program,
  forall input,
  (
    forall depth,
    exists steps,
    (result_narrowing (procedure input depth) (compute program input steps))
  ) /\
  (
    forall steps,
    exists depth,
    (result_narrowing (compute program input steps) (procedure input depth))
  ).

Definition program_halts_on_input (program : nat) (input : nat) : Prop :=
  (exists steps, (compute program input steps) <> NOTYET).

Definition program_is_decider (program : nat) : Prop :=
  forall input,
  exists steps,
  (compute program input steps) <> NOTYET.

Definition program_solves_halting_problem_partially (program : nat) : Prop :=
  forall input,
  forall steps,
  (
       ((compute program input steps) = ACCEPT)
    -> (match (decode_pair input) with | (target_program, target_input) => (  (program_halts_on_input target_program target_input)) end)
  ) /\
  (
       ((compute program input steps) = REJECT)
    -> (match (decode_pair input) with | (target_program, target_input) => (~ (program_halts_on_input target_program target_input)) end)
  ).

Lemma minsky: (~ (exists halts, (program_is_decider halts) /\ (program_solves_halting_problem_partially halts))).
unfold not.
intros H_ph.
elim H_ph; clear H_ph; intros invocation_halts [H_ph_d H_ph_b].
pose
  (procedure_modified := (fun (input : nat) (depth : nat) =>
    (match (compute invocation_halts input depth) with
    | ACCEPT => NOTYET
    | REJECT => REJECT
    | NOTYET => NOTYET
    end))).
pose
  (procedure_wrapper := (fun (input : nat) (depth : nat) =>
    (procedure_modified (encode_pair (input, input)) depth))).
unfold procedure_modified in procedure_wrapper.
clear procedure_modified.
assert (T1 := (exists_program_of_procedure procedure_wrapper)).
assert (T2 : (procedure_narrowing procedure_wrapper)).
{
  clear T1.
  unfold procedure_narrowing, procedure_wrapper.
  intros.
  unfold result_narrowing.
  case_eq (compute invocation_halts (encode_pair (input, input)) depth); try intuition.
  assert (T := (compute_narrowing invocation_halts (encode_pair (input, input)) depth depth')).
  mp_cancel.
  rewrite H0 in T.
  unfold result_narrowing in T.
  rewrite T.
  reflexivity.
}
mp_cancel.
clear T2.
elim T1; clear T1; intros program_wrapper H_pw.
unfold procedure_wrapper in H_pw.
clear procedure_wrapper.
specialize (H_pw program_wrapper).
destruct H_pw as [H_pw_fwd H_pw_rev].
unfold program_is_decider in H_ph_d.
specialize (H_ph_d (encode_pair (program_wrapper, program_wrapper))).
elim H_ph_d; clear H_ph_d; intros steps_inner H_ph_d.
unfold program_solves_halting_problem_partially in H_ph_b.
specialize (H_ph_b (encode_pair (program_wrapper, program_wrapper)) steps_inner).
destruct H_ph_b as [H_ph_b_1 H_ph_b_2].
case_eq (compute invocation_halts (encode_pair (program_wrapper, program_wrapper)) steps_inner).
{
  intros.
  rewrite H in *.
  mp_cancel_reflexivity.
  unfold program_halts_on_input in H_ph_b_1.
  rewrite decode_encode in H_ph_b_1.
  elim H_ph_b_1; clear H_ph_b_1; intros steps_outer H_ph_b_1.
  specialize (H_pw_rev steps_outer).
  case_eq (compute program_wrapper program_wrapper steps_outer).
  {
    intros.
    rewrite H0 in *.
    unfold result_narrowing in H_pw_rev.
    elim H_pw_rev; clear H_pw_rev; intros depth H_pw_rev.
    case_eq (compute invocation_halts (encode_pair (program_wrapper, program_wrapper)) depth); intros Hx; rewrite Hx in *; try discriminate.
  }
  {
    intros.
    rewrite H0 in *.
    unfold result_narrowing in H_pw_rev.
    elim H_pw_rev; clear H_pw_rev; intros depth H_pw_rev.
    case_eq (compute invocation_halts (encode_pair (program_wrapper, program_wrapper)) depth); intros Hx; rewrite Hx in *; try discriminate.
    assert (T := (compute_non_divergent invocation_halts (encode_pair (program_wrapper, program_wrapper)) steps_inner depth)).
    mp_cancel.
    assumption.
  }
  {
    intros.
    intuition.
  }
}
{
  intros.
  rewrite H in *.
  mp_cancel_reflexivity.
  unfold not, program_halts_on_input in H_ph_b_2.
  specialize (H_pw_fwd steps_inner).
  rewrite H in H_pw_fwd.
  unfold result_narrowing in H_pw_fwd.
  elim H_pw_fwd; intros.
  rewrite decode_encode in H_ph_b_2.
  contradict H_ph_b_2.
  exists x.
  unfold not.
  intros.
  rewrite H0 in H1.
  discriminate.
}
{
  intros.
  unfold not in H_ph_d.
  mp_cancel.
  assumption.
}
Qed.

我编辑了陈述以澄清我的意图。此外,如果我理解正确,(至少第一个版本的)你的解决方案与我的更或多或少相等。 - Pteromys

1
我想新增一条回答,以详细说明我在评论中提出的有关处理神谕问题的方案。我不确定DeAmorim用于处理神谕的方案是否能够像我提出的方案一样有效。这里的哲学思想是应该避免假设存在具有各种属性的结构,而是直接定义它们(例如,作为Coq函数),并展示它们具有所需的属性。因为我们不希望将图灵机的实际描述处理到状态机的级别,所以使用的技巧是假设从Coq函数到程序的映射。当假定Coq函数可以映射到程序时,逻辑在引入一个不可计算的神谕(以Coq函数形式)时必然变得不一致(即使显然没有提供其构造性定义)。虽然不太可能在手动证明中利用这种不一致性,但完全避免不一致性更加令人放心。
然而,目前还不清楚这种提出的策略是否能够实现。DeAmorim回答中链接的文章称:“该主张是教会定理在ETT中作为一种类型(命题)是错误的,也就是说它导致了矛盾。”其中的数学知识似乎超出了我的水平范围,我不确定它是否适用。
这里考虑的示例问题是在假设我们拥有A_TM的oracle的情况下解决H_TM。H_TM告诉我们一个图灵机(“程序”)对于特定输入是否“停机”,而A_TM则告诉我们一个图灵机是否“接受”特定输入。
显然,反过来拥有H_TM的oracle并尝试解决A_TM的情况完全是微不足道的。只需查询oracle以确定程序是否停机,如果是,则可以运行程序以确定其是否接受。
有点不太平凡的是,如果我们拥有A_TM的oracle,就可以决定H_TM。显然,如果机器接受输入,那么oracle查询可以确定这一点。现在的诀窍是构造一个修改后的程序,它执行与原始程序相反的操作(交换ACCEPT和REJECT),并且对这个修改后的程序进行oracle查询,现在确定原始程序是否会拒绝输入。如果oracle拒绝了两种情况,原始程序在给定的输入上循环,我们就拒绝了(在其他情况下,我们接受)。
这里实现的“机制”允许定义类型为nat (* query *) -> nat (* wisdom *) -> bool (* advice *)的oracle验证器。与验证器相对应的真正oracle将输出true,当且仅当存在一个智慧值使得验证器输出true。聪明之处在于,可以从可计算的验证器生成不可计算的oracle,并且单个存在量词足以生成相当强大的oracle。
“带oracle的过程”(pwo)必须基于循环状态实现,因为在这种设置中,不能简单地使用函数应用来调用oracle。 pwo_entails归纳地定义了pwo的评估方式,类似于Software Foundations中ceval定义Imp程序的评估。
A_TM的Oracle验证器orv_atm只需执行wisdom步骤来确定其是否接受,而pwo_hfa最多进行两次Oracle调用来决定H_TM。引理H_from_A表明这是有效的。
与涉及停机问题的先前答案相比,一个重大变化是基本公理已经加强,允许将Coq函数转换为可传递给compute的程序。现在假设存在一个可计算的函数来执行这种转换。以前仅假定存在相应的程序。这个变化是必要的,因为pwo_hfa需要“编程”生成一个程序。
Require Import List.
Require Import Arith.
Require Import Omega.

Ltac mp_cancel :=
  repeat match goal with
  | [ H2 : ?P -> ?Q , H1 : ?P |- _ ] => specialize (H2 H1)
  end.

Ltac mp_cancel_reflexivity :=
  repeat match goal with
  | [ H1 : ?P = ?P -> ?Q |- _ ] => assert (H_mp_cancel_reflexivity : P = P) by reflexivity; specialize (H1 H_mp_cancel_reflexivity); clear H_mp_cancel_reflexivity
  end.

Parameter encode_pair: (nat * nat) -> nat.
Parameter decode_pair: nat -> (nat * nat).

Axiom decode_encode: forall a b, (decode_pair (encode_pair (a, b))) = (a, b).
Axiom encode_decode: forall x,   (encode_pair (decode_pair x)) = x.

Inductive result :=
| ACCEPT : result
| REJECT : result
| NOTYET : result.

Definition result_narrowing (a : result) (b : result) : Prop :=
  (match a with
  | ACCEPT => b = ACCEPT
  | REJECT => b = REJECT
  | NOTYET => True
  end).

Lemma result_narrowing_trans: forall a b c, result_narrowing a b -> result_narrowing b c -> result_narrowing a c.
intros until 0.
destruct a; destruct b; destruct c;
  unfold result_narrowing;
  intros;
  try discriminate;
  reflexivity.
Qed.

Lemma result_push_accept: forall x, result_narrowing ACCEPT x -> x = ACCEPT.
unfold result_narrowing.
intuition.
Qed.

Lemma result_push_reject: forall x, result_narrowing REJECT x -> x = REJECT.
unfold result_narrowing.
intuition.
Qed.

Parameter compute: nat (* program *) -> nat (* input *) -> nat (* steps *) -> result.

Axiom compute_narrowing:
  forall program input steps steps',
  (steps' >= steps) -> (result_narrowing (compute program input steps) (compute program input steps')).

Require Import Classical.

Lemma compute_non_divergent:
  forall program input steps steps',
  (compute program input steps) = ACCEPT ->
  (compute program input steps') = REJECT ->
  False.
intros.
assert (T := (classic (steps' >= steps))).
destruct T.
assert (T := (compute_narrowing program input steps steps')).
mp_cancel.
rewrite H, H0 in T.
unfold result_narrowing in T.
discriminate T.
unfold not in H1.
assert (T := (classic (steps' = steps))).
destruct T.
rewrite H2 in H0.
rewrite H in H0.
discriminate.
assert (steps >= steps').
omega.
assert (T := (compute_narrowing program input steps' steps)).
mp_cancel.
rewrite H, H0 in T.
unfold result_narrowing in T.
discriminate.
Qed.

Definition procedure_type := nat (* input *) -> nat (* depth *) -> result.

Definition procedure_narrowing (procedure : procedure_type) : Prop :=
  forall input depth depth',
  (depth' >= depth) -> (result_narrowing (procedure input depth) (procedure input depth')).

Parameter program_of_procedure: procedure_type (* procedure *) -> nat (* program *).

Axiom program_of_procedure_behavior:
  forall procedure : procedure_type,
  (procedure_narrowing procedure) ->
  forall input,
  (
    forall depth,
    exists steps,
    (result_narrowing (procedure input depth) (compute (program_of_procedure procedure) input steps))
  ) /\
  (
    forall steps,
    exists depth,
    (result_narrowing (compute (program_of_procedure procedure) input steps) (procedure input depth))
  ).

Definition program_halts_on_input (program : nat) (input : nat) : Prop :=
  (exists steps, (compute program input steps) <> NOTYET).

(* orv = oracle verifier *)

Definition orv_type := nat (* query *) -> nat (* wisdom *) -> bool (* advice *).

Definition oracle_accepts (oracle : orv_type) (query : nat) :=
  exists wisdom, (oracle query wisdom) = true.

Definition oracle_rejects (orv : orv_type) (inp : nat) := (~ (oracle_accepts orv inp)).

(* pwo = procedure with oracle *)

Inductive pwo_out :=
| PWO_RESULT : bool (* result *) -> pwo_out
| PWO_ORACLE : nat (* state *) -> nat (* query *) -> pwo_out
.

Definition pwo_type := nat (* input *) -> nat (* state *) -> bool (* advice *) -> pwo_out.

Inductive pwo_entails: orv_type (* oracle *) -> pwo_type (* procedure *) -> nat (* input *) -> nat (* state *) -> bool (* advice *) -> bool (* result *) -> Prop :=
| PwoEntailsResult:
forall oracle procedure input state advice result,
(procedure input state advice) = (PWO_RESULT result) ->
(pwo_entails oracle procedure input state advice result)
| PwoEntailsOracleAccept:
forall oracle procedure input state advice result state' query,
(procedure input state advice) = (PWO_ORACLE state' query) ->
(oracle_accepts oracle query) ->
(pwo_entails oracle procedure input state' true   result) ->
(pwo_entails oracle procedure input state  advice result)
| PwoEntailsOracleReject:
forall oracle procedure input state advice result state' query,
(procedure input state advice) = (PWO_ORACLE state' query) ->
(oracle_rejects oracle query) ->
(pwo_entails oracle procedure input state' false  result) ->
(pwo_entails oracle procedure input state  advice result)
.

Definition pwo_decider_relative (orv : orv_type) (pwo : pwo_type) :=
  forall input,
  (pwo_entails orv pwo input 0 false false) \/
  (pwo_entails orv pwo input 0 false true).

(* define oracle for A_TM (turing machine accepts a particular input) *)

Definition orv_atm (pair_program_input : nat) (wisdom : nat) : bool :=
  (match (decode_pair pair_program_input) with
  | (target_program, target_input) =>
      (match (compute target_program target_input wisdom) with
      | ACCEPT => true
      | _ => false
      end)
  end).

(* define procedure for H_TM (turing machine halts on a particular input) relative to an oracle for A_TM *)

Definition pwo_hfa_construction (target_program : nat) :=
  (fun input depth =>
    (match (compute target_program input depth) with
    | ACCEPT => REJECT
    | REJECT => ACCEPT
    | NOTYET => NOTYET
  end)).

Lemma pwo_hfa_construction_narrowing: forall target_program, (procedure_narrowing (pwo_hfa_construction target_program)).
intros.
unfold procedure_narrowing, result_narrowing, pwo_hfa_construction.
intros.
case_eq (compute target_program input depth); intro; try
(
case_eq (compute target_program input depth'); intro; try reflexivity; try
(
assert (T := (compute_narrowing target_program input depth depth'));
mp_cancel;
unfold result_narrowing in T;
rewrite H0 in T;
rewrite H1 in T;
discriminate
)
).
Qed.

Definition pwo_hfa (input : nat) (state : nat) (advice : bool) : pwo_out :=
  (match (decode_pair input) with
  | (target_program, target_input) =>
      (match state with
      | O =>
        (PWO_ORACLE 1 (encode_pair (target_program, target_input)))
      | (S O) =>
        (if advice
         then (PWO_RESULT true)
         else (PWO_ORACLE 2 (encode_pair ((program_of_procedure (pwo_hfa_construction target_program)), target_input))))
      | _ =>
        (PWO_RESULT advice)
      end)
  end).

Lemma H_from_A:
  exists pwo_hfa,
  (pwo_decider_relative orv_atm pwo_hfa) /\
  (
    forall input,
      (pwo_entails orv_atm pwo_hfa input 0 false true  ->
        (match (decode_pair input) with | (target_program, target_input) => (  (program_halts_on_input target_program target_input)) end)) /\
      (pwo_entails orv_atm pwo_hfa input 0 false false ->
        (match (decode_pair input) with | (target_program, target_input) => (~ (program_halts_on_input target_program target_input)) end))
  )
.
exists pwo_hfa.
split.
{
  unfold pwo_decider_relative.
  intros.
  pose (pair := (decode_pair input)).
  case_eq (pair); intros target_program target_input H_pair.
  replace input with (encode_pair (target_program, target_input)).
  Focus 2.
  {
    rewrite <- H_pair.
    unfold pair.
    apply encode_decode.
  }
  Unfocus.
  assert (T1 := (classic (oracle_accepts orv_atm (encode_pair (target_program, target_input))))).
  destruct T1 as [T1 | T1].
  {
    right.
    eapply PwoEntailsOracleAccept.
    Focus 2.
    eexact T1.
    unfold pwo_hfa.
    rewrite decode_encode.
    instantiate (1 := 1).
    reflexivity.
    apply PwoEntailsResult.
    unfold pwo_hfa.
    rewrite decode_encode.
    reflexivity.
  }
  {
    assert (T2 := (classic (oracle_accepts orv_atm (encode_pair ((program_of_procedure (pwo_hfa_construction target_program)), target_input))))).
    destruct T2 as [T2 | T2].
    {
      right.
      eapply PwoEntailsOracleReject.
      Focus 2.
      unfold oracle_rejects.
      eexact T1.
      unfold pwo_hfa.
      rewrite decode_encode.
      instantiate (1 := 1).
      reflexivity.
      eapply PwoEntailsOracleAccept.
      Focus 2.
      eexact T2.
      unfold pwo_hfa.
      rewrite decode_encode.
      instantiate (1 := 2).
      reflexivity.
      apply PwoEntailsResult.
      unfold pwo_hfa.
      rewrite decode_encode.
      reflexivity.
    }
    {
      left.
      eapply PwoEntailsOracleReject.
      Focus 2.
      unfold oracle_rejects.
      eexact T1.
      unfold pwo_hfa.
      rewrite decode_encode.
      instantiate (1 := 1).
      reflexivity.
      eapply PwoEntailsOracleReject.
      Focus 2.
      eexact T2.
      unfold pwo_hfa.
      rewrite decode_encode.
      instantiate (1 := 2).
      reflexivity.
      apply PwoEntailsResult.
      unfold pwo_hfa.
      rewrite decode_encode.
      reflexivity.
    }
  }
}
{
  intros.
  case_eq (decode_pair input); intros target_program target_input H_input.
  replace input with (encode_pair (target_program, target_input)).
  Focus 2.
  {
    rewrite <- H_input.
    rewrite encode_decode.
    reflexivity.
  }
  Unfocus.
  clear H_input.
  split.
  {
    intros.
    inversion H; subst.
    {
      unfold pwo_hfa in H0.
      rewrite decode_encode in H0.
      discriminate H0.
    }
    {
      unfold pwo_hfa in H0.
      rewrite decode_encode in H0.
      injection H0.
      intros.
      rewrite <- H3 in H1.
      unfold oracle_accepts in H1.
      unfold program_halts_on_input.
      elim H1; intros.
      exists x.
      unfold orv_atm in H5.
      rewrite decode_encode in H5.
      destruct (compute target_program target_input x); try intuition; try discriminate.
    }
    {
      unfold pwo_hfa in H0.
      rewrite decode_encode in H0.
      injection H0.
      intros.
      rewrite <- H4 in *.
      inversion H2; subst.
      {
        unfold pwo_hfa in H5.
        rewrite decode_encode in H5.
        discriminate.
      }
      {
        unfold pwo_hfa in H5.
        rewrite decode_encode in H5.
        injection H5; intros.
        subst.
        unfold oracle_accepts in H6.
        unfold program_halts_on_input.
        elim H6; intros.
        unfold orv_atm in H3.
        rewrite decode_encode in H3.
        case_eq (compute (program_of_procedure (pwo_hfa_construction target_program)) target_input x); intros; rewrite H4 in H3; try discriminate.
        assert
          (T :=
            (program_of_procedure_behavior
              (pwo_hfa_construction target_program)
              (pwo_hfa_construction_narrowing target_program)
              target_input
            )).
        destruct T.
        specialize H9 with x.
        elim H9; intros.
        assert ((pwo_hfa_construction target_program target_input x0) = ACCEPT).
        eapply result_push_accept.
        rewrite <- H4.
        assumption.
        unfold pwo_hfa_construction in H11.
        case_eq (compute target_program target_input x0); intros; rewrite H12 in H11; try discriminate.
        exists x0.
        unfold not.
        intros.
        rewrite H12 in H13.
        discriminate.
      }
      {
        unfold pwo_hfa in H5.
        rewrite decode_encode in H5.
        injection H5; intros.
        subst.
        inversion H7.
        unfold pwo_hfa in H3.
        rewrite decode_encode in H3.
        discriminate H3.
        unfold pwo_hfa in H3.
        rewrite decode_encode in H3.
        discriminate H3.
        unfold pwo_hfa in H3.
        rewrite decode_encode in H3.
        discriminate H3.
      }
    }
  }
  {
    intros.
    unfold not.
    intros.
    inversion H; subst; unfold pwo_hfa in H1; rewrite decode_encode in H1; try discriminate; injection H1; intros; subst.
    {
      inversion H3; subst; unfold pwo_hfa in H4; rewrite decode_encode in H4; try discriminate; injection H4; intros; subst.
    }
    {
      inversion H3; subst; unfold pwo_hfa in H4; rewrite decode_encode in H4; try discriminate; injection H4; intros; subst.
      {
        inversion H6; subst; unfold pwo_hfa in H7; rewrite decode_encode in H7; try discriminate.
      }
      {
        (* oracle rejected both on a program that halts *)
        clear H H3 H1 H6 H4.
        unfold program_halts_on_input in H0.
        elim H0; clear H0; intros.
        unfold not in H.
        case_eq (compute target_program target_input x); intros; try ( solve [ intuition ] ).
        {
          rename H2 into HH.
          unfold oracle_rejects, not in HH.
          apply HH.
          clear HH.
          unfold oracle_accepts.
          exists x.
          unfold orv_atm.
          rewrite decode_encode.
          rewrite H0.
          reflexivity.
        }
        {
          rename H5 into HH.
          unfold oracle_rejects, not in HH.
          apply HH.
          clear HH.
          unfold oracle_accepts.
          assert
          (T :=
            (program_of_procedure_behavior
              (pwo_hfa_construction target_program)
              (pwo_hfa_construction_narrowing target_program)
              target_input
            )).
          destruct T.
          assert ((pwo_hfa_construction target_program target_input x) = ACCEPT).
          unfold pwo_hfa_construction.
          rewrite H0.
          reflexivity.
          specialize H1 with x.
          elim H1; intros.
          rewrite H4 in H5.
          unfold result_narrowing in H5.
          exists x0.
          unfold orv_atm.
          rewrite decode_encode.
          rewrite H5.
          reflexivity.
        }
      }
    }
  }
}
Qed.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接