chriswarbo-net: 8a4bc36d3c3dad23e084e6f1fcf93c8308202e05

     1: ---
     2: title: Boltzmann RAM
     3: packages: [ 'mathml' ]
     4: ---
     5: 
     6: Simulating every computable universe (the constructivist/intuitionistic version
     7: of Tegmark's level 4) is actually remarkably easy. Each of those universes can
     8: be simulated by running some computer program (by definition of constrictivism),
     9: so we can simulate all of them by "just" running every possible computer
    10: program!
    11: 
    12: ## A simple existence proof ##
    13: 
    14: There's actually a *single*, very simple, program which will simulate all
    15: others, by [diagonalising](https://en.wikipedia.org/wiki/Diagonal_argument) over
    16: the program lengths and running times:
    17: 
    18:  - Run all programs of length `1`{.unwrap pipe="num | math"} (i.e. programs
    19:    `0` and `1`) for `1`{.unwrap pipe="num | math"} step each.
    20:  - Re-run all programs of length `1`{.unwrap pipe="num | math"}, this time for
    21:    `2`{.unwrap pipe="num | math"} steps. Then run programs of length
    22:    `2`{.unwrap pipe="num | math"} (`00`, `01`, `10` and `11`) for
    23:    `1`{.unwrap pipe="num | math"} step each.
    24:  - Re-run programs of length `1`{.unwrap pipe="num | math"} for
    25:    `3`{.unwrap pipe="num | math"} steps; programs of length
    26:    `2`{.unwrap pipe="num | math"} for `2`{.unwrap pipe="num | math"} steps; and
    27:    run all programs of length `3`{.unwrap pipe="num | math"} (`000`, `001`,
    28:    `010`, etc.) for `1`{.unwrap pipe="num | math"} step each.
    29:  - Keep going in this manner forever: each time re-running all previous programs
    30:    for an extra step, then running all one-bit-longer programs for
    31:    `1`{.unwrap pipe="num | math"} step.
    32: 
    33: If we wait long enough, this loop will eventually run *any* program for *any*
    34: number of steps: including simulations of our universe being run for long enough
    35: to reach the present day!
    36: 
    37: ## A remarkable linear-time solution ##
    38: 
    39: The above algorithm is exponentially slow: each iteration runs twice as many
    40: programs as the last (since there are
    41: `num '2'; var 'l';`{.unwrap pipe="sh | mapply power | math"} programs of length
    42: `l`{.unwrap pipe="var | math"}), so reaching step
    43: `var 'n'; num '1'`{.unwrap pipe="sh | add | math"} of any program will take at
    44: least twice as long as reaching step `n`{.unwrap pipe="var | math"}. That's
    45: *a lot* of overhead, which will soon grind everything to a crawl, and makes this
    46: whole endeavour seem far-fetched.
    47: 
    48: However,
    49: [Schmidhuber suggests](http://people.idsia.ch/~juergen/computeruniverse.html) we
    50: can avoid this in two ways:
    51: 
    52:  - Firstly we can 'spread out' the programs by writing them in a
    53:    [prefix-free code](https://en.wikipedia.org/wiki/Prefix_code). This requires
    54:    more bits to encode our programs, reducing the number of programs of each
    55:    length; i.e. there are *fewer than*
    56:    `num '2'; var 'l';`{.unwrap pipe="sh | mapply power | math"} programs of
    57:    length `l`{.unwrap pipe="var | math"} when encoded in a prefix-free way.
    58:  - Then, instead of re-running with an extra step each time, we instead re-run
    59:    with *double* the number of steps, i.e. running for
    60:    `1`{.unwrap pipe="num | math"} step, then `2`{.unwrap pipe="num | math"}
    61:    steps, then `4`{.unwrap pipe="num | math"} steps, then
    62:    `8`{.unwrap pipe="num | math"} steps, etc.
    63: 
    64: This doubling has an interesting effect: the original algorithm took twice as
    65: long to reach step `var 'n'; num '1'`{.unwrap pipe="sh | add | math"} as to
    66: reach step `n`{.unwrap pipe="var | math"}; yet this algorithm can reach step
    67: `num '2'; var 'n'`{.unwrap pipe="sh | mult | math"}: that's *linear time*! In
    68: other words, nothing will slow down over time: it's as if we were running each
    69: program on a dedicated computer, yet we're actually running *all* programs on a
    70: *single* machine!
    71: 
    72: ```{pipe="sh > runtime.mml"}
    73: {
    74:   num '1'
    75:   { num '2'; var 'l'; } | mapply 'power'
    76: } | mapply 'divide'
    77: ```
    78: 
    79: The reason this works is that our changes (prefix-free encoding and
    80: exponentially-distributed running times) cause the allocation of runtime between
    81: programs to obey the [Kraft
    82: inequality](https://en.wikipedia.org/wiki/Kraft%E2%80%93McMillan_inequality).
    83: Each program gets a *constant fraction* of our runtime (equivalent to running
    84: on a dedicated, albeit slower, machine). The downside is those fractions get
    85: *very* small for larger programs: those of length `1`{.unwrap pipe="num | math"}
    86: will get `num '1'; num '2';`{.unwrap pipe="sh | mapply divide | math"} the
    87: runtime, programs of length `2`{.unwrap pipe="num | math"} will get
    88: `num '1'; num '4';`{.unwrap pipe="sh | mapply divide | math"} of the runtime,
    89: and in general programs of length `l`{.unwrap pipe="var | math"} will get
    90: `cat runtime.mml`{.unwrap pipe="sh | math"} of the runtime. The use of a
    91: prefix-free code ensures that there are few-enough programs of each length to
    92: avoid these fractions adding up to more than `1`{.unwrap pipe="num | math"}.
    93: 
    94: ```{pipe="sh > 10.mml"}
    95: {
    96:   {
    97:     num '1'
    98:     { num '2'; num '10'; } | mapply 'power'
    99:   } | mapply 'divide'
   100:   { num '1'; num '1024'; } | mapply 'divide'
   101: } | mapply 'eq'
   102: ```
   103: 
   104: ```{pipe="sh > sum.mml"}
   105: {
   106:   {
   107:     num '1'
   108:     num '2'
   109:     num '4'
   110:     num '8'
   111:     num '16'
   112:     num '32'
   113:     num '64'
   114:   } | add
   115:   num '127'
   116: } | mapply 'eq'
   117: ```
   118: 
   119: Finally, the *overall speed* of each program is only half of its allocated
   120: runtime, so a program of (prefix-free encoded) length
   121: `10`{.unwrap pipe="num | math"} will get
   122: `cat 10.mml`{.unwrap pipe="sh | math"} of the runtime, but only run at
   123: `num '1'; num '2048';`{.unwrap pipe="sh | mapply divide | math"} of its normal
   124: speed. The reason is that we keep restarting everything. As an example, for a
   125: program to reach step `100`{.unwrap pipe="num | math"} of its execution it must
   126: be restarted many times, which wastes some of its allocated runtime: first it
   127: wastes `1`{.unwrap pipe="num | math"} step, then is later restarted and run for
   128: `2`{.unwrap pipe="num | math"} steps (making `3`{.unwrap pipe="num | math"}
   129: wasted steps so far); then `4`{.unwrap pipe="num | math"} steps
   130: (`7`{.unwrap pipe="num | math"} wasted so far); and so on. *Eventually* it will
   131: be run for `128`{.unwrap pipe="num | math"} steps, which is enough for it to
   132: reach step `100`{.unwrap pipe="num | math"} that we wanted. At that point it's
   133: wasted `cat sum.mml`{.unwrap pipe="sh | math"} steps; and in general, when we
   134: re-run a program for `S`{.unwrap pipe="var | math"} steps, it means we've
   135: already wasted (one fewer than) `S`{.unwrap pipe="var | math"} steps on it so
   136: far. Since we're thus spending (just less than) twice as many steps to reach
   137: each point of a program's execution, this restarting is hence causing every
   138: program to run at around half the speed it otherwise would.
   139: 
   140: Whilst these linear slowdowns of each program are exponential in their length;
   141: they are nevertheless *constant over time*. Hence if we start this program
   142: running, it will (gradually) spin-up a simulation of every possible universe
   143: simultaneously (albeit most will be running at an extremely slow rate).
   144: 
   145: ## Implications ##
   146: 
   147: The "simulation hypothesis" has recently entered popular culture as a serious
   148: idea (building on previous, more fanciful incarnations like The Matrix). It asks
   149: whether our Universe was *purposefully created* as a simulation, inside and of
   150: some other "host" Universe (perhaps to investigate historical or counterfactual
   151: scenarios). That's a nice philosophical idea, akin to thought experiments like
   152: the classic [brain in a jar](https://en.wikipedia.org/wiki/Brain_in_a_vat), but
   153: hence just as self-defeating: they are predicated on our inability to discern
   154: the difference between their postulated worlds, so they therefore make no
   155: discernable difference to anything either way!
   156: 
   157: In contrast, any simulations run by the above algorithms are *not* purposeful:
   158: if run for long enough, they *will* simulate our Universe, and countless others;
   159: not because we were specially chosen, but precisely because we are *not*
   160: fundamentally different from any other computer program! Since there's no
   161: postulated "host", to [select and manipulate the
   162: rules](https://en.wikipedia.org/wiki/Omphalos_hypothesis), we can treat this
   163: setup in a scientific way; more akin to a
   164: [Boltzmann brain](https://en.wikipedia.org/wiki/Boltzmann_brain).
   165: 
   166: The idea of a Boltzmann brain is that random movements in some collection of
   167: matter, like a gas cloud, may *occasionally* bring their constituent parts
   168: together in a way which is
   169: [capable of thought](https://en.wikipedia.org/wiki/Cogito,_ergo_sum). The usual
   170: argument against this as an explanation for our observations is that
   171: smaller arrangements of matter are exponentially more likely to occur by chance
   172: than larger arrangements. Hence, if we were such a Boltzmann brain, we would
   173: expect to see very little structure or order around us; yet we observe a vast
   174: Universe filled with structure.
   175: 
   176: (Of course, such "observations" might have simply appeared, by pure coincidence,
   177: as "memories" inside a randomly assembled Boltzmann brain which *thinks* it's
   178: observed a structured Universe; and this line of reasoning can never be
   179: completely disproved. However, the further we travel down that road, the closer
   180: we get to those unfalsifiable-by-definition ideas like the simulation hypothesis
   181: and the brain in a jar; and hence the easier it becomes to dismiss that whole
   182: direction as empirically irrelevant!)
   183: 
   184: ### Boltzmann RAM ###
   185: 
   186: Rather than applying the Boltzmann brain idea to the structure that
   187: *constitutes* us, we can instead apply it to a structure that *computes* us; for
   188: example, by flipping random bits on a Turing machine tape. Again, it's unlikely
   189: that an algorithm will arise by chance for simulating precisely us, or our
   190: Universe, due to how complex it is to describe: it's exponentially more likely
   191: for smaller, simpler programs to arise.
   192: 
   193: However, we've seen above that there are small, simple programs that *do*
   194: simulate our Universe (albeit not *precisely*, since they also run *every other*
   195: Universe alongside). Such programs may well arise by chance, given a
   196: reasonable number of bit flips. In that case, we need to ask how likely our
   197: observations about the Universe would be, if there were so many simulations
   198: running at once. Since "disorder" takes more bits to encode, and the above
   199: algorithms allocate less runtime to longer programs, we would expect most minds
   200: to observe an ordered, structured Universe (so long as there's *enough*
   201: complexity for such minds to arise at all)!
   202: 
   203: ## Conclusion ##
   204: 
   205: Note that this hypothesis does not postulate some 'higher level' Universe that
   206: runs the computation of ours; it could instead be the case that such computation
   207: is just the underlying nature of reality. As an analogy, General Relativity
   208: assumes curved spacetime is the underlying nature of reality, that's why Earth
   209: orbits the Sun, and the motion can be calculated using tensor algebra. Yet that
   210: *does not* imply there must be some intelligent beings performing those
   211: calculations in order for the motion to occur: it just happens, all on its own.
   212: Likewise, it may be the case that computations "just happen", and we can use
   213: complexity arguments to make falsifiable predictions about what's more or less
   214: likely to happen.
   215: 
   216: To his credit, Schmidhuber followed this line of reasoning to arrive at a
   217: prediction that large-scale quantum computation will not be possible: precisely
   218: because the computational effort required to describe that behaviour would make
   219: Universes containing them very unlikely, compared to Universes which constrain
   220: quantum effects to more easily-calculated realms (perhaps taking shortcuts to
   221: arrive at classical solutions on the macro-scale). Whilst I doubt that will be
   222: the case, it's certainly a novel perspective on cosmology; and yet another
   223: justification for trying to scale up our quantum computers!

Generated by git2html.