summaryrefslogtreecommitdiffstats
path: root/proof-sketch.txt
blob: 4a79e075b9162151075d8f9f9eb2051ff329c4f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
                The Three Valued Halting Problem is Undecidable
                         Kaz Kylheku <kaz@kylheku.com>
                                January 15 2015

Abstract:

  Some researchers do not accept the conventional halting problem proof, namely
  that the halting function is not computable. A three-valued version of
  the problem has been proposed to address the alleged problems. However,
  this succumbs to the same techniques. It is shown via an informal proof
  sketch that adding a third value to the range of a halting decider
  computation, such that computations can use this third value to complain
  that they are being tricked, does not work. The proof sketch shows that such
  a third value is not total computable, which means that three-valued halting
  computations cannot reliably detect the "self-referential trickery" of the
  conventional halting problem proof.

Background:

  The halting function is a boolean-valued, two-parameter mathematical function.
  Its parameters are a program and an input. The halting function is true for a
  <program, input> pair which terminates: that is to say, if that program
  terminates when operating on that input. The halting function is false for a
  <program, input> pair when the program does not terminate when processing
  that input. The halting function is total: it provides a boolean value for
  all possible programs and inputs.

  It is not possible to create a computational function (or algorithm) which
  calculates the halting function: a computational function which is total.
  That is to say, a computational function which can examine any <program,
  input> pair, perform a calculation, and then terminate, providing an output
  value which corresponds to the halting function's value. This was
  first shown by Alan Turing[1].

  There is a proof of this which involves a trick. For any function which is
  proposed as a halting decider (a computational implementation of the halting
  function), it is possible to create a wrapper program which incorporates that
  halting decider function. The program passes a representation of itself, and
  to its input down to the halting decider, takes the return value of the
  decider, and then behaves in a way which contradicts that return value: if
  the return value is true ("the program halts on that input") then the program
  deliberately fails to halt, by entering an infinite loop. If the return value
  is false ("the program does not halt"), the program immediately halts.  By
  itself, this does not create a counterexample which proves that the halting
  decider is wrong. One more trick is needed: the program is run, with that
  program itself as its own input. At that point, the decider's return value is
  a comment on the very program which is running, and is logically contradicted
  by the behavior of that program. This contradiction shows that the halting
  decider is computing, for that test case, the wrong value, opposite to the
  halting function. If the halting function says that the program, given itself
  as input halts, the halting decider says that it does not halt, and
  vice versa.

  The proof can be sketched like this:

    program program(input) {
      function halting_decider(program, input) {
        # performs some computation and returns true or false
      }

      function main(input) {
        if (halting_decider(this_program, input))
          loop_forever
        else
          exit(0)
      }

      main(input)
    }

  The program is then invoked with itself as input so that input is this_program,
  and so halting_decider is called as halting_decider(this_program,
  this_program).   Thus the program which is executing is the program invoked on
  itself, and the decider is asked to compute whether that exact test case halts
  or not. Of course, the test case is contrived to contradict the decider.

  Every conceivable implementation of halting_decider succumbs to this trick,
  which shows that for every every attempt to make a halting decider,
  test cases can be found which break it, which shows that no function
  can compute the halting for all possible values.

  For detailed formal proofs related to the halting problem and decidability in
  general, the reader can consult [2].

Motivation:

  Some researchers disbelieve the proof of the halting problem, claiming
  that it is a contrived trick and that the test cases which are generated
  by that trick are somehow invalid, the implication being that deciders
  may be legitimately excused for not being able to to determine whether or not
  these cases halt.

  It has been proposed that a three-valued version of the halting problem
  can provide the necessary framework. Under this notion, the halting function
  is augmented to have a third possible return value, so that for each
  <program, input> pair, it assigns the value true, false or error.  The new
  error value has some meaning such as that <program, input> test case
  constitutes a test case which is trying to break the decider through some
  self-referential trick.

  This paper presents a proof sketch which suggests that the three-valued
  halting function is also not total computable. Using a more refined trick,
  three-valued halting deciders can be shown to be incorrect. For any
  halting decider, a test case can be found such that it incorrectly returns
  error in a situation when no trick is going on: a situation when that decider
  could be altered to return true or false without being contradicted.
  Or else, a test case can be found when that decider incorrectly returns
  true or false which are contradicted, failing to identify that as
  an error situation.

The Proof:

  We begin by forming a model of the three-valued decider, and argue
  that this model is completely general: any three-valued decider
  which totally computes the three-valued halting function can conform
  to this model.

  The three-valued decider has to perform some computation which tells it
  whether to return error, or whether to determine the boolean halting value.
  In other words, it has this structure:

    function halting_decider(program, input) {
      if (should_return_error(program, input)) {
        return error
      } else {
        return decide_halting(program, input) # strictly true or false
      }
    }

  The functions should_return_error and decide_halting are subroutines of
  halting_decider: they are effectively part of that function, and their
  implementation may vary among various proposed halting_deciders.

  The attack on the three-valued halting_decider looks like this:

    program program(input) {
      function halting_decider(program, input) {
        # performs some computation and returns error, true or false
      }

      function error_decider(decider, program, input) {
        # performs some computation and returns error, true or false
      }

      function main(input) {
        if (error_decider(halting_decider, program, input)) {
          switch (halting_decider(program, input)) {
          case true:
            exit(0)
          case false:
            loop_forever
          case error:
            print("incorrect error case: no contradiction is going on!")
            abort()
          }
        } else {
          switch (halting_decider(program, input)) {
          case true:
            loop_forever
          case false:
            exit(0)
          case error:
            print("correct error case: contradiction is being perpetrated!")
            abort()
          }
        }
      }

      main(input)
    }

  As before, the program is invoked on itself as input.

  Here is how this setup differs from the two-valued halting problem proof.

  There is a now a new function error_decider. Whereas the two-valued proof
  uses a boiler-plate wrapper program that easily produces invalid test
  cases upon the mere insertion of the halting decider, the three-valued proof
  requires the attacker to not only insert the halting decider under test, but
  to perform a search for a suitable error_decider function. When a suitable
  error_decider is found, then when the resulting program is invoked on itself,
  the halting_decider is shown to be erroneous on that input case.

  What is a suitable error_decider? The suitable error_decider is a function
  which, for at least the relevant input arguments required in the test case,
  produces the same result as the halting_decider's should_return_error
  subroutine.

  In the case when error_decider(halting_decider, program, input)  happens to
  produce the same value as should_return_error(program, input), the
  halting_decider calculates the incorrect value.

  In this situation, should_return_error might return true, indicating
  to halting_decider that it should return the error code, because a trick is
  supposedly being instigated.  But if that is so, then the wrapper program's
  error_decider also returns true, causing the main function to execute the
  consequent branch of the if statement, in which no trickery is going on: the
  true value maps to termination, and false to non-termination.  The decider's
  error case ends up here and is reported as being incorrect.  On the other
  hand, if should_return_error indicates false, then the halting_decider will
  return true or false instead of error. But error_decider also returns false,
  and so the halting_decider's true or false value is routed to the second
  switch statement, in the antecedent "else" branch of the if inside main.
  In that statement, the true or false is met with contradicting behavior,
  showing it to be erroneous, similarly to the situation in the two-valued
  halting proof.

  All that is left is question: can an error_decider always be found
  to attack any possible halting_decider?  This is true because halting_decider
  has to compute somehow whether or not to return error. That computation
  corresponds to a function (which we modeled as should_return_error: a
  subroutine of the halting_decider).

  Now, a function is a finite entity chosen from a countably infinite
  enumerable set (the set of all possible functions).

  The attacker simply has to enumerate through the sequence of all possible
  functions that are suitable as error_decider, and keep re-generating the
  program with each successive choice of function.  This search is
  guaranteed to eventually find an error_decider function which matches the
  computation of the halting_decider's should_return_error function---at least
  on the relevant arguments, if not the entire argument space.

Remarks:

  The concept of a three-valued halting decider function with only two
  parameters is troublesome, because we would like to maintain the property that
  we are exploring the relationship between a mathematical function, and a
  procedure which computes it.  Yet, the error value appears to be a property
  of the decider, not of the function: it refers to the inability of the
  decider to compute something.  In the three-valued decider situation, the
  underlying mathematical function must in fact be regarded as in fact having
  three parameters: halt3(decider, program, input). The function either decides
  whether the <program, input> pair halts. Or else it declares error. The error
  return means this: "neither true nor false is the correct return value for
  the specified decider with regard to the <program, input> test case". Thus if
  that decider correctly computes the three-valued halting function, it must
  return error the inputs (program, input). The third argument is implicit: it
  is that decider itself.  The decider knows its own identity.

  It must be remembered that if the value of halt3(d, p, i) is error,
  the function halt(p, i) still has a true or false value: the program
  either halts or doesn't halt. halt3(d, p, i) = error asserts that
  decider d is not able to determine this, because it is somehow being "gamed"
  by that test case.

  This is also the reason we have three arguments in the error_decider: for
  that function, the three-argument space is made explicit. It does not refer
  to itself but to a specific decider that it is passed as an argument.

  Thus, in this proof sketch, the argument is essentially this:
  a decider d(p, i) cannot compute halt3(d, p, i) over all
  possible p, i combinations.

Objection:

  I hereby anticipate the following objection to the proof. Surely,
  if error_decider is found which is similar to should_return_error,
  then in fact since should_return_error is a subroutine of the
  halting_decider, then in fact the decider is the target of foul play.

  However, this is not the case. The error_decider function isn't actually
  similar to should_return_error, let alone the same. It only has to have the
  same value as should_return_error for one single combination of argument
  values: that which occurs in the test case. We have to consider this in the
  light of the argument space of these functions being infinite! Two
  functions over infinite domains which agree in just one value are hardly similar at
  all, by any sane definition of similar.

  The error_decider is independently discovered; there is no requirement that
  it be reverse-engineered out of any piece of the halting decider. It may be
  discovered by a fair search which proceeds through the possible functions
  until the inevitable event that the halting decider happens to break.

  By coincidence, error_decider could perform exactly the same computation as
  should_return_error, but this is not a requirement of the proof.
  Furthermore, more than one error_decider exists that can produce the
  necessary test case.

References:

[1] _On computable numbers, with an application to the Entscheidungsproblem_,
    Alan Turing, Proceedings of the London Mathematical Society,
    Series 2, Volume 42 (1937), pp 230–265.

[2] _Mathematical Logic_, Stephen Cole Kleene, 1966,
    Chapter V: Computability and Decidability.