On 03/03/2021 18:41, olcott wrote:
On 3/3/2021 8:54 AM, Mike Terry wrote:
On 02/03/2021 03:32, olcott wrote:
On 3/1/2021 9:05 PM, olcott wrote:
On 3/1/2021 8:22 PM, Mike Terry wrote:
On 02/03/2021 01:31, olcott wrote:
On 3/1/2021 7:00 PM, Mike Terry wrote:
On 01/03/2021 23:00, olcott wrote:
On 3/1/2021 4:42 PM, Mike Terry wrote:
On 01/03/2021 20:22, olcott wrote:
On 3/1/2021 1:39 PM, Kaz Kylheku wrote:
On 2021-03-01, olcott <NoOne@NoWhere.com> wrote:
On 3/1/2021 12:25 PM, Kaz Kylheku wrote:
On 2021-03-01, olcott <NoOne@NoWhere.com> wrote:
On 3/1/2021 10:52 AM, Kaz Kylheku wrote:
I already acknowledged this. When an equivalent
computation is run on a
machine without resource limits (such as a TM) then this >>>>>>>>>>>>>>> computation is
infinitely recursive.
That depends on what you mean by "equivalent". Since you >>>>>>>>>>>>>> have tied the
validity your work to the x86 representation, a Turing >>>>>>>>>>>>>> Machine version
of your program, to be equivalent, has to contain the x86 >>>>>>>>>>>>>> program, and
the simulator, faithfully reproducing the same resource >>>>>>>>>>>>>> limitations.
A RASP machine is known to be equivalent to a UTM.
Thus the RASP is to the RAM as the Universal Turing machine >>>>>>>>>>>>> is to the
Turing machine. The RASP is an example of the von Neumann >>>>>>>>>>>>> architecture.
https://en.wikipedia.org/wiki/Random-access_stored-program_machine
The x86 language is another exapmle of the von Neumann >>>>>>>>>>>>> architecture.
All computations implemented using the von Neumann
architecture are
equivalent to one another as long as they do not require >>>>>>>>>>>>> more memory
than is available.
Thus all computations implemented using the von Neumann >>>>>>>>>>>>> architecture are
equivalent UTM computations as long as they do not require >>>>>>>>>>>>> more memory
than is available.
That is true; but an infinite recursion does require
When I say that the x86 emulator: Simulate() is
functionally equivalent
to this code:
int Simulate(u32 P, u32 I)
{
((void(*)(int))P)(I);
return 1;
}
That is all that need be said about the capabilities of >>>>>>>>>>>>>>> the x86 emulator.
Yes; then clearly, the H_Hat(P, P) call (with the right >>>>>>>>>>>>>> casts put in)
denotes a runaway recursive call.
So we agree on this point?
Why wouldn't we? It's obviously a runaway recursive call. >>>>>>>>>>>>
When we know that Simulate() is an x86 emulator that is >>>>>>>>>>>>> functionally
equivalent to direct execution of its input then from the >>>>>>>>>>>>> execution
trace of the execution of this input (including the inputs >>>>>>>>>>>>> to functions
stored on the stack) alone we can decide that H_Hat() is >>>>>>>>>>>>> infinitely
recursive.
Yes.
In a static call graph of a program, we can spot loops. >>>>>>>>>>>>
For instance a simple infinite loop occurs when a basic >>>>>>>>>>>> block jumps to
itself.
A basic block has no branching instructions anywhere, except >>>>>>>>>>>> as possibly
the last instruction. If that last instruction is an
unconditional jump
back to that block, that is an infinite loop.
Compilers do this kind of control flow analysis to find loops, >>>>>>>>>>>> where certain optimizations can be concentrated (e.g. giving >>>>>>>>>>>> more
registers to variables in nested loops).
If we monitor the execution dynamically, we can likewise >>>>>>>>>>>> identify
"dynamic basic blocks" sequences of unconditionally executed >>>>>>>>>>>> instructions with no branches, and we can detect infinite >>>>>>>>>>>> loops.
(Possibly ones that contain a stack push, if the jump is >>>>>>>>>>>> actually
a subroutine call.)
If we simply identify the situation, but don't interfere in >>>>>>>>>>>> it, then the
situation persists: there is runaway recursion.
When this same simulator monitors its own simulation it could >>>>>>>>>>> figure out that the H_Hat() invocation of itself is
infinitely recursive.
I'm afraid that's "wrong-headed" thinking, because Simulate >>>>>>>>>> simply DOES NOT monitor its own simulation, so you can't talk >>>>>>>>>> about when it does something IT DOESN'T DO. :) The same
mental confusion underlies your ".. /would/ fail to terminate >>>>>>>>>> /if/ ABNHBD() didn't abort the emulation", and other
anthropomorphisms you make for TMs.
So in other words you cannot possibly begin to imagine that a >>>>>>>>> software function could be adapted to add functionality.
/We/ can figure out that the above H_Hat is infinitely
recursive, but /we/ are not Simulate, so H_Hat is not our
"We_hat", and this says nothing about the Linz proof.
The /Simulate/ as you've described it (pure emulation)
/cannot/ "figure it out", because it fails to return and so >>>>>>>>>> doesn't "figure" anything. Again, nothing much to say re HP >>>>>>>>>> proof.
We could code a /new/ emulator routine Simulate2 (distinct >>>>>>>>>> from H_Hat and Simulate above) which has monitoring logic, and >>>>>>>>>> use it to examine a simulation of the H_Hat above calling
Simulate(H_Hat) (note: NOT calling Simulate2...), and this >>>>>>>>>> Simulate2 could correctly decide that H_Hat(H_Hat) does not >>>>>>>>>> terminate.
But H_Hat is not the "hatted" machine for Simulate2, so this >>>>>>>>>> also is of little interest. Nobody claims that "no decider >>>>>>>>>> can correctly decide halting for H_Hat", only that H does not >>>>>>>>>> correctly decide halting for its own H_Hat.
If we "update" H_Hat so that instead of calling Simulate, it >>>>>>>>>> calls the new Simulate2, then we have a /new/ program H_Hat2, >>>>>>>>>> and a /new/ calculation to consider. When H_Hat2 examines the >>>>>>>>>> calculation H_Hat2(H_Hat2), with H_Hat2 calling Simulate2
etc., then H_Hat2 /*cannot*/ "figure out" that the calculation >>>>>>>>>> has infinite recursion, BECAUSE IT DOESN'T. It can certainly >>>>>>>>>> /decide/ that H_Hat2 is infinitely recursive, but that would >>>>>>>>>> just be Simulate2 deciding incorrectly. All in line with Linz >>>>>>>>>> proof.
Remember, Simulate2 is not a "pure emulator" like Simulate, so >>>>>>>>>> the fact that there may be two nested emulated calls to
Simulate2 with the same argument IS NOT PROOF OF INFINITE
RECURSION.
When Simulate2() is identical to Simulate() in every way except >>>>>>>>> that it also examines the execution trace of its input to
figure out that its input is calling Simulate2() in infinite >>>>>>>>> recursion then by logical necessity H_Hat() is still infinitely >>>>>>>>> recursive.
?
H_Hat doesn't call Simulate2. Maybe you mean H_Hat2 ?
Obviously the /H_Hat/ calculation is "still" infinitely
recursive, because that calculation has never changed - by
definition it calls Simulate (not Simulate2).
Hmm, your phrase "figuring out" isn't a standard CS term - I've >>>>>>>> guessed it to mean that Simulate2 returns some different rc (not >>>>>>>> 1) when it detects the nested calls to Simulate2 with the same >>>>>>>> input data, thus making a different decision to the rc=1 [halts] >>>>>>>> decision. Maybe you mean something else?
Mike.
An x86 emulator could keep track of all of the details of the
execution trace of any input. If the original Simulate() was
adapted to have this feature and the names remain the same then
H_Hat() would invoke this updated version of Simulate() and still >>>>>>> be infinitely recursive.
So was my guess right? Does "figuring out" mean that Simulate2
returns some different rc (not 1) when it detects the nested calls >>>>>> to Simulate2 with the same input data, thus making a different
decision to the rc=1 [halts] decision? All you've done is replace >>>>>> "figuring out" with "keep track of" which is no better.
When software engineers add features to existing functions they
do not change the names.
What they do is change the versions. But you are not a software
engineer, so maybe you don't know about version numbers.
Software engineers use version control systems so that if required >>>>>> they can recreate the software at whatever point in the
development cycle is required, so there will not be confusion over >>>>>> what code is being used/discussed at any time.
This is the version number of the whole system at that point in
time. I worked on defense projects using version control. The
function almost always keep their original names and merely acquire
additional functionality.
In contrast, /you/ habitually use the same name for different
programs, and different names for the same programs. I suspect
this is because in part your arguments rely on confusing different >>>>>> programs as being the same thing, in various ways. So it seems to >>>>>> me that you do this deliberately. This is the exact opposite to
how a software engineer or professional programmer would behave.
There are only three programs that I will be talking about:
// P has the machine address of H_Hat()
void H_Hat(u32 P)
{
u32 Input_Halts = Simulate(P, P);
if (Input_Halts)
HERE: goto HERE;
return;
}
That calls Simulate(P, P) and as the conversation progresses will
gradually be adapted step-by-step until it becomes Halts(P, P).
There will not be any version numbers for Simulate() because
Simulate() is merely a conversational convention used to construct
complete understanding of Halts() in small incremental steps.
If it is not deliberate, I suggest you just affix versions to the
end of your programs e.g. "H_Hat2" when they change. That's
easier than full versions, e.g. "H_Hat (V2.1.0.0)" and more
appropriate for newsgroup discussions.
Mike.
int Simulate_1(u32 P, u32 I);
Emulates the x86 code pointed to by machine address P with data at
machine address I.
int Simulate_2(u32 P, u32 I);
Same as Simulate_1() except keeps track of the execution trace of
its input including the data passed to any function invocations.
What does "keeps track of" mean? Does Simulate_2 "do" anything with
this tracked data, or is it just (in this version at least) ignored?
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 292 |
Nodes: | 16 (2 / 14) |
Uptime: | 182:07:12 |
Calls: | 6,616 |
Calls today: | 3 |
Files: | 12,165 |
Messages: | 5,314,396 |