Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit ed71b2d

Browse files
MadPidgeonpimsp
andcommitted
feat(computability/tm_computable): define computable (in polytime) for TMs, prove id is computable in constant time (#4048)
We define computability in polynomial time to be used in our future PR on P and NP. We also prove that id is computable in constant time. <!-- put comments you want to keep out of the PR commit here --> Co-authored-by: Pim <spelier.pim@gmail.com>
1 parent dce6b37 commit ed71b2d

File tree

1 file changed

+236
-0
lines changed

1 file changed

+236
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/-
2+
Copyright (c) 2020 Pim Spelier, Daan van Gent. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Pim Spelier, Daan van Gent.
5+
-/
6+
7+
import computability.encoding
8+
import computability.turing_machine
9+
import data.polynomial.basic
10+
import data.polynomial.eval
11+
12+
/-!
13+
# Computable functions
14+
15+
This file contains the definition of a Turing machine with some finiteness conditions
16+
(bundling the definition of TM2 in turing_machine.lean), a definition of when a TM gives a certain
17+
output (in a certain time), and the definition of computability (in polytime or any time function)
18+
of a function between two types that have an encoding (as in encoding.lean).
19+
20+
## Main theorems
21+
22+
- `id_computable_in_poly_time` : a TM + a proof it computes the identity on a type in polytime.
23+
- `id_computable` : a TM + a proof it computes the identity on a type.
24+
25+
## Implementation notes
26+
27+
To count the execution time of a Turing machine, we have decided to count the number of times the
28+
`step` function is used. Each step executes a statement (of type stmt); this is a function, and
29+
generally contains multiple "fundamental" steps (pushing, popping, so on). However, as functions
30+
only contain a finite number of executions and each one is executed at most once, this execution
31+
time is up to multiplication by a constant the amount of fundamental steps.
32+
-/
33+
34+
open computability
35+
36+
namespace turing
37+
/-- A bundled TM2 (an equivalent of the classical Turing machine, defined starting from
38+
the namespace `turing.TM2` in `turing_machine.lean`), with an input and output stack,
39+
a main function, an initial state and some finiteness guarantees. -/
40+
structure fin_tm2 :=
41+
{K : Type} [K_decidable_eq : decidable_eq K] [K_fin : fintype K] -- index type of stacks
42+
(k₀ k₁ : K) -- input and output stack
43+
(Γ : K → Type) -- type of stack elements
44+
(Λ : Type) (main : Λ) [Λ_fin : fintype Λ] -- type of function labels
45+
(σ : Type) (initial_state : σ) -- type of states of the machine
46+
[σ_fin : fintype σ]
47+
[Γk₀_fin : fintype (Γ k₀)]
48+
(M : Λ → turing.TM2.stmt Γ Λ σ) -- the program itself, i.e. one function for every function label
49+
50+
namespace fin_tm2
51+
section
52+
variable (tm : fin_tm2)
53+
54+
instance : decidable_eq tm.K := tm.K_decidable_eq
55+
instance : inhabited tm.σ := ⟨tm.initial_state⟩
56+
57+
/-- The type of statements (functions) corresponding to this TM. -/
58+
@[derive inhabited]
59+
def stmt : Type := turing.TM2.stmt tm.Γ tm.Λ tm.σ
60+
61+
/-- The type of configurations (functions) corresponding to this TM. -/
62+
def cfg : Type := turing.TM2.cfg tm.Γ tm.Λ tm.σ
63+
64+
instance inhabited_cfg : inhabited (cfg tm) :=
65+
turing.TM2.cfg.inhabited _ _ _
66+
67+
/-- The step function corresponding to this TM. -/
68+
@[simp] def step : tm.cfg → option tm.cfg :=
69+
turing.TM2.step tm.M
70+
end
71+
end fin_tm2
72+
73+
/-- The initial configuration corresponding to a list in the input alphabet. -/
74+
def init_list (tm : fin_tm2) (s : list (tm.Γ tm.k₀)) : tm.cfg :=
75+
{ l := option.some tm.main,
76+
var := tm.initial_state,
77+
stk := λ k, @dite (k = tm.k₀) (tm.K_decidable_eq k tm.k₀) (list (tm.Γ k))
78+
(λ h, begin rw h, exact s, end)
79+
(λ _,[]) }
80+
81+
/-- The final configuration corresponding to a list in the output alphabet. -/
82+
def halt_list (tm : fin_tm2) (s : list (tm.Γ tm.k₁)) : tm.cfg :=
83+
{ l := option.none,
84+
var := tm.initial_state,
85+
stk := λ k, @dite (k = tm.k₁) (tm.K_decidable_eq k tm.k₁) (list (tm.Γ k))
86+
(λ h, begin rw h, exact s, end)
87+
(λ _,[]) }
88+
89+
/-- A "proof" of the fact that f eventually reaches b when repeatedly evaluated on a,
90+
remembering the number of steps it takes. -/
91+
structure evals_to {σ : Type*} (f : σ → option σ) (a : σ) (b : option σ) :=
92+
(steps : ℕ)
93+
(evals_in_steps : ((flip bind f)^[steps] a) = b)
94+
95+
/-- A "proof" of the fact that f eventually reaches b in at most m steps when repeatedly evaluated on a,
96+
remembering the number of steps it takes. -/
97+
structure evals_to_in_time {σ : Type*} (f : σ → option σ) (a : σ) (b : option σ) (m : ℕ) extends evals_to f a b :=
98+
(steps_le_m : steps ≤ m)
99+
100+
/-- Reflexivity of `evals_to` in 0 steps. -/
101+
@[refl] def evals_to.refl {σ : Type*} (f : σ → option σ) (a : σ) : evals_to f a a := ⟨0,rfl⟩
102+
103+
/-- Transitivity of `evals_to` in the sum of the numbers of steps. -/
104+
@[trans] def evals_to.trans {σ : Type*} (f : σ → option σ) (a : σ) (b : σ) (c : option σ)
105+
(h₁ : evals_to f a b) (h₂ : evals_to f b c) : evals_to f a c :=
106+
⟨h₂.steps + h₁.steps,
107+
by rw [function.iterate_add_apply,h₁.evals_in_steps,h₂.evals_in_steps]⟩
108+
109+
/-- Reflexivity of `evals_to_in_time` in 0 steps. -/
110+
@[refl] def evals_to_in_time.refl {σ : Type*} (f : σ → option σ) (a : σ) : evals_to_in_time f a a 0 :=
111+
⟨evals_to.refl f a, le_refl 0
112+
113+
/-- Transitivity of `evals_to_in_time` in the sum of the numbers of steps. -/
114+
@[trans] def evals_to_in_time.trans {σ : Type*} (f : σ → option σ) (a : σ) (b : σ) (c : option σ)
115+
(m₁ : ℕ) (m₂ : ℕ) (h₁ : evals_to_in_time f a b m₁) (h₂ : evals_to_in_time f b c m₂) :
116+
evals_to_in_time f a c (m₂ + m₁) :=
117+
⟨evals_to.trans f a b c h₁.to_evals_to h₂.to_evals_to, add_le_add h₂.steps_le_m h₁.steps_le_m⟩
118+
119+
/-- A proof of tm outputting l' when given l. -/
120+
def tm2_outputs (tm : fin_tm2) (l : list (tm.Γ tm.k₀)) (l' : option (list (tm.Γ tm.k₁))) :=
121+
evals_to tm.step (init_list tm l) ((option.map (halt_list tm)) l')
122+
123+
/-- A proof of tm outputting l' when given l in at most m steps. -/
124+
def tm2_outputs_in_time (tm : fin_tm2) (l : list (tm.Γ tm.k₀)) (l' : option (list (tm.Γ tm.k₁))) (m : ℕ) :=
125+
evals_to_in_time tm.step (init_list tm l) ((option.map (halt_list tm)) l') m
126+
127+
/-- The forgetful map, forgetting the upper bound on the number of steps. -/
128+
def tm2_outputs_in_time.to_tm2_outputs {tm : fin_tm2} {l : list (tm.Γ tm.k₀)}
129+
{l' : option (list (tm.Γ tm.k₁))} {m : ℕ} (h : tm2_outputs_in_time tm l l' m) : tm2_outputs tm l l' :=
130+
h.to_evals_to
131+
132+
/-- A Turing machine with input alphabet equivalent to Γ₀ and output alphabet equivalent to Γ₁. -/
133+
structure tm2_computable_aux (Γ₀ Γ₁ : Type) :=
134+
( tm : fin_tm2 )
135+
( input_alphabet : tm.Γ tm.k₀ ≃ Γ₀ )
136+
( output_alphabet : tm.Γ tm.k₁ ≃ Γ₁ )
137+
138+
/-- A Turing machine + a proof it outputs f. -/
139+
structure tm2_computable {α β : Type} (ea : fin_encoding α) (eb : fin_encoding β) (f : α → β)
140+
extends tm2_computable_aux ea.Γ eb.Γ :=
141+
(outputs_fun : ∀ a, tm2_outputs tm (list.map input_alphabet.inv_fun (ea.encode a))
142+
(option.some ((list.map output_alphabet.inv_fun) (eb.encode (f a)))) )
143+
144+
/-- A Turing machine + a time function + a proof it outputs f in at most time(len(input)) steps. -/
145+
structure tm2_computable_in_time {α β : Type} (ea : fin_encoding α) (eb : fin_encoding β) (f : α → β)
146+
extends tm2_computable_aux ea.Γ eb.Γ :=
147+
( time: ℕ → ℕ )
148+
( outputs_fun : ∀ a, tm2_outputs_in_time tm (list.map input_alphabet.inv_fun (ea.encode a))
149+
(option.some ((list.map output_alphabet.inv_fun) (eb.encode (f a))))
150+
(time (ea.encode a).length) )
151+
152+
/-- A Turing machine + a polynomial time function + a proof it outputs f in at most time(len(input)) steps. -/
153+
structure tm2_computable_in_poly_time {α β : Type} (ea : fin_encoding α) (eb : fin_encoding β) (f : α → β)
154+
extends tm2_computable_aux ea.Γ eb.Γ :=
155+
( time: polynomial ℕ )
156+
( outputs_fun : ∀ a, tm2_outputs_in_time tm (list.map input_alphabet.inv_fun (ea.encode a))
157+
(option.some ((list.map output_alphabet.inv_fun) (eb.encode (f a))))
158+
(time.eval (ea.encode a).length) )
159+
160+
/-- A forgetful map, forgetting the time bound on the number of steps. -/
161+
def tm2_computable_in_time.to_tm2_computable {α β : Type} {ea : fin_encoding α} {eb : fin_encoding β}
162+
{f : α → β} (h : tm2_computable_in_time ea eb f) : tm2_computable ea eb f :=
163+
⟨h.to_tm2_computable_aux, λ a, tm2_outputs_in_time.to_tm2_outputs (h.outputs_fun a)⟩
164+
165+
/-- A forgetful map, forgetting that the time function is polynomial. -/
166+
def tm2_computable_in_poly_time.to_tm2_computable_in_time {α β : Type} {ea : fin_encoding α}
167+
{eb : fin_encoding β} {f : α → β} (h : tm2_computable_in_poly_time ea eb f) : tm2_computable_in_time ea eb f :=
168+
⟨h.to_tm2_computable_aux, λ n, h.time.eval n, h.outputs_fun⟩
169+
170+
open turing.TM2.stmt
171+
172+
/-- A Turing machine computing the identity on α. -/
173+
def id_computer {α : Type} (ea : fin_encoding α) : fin_tm2 :=
174+
{ K := unit,
175+
k₀ := ⟨⟩,
176+
k₁ := ⟨⟩,
177+
Γ := λ _, ea.Γ,
178+
Λ := unit,
179+
main := ⟨⟩,
180+
σ := unit,
181+
initial_state := ⟨⟩,
182+
Γk₀_fin := ea.Γ_fin,
183+
M := λ _, halt }
184+
185+
instance inhabited_fin_tm2 : inhabited fin_tm2 :=
186+
⟨id_computer computability.inhabited_fin_encoding.default⟩
187+
188+
noncomputable theory
189+
190+
/-- A proof that the identity map on α is computable in polytime. -/
191+
def id_computable_in_poly_time {α : Type} (ea : fin_encoding α) : @tm2_computable_in_poly_time α α ea ea id :=
192+
{ tm := id_computer ea,
193+
input_alphabet := equiv.cast rfl,
194+
output_alphabet := equiv.cast rfl,
195+
time := 1,
196+
outputs_fun := λ _, { steps := 1,
197+
evals_in_steps := rfl,
198+
steps_le_m := by simp only [polynomial.eval_one] } }
199+
200+
instance inhabited_tm2_computable_in_poly_time : inhabited (tm2_computable_in_poly_time
201+
(default (fin_encoding bool)) (default (fin_encoding bool)) id) :=
202+
⟨id_computable_in_poly_time computability.inhabited_fin_encoding.default⟩
203+
204+
instance inhabited_tm2_outputs_in_time : inhabited (tm2_outputs_in_time (id_computer fin_encoding_bool_bool)
205+
(list.map (equiv.cast rfl).inv_fun [ff]) (some (list.map (equiv.cast rfl).inv_fun [ff])) _)
206+
:=
207+
⟨(id_computable_in_poly_time fin_encoding_bool_bool).outputs_fun ff⟩
208+
209+
instance inhabited_tm2_outputs : inhabited (tm2_outputs (id_computer fin_encoding_bool_bool)
210+
(list.map (equiv.cast rfl).inv_fun [ff]) (some (list.map (equiv.cast rfl).inv_fun [ff]))) :=
211+
⟨tm2_outputs_in_time.to_tm2_outputs turing.inhabited_tm2_outputs_in_time.default⟩
212+
213+
instance inhabited_evals_to_in_time : inhabited (evals_to_in_time (λ _ : unit, some ⟨⟩) ⟨⟩ (some ⟨⟩) 0)
214+
:= ⟨evals_to_in_time.refl _ _⟩
215+
216+
instance inhabited_tm2_evals_to : inhabited (evals_to (λ _ : unit, some ⟨⟩) ⟨⟩ (some ⟨⟩))
217+
:= ⟨evals_to.refl _ _⟩
218+
219+
/-- A proof that the identity map on α is computable in time. -/
220+
def id_computable_in_time {α : Type} (ea : fin_encoding α) : @tm2_computable_in_time α α ea ea id :=
221+
tm2_computable_in_poly_time.to_tm2_computable_in_time $ id_computable_in_poly_time ea
222+
223+
instance inhabited_tm2_computable_in_time : inhabited (tm2_computable_in_time fin_encoding_bool_bool fin_encoding_bool_bool id) :=
224+
⟨id_computable_in_time computability.inhabited_fin_encoding.default⟩
225+
226+
/-- A proof that the identity map on α is computable. -/
227+
def id_computable {α : Type} (ea : fin_encoding α) : @tm2_computable α α ea ea id :=
228+
tm2_computable_in_time.to_tm2_computable $ id_computable_in_time ea
229+
230+
instance inhabited_tm2_computable : inhabited (tm2_computable fin_encoding_bool_bool fin_encoding_bool_bool id) :=
231+
⟨id_computable computability.inhabited_fin_encoding.default⟩
232+
233+
instance inhabited_tm2_computable_aux : inhabited (tm2_computable_aux bool bool) :=
234+
⟨(default (tm2_computable fin_encoding_bool_bool fin_encoding_bool_bool id)).to_tm2_computable_aux⟩
235+
236+
end turing

0 commit comments

Comments
 (0)