Here's how you would introduce a meaningful axiom (I'm still learning Coq myself so hopefully if I've done this wrong someone will correct me).
With Parameter
we are stipulating the existence of a function called compute
without giving a definition. With Axiom
we are fixing some of its properties (hopefully without introducing a contradiction). Allegedly, Parameter and Axiom do the same thing internally.
Likewise to the declaration of compute
, your Axiom Phi stipulates the existence of a function Phi from nat
to partial
, but you can't do anything with it in Coq
yet because it has no known properties. Note that the existence of Phi doesn't imply anything like "there is an effective enumeration of the partial computable functions".
Here the axiom states that, when called with a greater number of allowed steps of computation, compute
never changes an ACCEPT into a REJECT or vice versa or backs up to NOTYET. With these definitions, I've checked that it is possible to prove the trivial lemma test
as a starter.
Obviously, I haven't carried this through to see if you can prove the undecidability of the halting problem but it should be possible by adding an axiom asserting the existence of a nat
representing a program that computes equivalently to the construction required in proof of the halting problem. Of course, largely the entire point of the proof is lost by doing things this way but there'd still be a little bit left to prove.
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
EDIT: Here's a bit more. After thinking about the problem a bit, I realize I was definitely wrong that such a proof would necessarily have to axiomize the interesting constructions. One can plug in axioms that allow only simple low-level constructions and build higher-level ones on top of them. I'm assuming the goal is to follow Minsky's proof, as it seems simpler to formalize:
http://www.cs.odu.edu/~toida/nerzic/390teched/computability/unsolv-1.html
Here, additional axioms assert 1) the existence of a program that always accept 2) the existence of a program that always rejects and 3) for any three programs conditional
when_accept
when_reject
, the existence of a program that first runs conditional
and then based on that runs when_accept
or when_reject
(all the subprogram invocations are on the same input given to the composed program). With these axioms, it is possible to prove, for any program target
the existence of a program that runs target
and then outputs the opposite result (but also looping forever if target
loops forever). This "negation" construction is just a simple example, and not one of the constructions used in Minsky's proof.
To follow Minsky's proof, one would need to add further axioms for the existence of a program that loops, and for the copier construction. Then add a definition for when a program is a decider, and prove that there is no decider for the halting problem. The codec
axiom saves having to define functions encode_pair
and decode_pair
in Coq
and prove codec
as a Lemma. I think the properties of encode_pair
and decode_pair
will be needed for the copier construction and in the final proof (certainly the definition of a decider for the halting problem would need it as the machine input is a 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.
EDIT#2: I had sort of understood that you wanted to be able to program the constructions in Coq but I hadn't figured out how that could be done at the time of my previous edit. In the following snippet it's done using the axiom exists_program_of_procedure
, which asserts that there exists a nat
representing a program that has the same behavior as any given procedure (at least for well-behaved procedures that are "narrowing" in their computation). Included is a straightforward formalization of Minsky's proof. Your approach has certainly cleaner axioms and will probably result in shorter proof due to the use of modulus
to oracle-ify the steps to stabilization rather than dealing with narrowing
. Most interesting to see is whether your approach can be carried out without the use of Classical
. Update: It appears that your axioms introduce a contradiction since modulus
isn't supposed to be computable (?).
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.
Lemma Phi : nat -> partial. unfold partial; intros; apply (Some 0)
. That being said, I have no idea what it is proving, perhaps that the type partial is inhabited given that the type nat is inhabited? – AtsbyPhi
, it doesn't make sense at all without specifying other axioms thatPhi
should satisfy, e.g.,Phi_inverse
being the inverse ofPhi
, etc. Sorry for the confusion. – Pteromysmodulus
is computable? What if you were to define a function that called modulus on itself specifically for the purpose of changing behavior after that point? – Atsby