Kraft says that if we can generate an infinite list of (output, size of program) requirements for building a self-delimiting binary computer C (and repetitions are allowed in a list), then we can generate C's graph, the infinite set of all C's (program, output) pairs. All this works as long as
summed over all the requirements is ≤ 1. I.e., as long as the total algorithmic probability that we are requested to consume is ≤ 1:
That's it! It's a necessary and sufficient condition for the existence of a computer C satisfying these requirements!
So this really tells us a lot about how to construct self-delimiting computers.
That
is called the Kraft inequality in conventional Shannon-style information theory, so I call it that here, even though this theorem is mine, [It's stated and proved for the first time in my paper ``A theory of program size formally identical to information theory,'' J.ACM 22 (1975), pp. 329-340. This paper is also the source of the theorems in the previous and in the next three chapters, unless another reference is explicitly given. Kraft's original theorem deals with finite prefix-free sets of words.] not Kraft's.
For example, if this algorithm is given the requirements
(0 1) (1 2) (2 3) (3 4)...
(i.e., a 1-bit p for 0, a 2-bit p for 1, a 3-bit p for 2, etc.)
it will produce the assignments
(0 0) (10 1) (110 2) (1110 3)...
(i.e., p = 0 yields 0, p = 10 yields 1, p = 110 yields 2, etc.).
In this case the free storage pool will always
only contain a single prefix, one that consists entirely of 1's.
Key insight: Consider each binary program p to simultaneously be the base-two fraction .p in the unit interval of reals between zero and one.
Key fact: if you follow this first-fit algorithm, the free space will appear in blocks in the unit interval program space whose sizes (probability) are all distinct negative powers of two, and in order of increasing size. So if an allocation cannot be made, the piece requested must be larger (at least twice as large) as the last and largest piece. But the total free storage (probability!) is less than twice the size of the last and largest free piece, so allocating this new piece would have violated the Kraft inequality.
To actually run the assigned programs, we have to feed the output of kraft.l into exec.l. So there will in fact be an additional prefix in front of the assigned programs to tell U how to do kraft.l and how to do exec.l.
Kraft is written as a function that is applied to a finite list of requirements and produces the corresponding finite list of assignments. Exec.l will use try to run the requirement generator for more and more time to produce longer and longer lists of requirements. It will then use the Kraft function to transform these into longer and longer lists of assignments, which it will use to actually run individual programs by reading them bit by bit as required. The Kraft function has the property that applying it to a longer list of requirements just produces a longer list of assignments. I.e., it's monotone, it never changes its mind.
Now let's run this software and give it a few simple test cases:
[[[[[
KRAFT INEQUALITY CRITERION FOR CONSTRUCTING COMPUTERS
Take as input requirement pairs (output, size of program)
and produce as output assignment pairs (program, output).
]]]]]
[used to assign a program to an output]
define (extend-with-0s bit-string [to] given-length)
if = length bit-string given-length [then]
bit-string [else]
append (extend-with-0s bit-string [to] - given-length 1)
cons 0 nil
define extend-with-0s
value (lambda (bit-string given-length) (if (= (length b
it-string) given-length) bit-string (append (exten
d-with-0s bit-string (- given-length 1)) (cons 0 n
il))))
[test it]
(extend-with-0s '(1 1 1) [to] 6)
expression (extend-with-0s (' (1 1 1)) 6)
value (1 1 1 0 0 0)
(extend-with-0s '(1 1 1) [to] 5)
expression (extend-with-0s (' (1 1 1)) 5)
value (1 1 1 0 0)
(extend-with-0s '(1 1 1) [to] 4)
expression (extend-with-0s (' (1 1 1)) 4)
value (1 1 1 0)
(extend-with-0s '(1 1 1) [to] 3)
expression (extend-with-0s (' (1 1 1)) 3)
value (1 1 1)
[used to subtract storage from a piece of free storage]
define (remove-piece free-prefix size-of-program)
if = size-of-program length free-prefix
nil [then no storage left, else]
cons append (extend-with-0s free-prefix [to] - size-of-program 1)
cons 1 nil
(remove-piece free-prefix - size-of-program 1)
define remove-piece
value (lambda (free-prefix size-of-program) (if (= size-
of-program (length free-prefix)) nil (cons (append
(extend-with-0s free-prefix (- size-of-program 1)
) (cons 1 nil)) (remove-piece free-prefix (- size-
of-program 1)))))
[test it]
(remove-piece '(1 1 1) 6)
expression (remove-piece (' (1 1 1)) 6)
value ((1 1 1 0 0 1) (1 1 1 0 1) (1 1 1 1))
(remove-piece '(1 1 1) 5)
expression (remove-piece (' (1 1 1)) 5)
value ((1 1 1 0 1) (1 1 1 1))
(remove-piece '(1 1 1) 4)
expression (remove-piece (' (1 1 1)) 4)
value ((1 1 1 1))
(remove-piece '(1 1 1) 3)
expression (remove-piece (' (1 1 1)) 3)
value ()
[
Make-assignments uses debug to show us the
free-space-pool after making each assignment.
If an assignment cannot be made because
there is not enough storage, we just skip it.
]
define (make-assignments free-space-pool requirements)
let free-space-pool debug free-space-pool
[Done just to show pool!]
if atom requirements nil [no requirements => no assignments]
[If so, we're finished!]
let requirement car requirements
let requirements cdr requirements
let output-of-program car requirement
let size-of-program cadr requirement
let already-scanned nil
let not-yet-scanned free-space-pool
let (loop-thru-free-space-pool) [DEFINE IT!]
if atom not-yet-scanned [cannot make this assignment!]
[indicate problem and go to next requirement]
cons not-enough-storage!
(make-assignments free-space-pool requirements)
let free-prefix car not-yet-scanned
let not-yet-scanned cdr not-yet-scanned
if < size-of-program length free-prefix
[doesn't fit --- continue scanning]
let already-scanned
append already-scanned
cons free-prefix nil
(loop-thru-free-space-pool)
[fits! --- make assignment]
[add to list of rest of assignments]
let free-space-pool
append already-scanned
append (remove-piece free-prefix size-of-program)
not-yet-scanned
let assignment
[make assignment - add to list of rest of assignments]
[found free piece where it fits!]
[extend with zeros to correct size]
cons (extend-with-0s free-prefix [to] size-of-program)
cons output-of-program
nil
[return full list of assignments]
cons assignment
(make-assignments free-space-pool requirements)
[NOW DO IT!]
(loop-thru-free-space-pool)
define make-assignments
value (lambda (free-space-pool requirements) ((' (lambda
(free-space-pool) (if (atom requirements) nil (('
(lambda (requirement) ((' (lambda (requirements)
((' (lambda (output-of-program) ((' (lambda (size-
of-program) ((' (lambda (already-scanned) ((' (lam
bda (not-yet-scanned) ((' (lambda (loop-thru-free-
space-pool) (loop-thru-free-space-pool))) (' (lamb
da () (if (atom not-yet-scanned) (cons not-enough-
storage! (make-assignments free-space-pool require
ments)) ((' (lambda (free-prefix) ((' (lambda (not
-yet-scanned) (if (< size-of-program (length free-
prefix)) ((' (lambda (already-scanned) (loop-thru-
free-space-pool))) (append already-scanned (cons f
ree-prefix nil))) ((' (lambda (free-space-pool) ((
' (lambda (assignment) (cons assignment (make-assi
gnments free-space-pool requirements)))) (cons (ex
tend-with-0s free-prefix size-of-program) (cons ou
tput-of-program nil))))) (append already-scanned (
append (remove-piece free-prefix size-of-program)
not-yet-scanned)))))) (cdr not-yet-scanned)))) (ca
r not-yet-scanned)))))))) free-space-pool))) nil))
) (car (cdr requirement))))) (car requirement))))
(cdr requirements)))) (car requirements))))) (debu
g free-space-pool)))
[put it all together]
define (kraft requirements)
let free-space-pool '(()) [everything free]
(make-assignments free-space-pool requirements)
define kraft
value (lambda (requirements) ((' (lambda (free-space-poo
l) (make-assignments free-space-pool requirements)
)) (' (()))))
[TEST KRAFT!]
(kraft '((x 0) (y 1)))
expression (kraft (' ((x 0) (y 1))))
debug (())
debug ()
debug ()
value ((() x) not-enough-storage!)
(kraft '((a 1) (b 0) (c 1)))
expression (kraft (' ((a 1) (b 0) (c 1))))
debug (())
debug ((1))
debug ((1))
debug ()
value (((0) a) not-enough-storage! ((1) c))
(kraft '((x 1) (y 2)))
expression (kraft (' ((x 1) (y 2))))
debug (())
debug ((1))
debug ((1 1))
value (((0) x) ((1 0) y))
(kraft '((a 1) (b 2) (c 3) (d 4) (e 5)))
expression (kraft (' ((a 1) (b 2) (c 3) (d 4) (e 5))))
debug (())
debug ((1))
debug ((1 1))
debug ((1 1 1))
debug ((1 1 1 1))
debug ((1 1 1 1 1))
value (((0) a) ((1 0) b) ((1 1 0) c) ((1 1 1 0) d) ((1 1
1 1 0) e))
(kraft '((e 5) (d 4) (c 3) (b 2) (a 1)))
expression (kraft (' ((e 5) (d 4) (c 3) (b 2) (a 1))))
debug (())
debug ((0 0 0 0 1) (0 0 0 1) (0 0 1) (0 1) (1))
debug ((0 0 0 0 1) (0 0 1) (0 1) (1))
debug ((0 0 0 0 1) (0 1) (1))
debug ((0 0 0 0 1) (1))
debug ((0 0 0 0 1))
value (((0 0 0 0 0) e) ((0 0 0 1) d) ((0 0 1) c) ((0 1)
b) ((1) a))
(kraft '((e 5) (c 3) (d 4) (a 1) (b 2)))
expression (kraft (' ((e 5) (c 3) (d 4) (a 1) (b 2))))
debug (())
debug ((0 0 0 0 1) (0 0 0 1) (0 0 1) (0 1) (1))
debug ((0 0 0 0 1) (0 0 0 1) (0 1) (1))
debug ((0 0 0 0 1) (0 1) (1))
debug ((0 0 0 0 1) (0 1))
debug ((0 0 0 0 1))
value (((0 0 0 0 0) e) ((0 0 1) c) ((0 0 0 1) d) ((1) a)
((0 1) b))
http://www.cs.umaine.edu/~chaitin/ait/kraft.l http://www.cs.umaine.edu/~chaitin/ait/kraft.r http://www.cs.auckland.ac.nz/CDMTCS/chaitin/ait/kraft.l http://www.cs.auckland.ac.nz/CDMTCS/chaitin/ait/kraft.r
We show in Part II that these two Δ's are O(1), but it would be nice to have explicit numerical upper bounds.