|
| 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