The efficient search and backtracking mechanism described
in section 2.1
is not aware of the nature of the particular programming language
given by
, the set of initial instructions for modifying states.
The language could be list-oriented such as LISP,
or based on matrix operations for neural network-like
parallel architectures, etc.
For the experiments we wrote an interpreter for an exemplary, stack-based,
universal programming language inspired by FORTH [8],
whose disciples praise its beauty and the compactness of its programs.
Each task's tape holds its state:
various stack-like data structures represented
as sequences of integers, including a data stack ds
(with stack pointer dp)
for function arguments, an auxiliary data stack Ds,
a function stack fns of entries describing (possibly recursive)
functions defined by the system itself,
a callstack cs (with stack pointer cp and top entry
)
for calling functions,
where local variable
is the current instruction pointer,
and base pointer
points into ds below the values
considered as arguments of the most recent function call:
Any instruction of the form
inst (
)
expects its
arguments
on top of ds, and replaces
them by its return values.
Illegal use of any instruction will cause the currently tested
program prefix to halt. In particular, it is illegal
to set variables (such as stack pointers or instruction pointers)
to values outside their prewired ranges,
or to pop empty stacks, or to divide by 0,
or to call nonexistent functions,
or to change probabilities of nonexistent tokens,
etc. Try (Section 2.1) will interrupt prefixes
as soon as their
.
Instructions.
We defined 68 instructions,
such as oldq(n) for calling the
-th previously
found program
, or
getq(n) for making a copy of
on stack ds
(e.g., to edit it with additional instructions).
Lack of space prohibits to explain all instructions
(see [9])
-- we have to limit ourselves to the few
appearing in solutions found in the experiments, using
readable names instead of their numbers:
Instruction c1() returns constant 1.
Similarly for c2(), ..., c5().
dec(x) returns
;
by2(x) returns
;
grt(x,y) returns 1 if
, otherwise 0;
delD() decrements stack pointer Dp of Ds;
fromD() returns the top of Ds;
toD() pushes the top entry of
onto Ds;
cpn(n) copies the n topmost ds entries onto the top of ds,
increasing dp by
;
cpnb(n) copies
ds entries above the
-th ds entry
onto the top of ds;
exec(n) interprets
as the number of an instruction and executes it;
bsf(n)
considers the entries
on stack ds above its
-th entry
as code and uses callstack cs to call this code
(code is executed by step 1 of Try (Section 2.1),
one instruction at a time;
the instruction ret() causes a
return to the address of the next instruction right after the
calling instruction).
Given
input arguments on ds,
instruction defnp()
pushes onto ds the begin of a definition
of a procedure with
inputs;
this procedure returns if its topmost
input is 0, otherwise decrements it.
callp() pushes onto ds
code for a call of the most recently defined function / procedure.
Both defnp and callp
also push code for making a fresh copy of the inputs of the most
recently defined code, expected on top of ds.
endnp() pushes code for returning from the current call,
then calls the code generated so far on stack ds
above the
inputs, applying the code to a copy of the inputs
on top of
.
boostq(i) sequentially goes through all tokens of
the
-th self-discovered frozen program,
boosting each token's probability by
adding
to its enumerator and also to the denominator
shared by all instruction probabilities --
denominator and all numerators are stored on tape, defining distribution
.
Initialization.
Given any task, we add task-specific instructions.
We start with a maximum entropy distribution on the
(all numerators set to 1),
then insert substantial prior bias by
assigning the lowest (easily computable) instruction numbers to
the task-specific instructions, and by boosting (see above)
the initial probabilities of appropriate ``small number pushers''
(such as c1, c2, c3)
that push onto ds
the numbers of the task-specific instructions, such that they
become executable as part of code on ds.
We also boost the probabilities of the simple arithmetic instructions
by2 and dec, such that the system can easily
create other integers from the probable ones (e.g., code
sequence (c3 by2 by2 dec) will return integer 11).
Finally we also boost boostq.