From LifeWiki
Jump to navigation Jump to search

This tutorial explains how to install lifelib and use it interactively from a Jupyter notebook.


The recommended way to install the latest version of lifelib is through the Python package manager, pip.

To get a full feature-complete installation of lifelib, run the following in a terminal (Linux / Mac OS X) or command prompt (Windows):

python -m pip install --upgrade python-lifelib notebook imageio matplotlib

This installs the Python bindings for lifelib together with its dependencies. The three optional dependencies (notebook, imageio, and matplotlib) are helpful for interactive use and graphing/plotting.

Extra step (Windows only)

If you're running on Windows, then you'll need to additionally install Cygwin in order to use lifelib. There is functionality built into lifelib to make this easier. In particular, run the following in the command prompt:


which will open a Python interactive prompt. This should be marked with a triple-chevron, >>>, instead of the usual prompt.

In the Python interactive prompt, run the following:

import lifelib

There will be a delay whilst this downloads the Cygwin setup program, followed shortly by a popup window which automatically installs Cygwin and the necessary dependencies for lifelib to run. Wait for this to complete and for the setup window to vanish. After that, you can type:


to leave the Python interactive prompt and return to a regular command prompt.

Creating a notebook

In your terminal or command prompt, launch a Jupyter notebook server by typing the following:

jupyter notebook

This will open a browser window that resembles the following:

Blank jupyter.png

Press the 'New' button in the top-right corner and 'Python 3' to create an untitled notebook. Now in the first cell, enter the following two lines:

import lifelib
sess = lifelib.load_rules('b3s23')
lt = sess.lifetree(n_layers=1, memory=1000)

Then press Shift+Enter to execute the contents of the cell. If this is the first time, it will take a while (20 seconds on a fast machine; up to a minute on a slower machine) to compile lifelib. Subsequently, this delay will not be incurred until next time you upgrade lifelib or compile a different set of rules. This should display a progress bar resembling the following:

Jupyter compile.png

It is worth dissecting the syntax here to see what exactly is happening:

  • The first line imports the lifelib Python package into Python.
  • The second line compiles lifelib with a single rule (B3/S23) and loads the resulting compiled lifelib 'session'.
  • The third line creates a HashLife-compatible container, or lifetree, with 1 bitlayer (so 2 states per cell) and 1000 megabytes of HashLife memory.

Loading and viewing patterns

Once you have a lifetree, you can very easily load patterns from RLE using the pattern method, and then view the result using the viewer method:

Jupyter viewer.png

The output is an embedded interactive copy of Chris Rowett's LifeViewer containing the pattern.

In this case, we've stored the pattern in a Python variable called x. If we make any edits to the pattern in the LifeViewer, these can be 'saved' back to this variable x by pressing Ctrl+S.

Because lifelib uses HashLife, it means that (for example) you can run this pattern for one googol generations and report its population:

Jupyter population.png

For another example, try copying and pasting this code into a Jupyter notebook cell and evaluating it (with Shift+Enter as before).

lidka = lt.pattern("bo7b$obo6b$bo7b8$8bo$6bobo$5b2obo2$4b3o!")
print("Initial population: %d" % lidka.population)
lidka_30k = lidka[30000]
print("Final population: %d" % lidka_30k.population)

Finally, for periodic patterns (oscillators, spaceships, or still-lifes) and glider guns, the gif method allows you to render a seamless animated GIF of the pattern:

Jupyter gif.png


Patterns in lifelib can be manipulated using a concise syntax.

Advancing and transforming

  • x[100] returns a copy of the pattern x advanced by 100 generations.
  • x(55, -89) returns a copy of the pattern x translated 55 cells east and 89 cells north.
  • x('rccw') returns a copy of the pattern x rotated 90 degrees counter-clockwise.
  • x.centre() returns a copy of the pattern x translated such that the bounding box is centred on the origin.
  • x.empty() and x.nonempty() are boolean functions for querying whether the pattern is empty.

Binary operators

  • x + y returns the union of x and y.
  • x ^ y returns the symmetric difference of x and y.
  • x & y returns the intersection of x and y.
  • x - y returns the difference of x and y.

In-place binary operators

  • x += y modifies the pattern in-place by pasting a copy of y on top of it.
  • x -= y removes the cells that are live in y from x.


  • x.population reports the population of the pattern x.
  • x.bounding_box returns a 4-element list of the form (left, top, width, height), or None if the pattern is empty.
  • x.period returns the period of a periodic object (still-life, oscillator or spaceship).
  • x.displacement returns the displacement of a periodic object.
  • x.apgcode returns the apgcode of a periodic object or glider gun.

Slicing, indexing, and coordinates

  • x[5, 8] returns the integer state of the cell with coordinate (5, 8). This can be used as an lvalue to set the state of a cell.
  • x[0:30, 0:20] returns the pattern restricted to a 30x20 rectangle beginning at the origin. This can be used as an lvalue to set the state of all cells within a rectangle (if the rvalue is an integer) or randomly fill a rectangle (if the rvalue is a float or a dictionary).
  • x[arr] returns a shape-(n,) numpy array of cell states at the coordinates specified in the shape-(n,2) numpy array arr.
  • x.coords() returns a shape-(n,2) numpy array containing the coordinates of all nonzero cells in the pattern.

Loading, saving, and serialization

  • x.save('path/to/filename.mc.gz') saves the pattern as a gzipped macrocell file. Supported extensions are .rle, .mc, .rle.gz, and .mc.gz, for consistency with Golly.
  • x.rle_string() returns the RLE of the pattern as a Python string without requiring filesystem I/O.

Example notebooks

There are notebooks in various repositories that you can download and run to learn more about lifelib functionality.