Lifelib

For general discussion about Conway's Game of Life.
User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Lifelib

Post by calcyman » November 7th, 2018, 10:51 am

I've published the latest version 2.0.17 of python-lifelib to the Python Package Index (PyPI), and added some more documentation to the project. Of particular interest is the Quick Start guide, which explains how to install lifelib:
  • Ensure you have a Python distribution with numpy, jupyter, and pip. For Windows, Linux and Mac OS X, you can install the Anaconda distribution.
  • On Mac OS X, you additionally need to have the command-line developer tools installed. (Specifically, check that you can run g++ --version to ascertain that gcc or clang is installed.)
  • Download the lifelib installation script and run it.
Here's an example running in a Jupyter notebook (Python 3):
Screenshot from 2018-11-07 14-34-44.png
Screenshot from 2018-11-07 14-34-44.png (89.77 KiB) Viewed 19241 times
The .viewer() function displays an embedded copy of Chris Rowett's LifeViewer with the contents of the pattern:
Screenshot from 2018-11-07 14-35-00.png
Screenshot from 2018-11-07 14-35-00.png (42.56 KiB) Viewed 19241 times
You can easily get and set rectangles using the Python slice notation:
Screenshot from 2018-11-07 14-41-24.png
Screenshot from 2018-11-07 14-41-24.png (27.94 KiB) Viewed 19241 times
There are a few more example notebooks available here.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » November 7th, 2018, 11:00 am

(Beginning new post since the forums only allow 3 attachments per post.)

Lifelib supports any of the rules supported by apgsearch, including History variants. You need to be careful to ensure that your lifetree has enough layers to accommodate the pattern:
Screenshot from 2018-11-07 14-49-20.png
Screenshot from 2018-11-07 14-49-20.png (108.51 KiB) Viewed 19238 times
To be safe, the number of layers should be 2 more than the ceiling of the base-2 logarithm of the number of states.

One of the more advanced features is 'pattern matching', which allows you to do a find-and-replace on patterns:
Screenshot from 2018-11-07 14-56-49.png
Screenshot from 2018-11-07 14-56-49.png (64.33 KiB) Viewed 19238 times
In this example, we replaced all of the eater5s (in two orientations) in the p57 loop with Sokwe's p3 catalyst.

By using Python code and the matplotlib plotting library, you can produce custom population plots:
Screenshot from 2018-11-07 15-03-39.png
Screenshot from 2018-11-07 15-03-39.png (47.42 KiB) Viewed 19237 times
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » December 28th, 2018, 12:51 am

Lifelib 2.1.0 has been released. The main new feature is the ability to load rule files (.table / .tree / .rule) such as Langton's Loops:
Screenshot from 2018-12-28 04-19-47.png
Screenshot from 2018-12-28 04-19-47.png (59.31 KiB) Viewed 18588 times
In particular, you can run:

Code: Select all

sess = lifelib.load_rules('path/to/Langtons-Loops.table')
The rule format accepts arbitrary neighbourhoods (as lists of coordinate pairs) and symmetry groups (as lists of generators, each expressed as a list of disjoint cycles). For instance, instead of:

Code: Select all

neighborhood:Moore
symmetries:rotate4reflect
you can write:

Code: Select all

neighborhood:[(0,0), (0,-1), (1,-1), (1,0), (1,1), (0,1), (-1,1), (-1,0), (-1,-1), (0,0)]
symmetries:[[(1, 3, 5, 7), (2, 4, 6, 8)], [(2, 8), (3, 7), (4, 6)]]
In particular, the neighbourhood on which a cell can depend is allowed to be an arbitrary subset of the 17x17 square centred on that cell. (Compare this with the Moore neighbourhood, which is a mere 3x3 square.) As such, each coordinate in each pair in the neighbourhood is allowed to range from -8 to 8, inclusive.

If you have Python 3.6 (or greater) with nutshell installed, then lifelib can additionally accept .ruel files; these are delegated to nutshell to be converted into Golly rule tables, whence they are processed identically.

As with Golly, rule files are limited to 256 states.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Lifelib

Post by simsim314 » February 24th, 2019, 6:18 pm

@calcyman I was wondering can you give a review when you recommend to use lifelib and when golly? Is it more search oriented? What functionality there is in lifelib which you don't have in golly? or is it only a question of performance?

Also I understand you moved to python instead of what you previously thought of making user friendly c++ version of lifelib. It's somewhat unfortunate, can you point me in the direction of how you used lifelib in c++ currently?

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » February 25th, 2019, 6:49 am

simsim314 wrote:@calcyman I was wondering can you give a review when you recommend to use lifelib and when golly? Is it more search oriented?
I'd continue to use Golly for manual editing and experimenting, and continue to use Golly Python scripts to augment that (such as using shift.py). For assembling large patterns, you're probably better off with lifelib. In fact, if you move your Caterloopillar construction script to lifelib, I imagine it would both run faster and handle larger constructions.

As a case in point, trying to construct metapatterns in Golly is incredibly slow and painful (because it's manipulating cell lists instead of directly operating on quadtrees), whereas this runs quickly irrespective of the size of the initial pattern:

https://gitlab.com/apgoucher/slmake/blo ... etafier.py
What functionality there is in lifelib which you don't have in golly?
  • Pattern matching (including finding/replacing subpatterns);
  • Kronecker products (useful for constructing metapatterns);
  • Construction of spaceship streams;
  • Ability to run spaceships and oscillators backwards;
  • Compatibility with LifeViewer (if you're running from a Jupyter notebook).
There isn't the idea of having to store patterns in a limited number of 'layers' either: patterns are objects, so you can pass them to functions and operate upon them.
or is it only a question of performance?
That's the main point: all pattern editing operations (translation, rotation/reflection, boolean operations, convolutions, etc.) operate directly on compressed quadtrees instead of on cell lists, so they're fast in the way that hashlife is. Also, the hashlife implementation itself falls back on a machine-optimised assembly routine for iterating 32x32 blocks, so you don't have to decide between HashLife or QuickLife; the same algorithm is efficient for both structured patterns and random patterns.
Also I understand you moved to python instead of what you previously thought of making user friendly c++ version of lifelib.
You can use either Python (2 or 3) or C++11 (or above) with lifelib. Using Python is recommended for several reasons. Firstly, lifelib's Python bindings are designed to support unbounded integers for operations such as advancing and shifting patterns. So in Python, you can do:

Code: Select all

import lifelib
lt = lifelib.load_rules('b3s23').lifetree()
spacefiller = lt.pattern('''
#N Max
#O Tim Coe
#C A spacefiller that fills space with zebra stripes.
#C www.conwaylife.com/wiki/index.php?title=Max
x = 27, y = 27, rule = B3/S23
18bo8b$17b3o7b$12b3o4b2o6b$11bo2b3o2bob2o4b$10bo3bobo2bobo5b$10bo4bobo
bobob2o2b$12bo4bobo3b2o2b$4o5bobo4bo3bob3o2b$o3b2obob3ob2o9b2ob$o5b2o
5bo13b$bo2b2obo2bo2bob2o10b$7bobobobobobo5b4o$bo2b2obo2bo2bo2b2obob2o
3bo$o5b2o3bobobo3b2o5bo$o3b2obob2o2bo2bo2bob2o2bob$4o5bobobobobobo7b$
10b2obo2bo2bob2o2bob$13bo5b2o5bo$b2o9b2ob3obob2o3bo$2b3obo3bo4bobo5b4o
$2b2o3bobo4bo12b$2b2obobobobo4bo10b$5bobo2bobo3bo10b$4b2obo2b3o2bo11b$
6b2o4b3o12b$7b3o17b$8bo!
''')
spacefiller[10 ** 60].bounding_box
to obtain the bounding box of the spacefiller after 10^60 generations.

Also, the ordeal of switching between rules is smoothed out in the Python bindings; you can just run:

Code: Select all

sess = lifelib.load_rules("b38s23")
It's somewhat unfortunate,
Why so? They have exactly the same performance, because all of the low-level code is implemented in C++ and assembly.
can you point me in the direction of how you used lifelib in c++ currently?
It's a header-only library, so it's best to have the lifelib repository in a subdirectory of your project. Then, most of the core functionality can be included using:

Code: Select all

#include "lifelib/pattern2.h"
Your patterns themselves reside in an object called a lifetree, which is a garbage-collected compressed forest of hashed quadtrees. So you'll need to create a lifetree at the start:

Code: Select all

int main() {
    apg::lifetree<uint32_t, 1> lt(1000);
    // code goes here
    return 0;
}
The template parameter uint32_t means that it uses 32-bit indices internally. The template parameter 1 means that you only want 1-bit states (0 or 1), ideal for 2-state rules. The constructor argument 1000 gives the lifetree a garbage collection threshold of 1000 MB; whenever it exceeds that, it will run garbage collection.

Now you can create patterns inside this lifetree. For example, you might want to run:

Code: Select all

apg::pattern pat(&lt, "file_to_load.mc");
or alternatively:

Code: Select all

apg::pattern lidka(&lt, "bo$obo$bo8$8bo$6bobo$5b2obo2$4b3o!", "b3s23");
You can then do things such as run it for 30000 generations:

Code: Select all

apg::pattern lidka_30k = lidka[30000];
and print its population:

Code: Select all

std::cout << lidka_30k.totalPopulation() << std::endl;
Both the Python and C++11 bindings have full support for the binary operators:
  • Conjunction: & and &=
  • Disjunction: + (or |) and += (or |=)
  • Exclusive disjunction: ^ and ^=
  • Subtraction: - and -=
  • Kronecker product: * and *=
You can also use square brackets to advance and round brackets to shift (again, in both Python and C++11), just like in Golly's glife:

Code: Select all

apg::pattern two_lidkas = lidka + lidka(1000, 0);
More niche functionality is provided in classifier.h (for object separation), upattern.h (for running unhashed patterns -- basically useful only for random soups), and streamlife.h (for a variant of hashlife which doesn't suffer when nearby glider streams run in opposite directions).

If you want to support a different rule in your C++ program, such as B38/S23, you need to run the following in Python (from your project's root directory, assuming that is the immediate parent of the lifelib repository directory):

Code: Select all

from lifelib.autocompile import reset_tree
reset_tree("b38s23")
This is what apgsearch uses to change rules, for instance. You can have up to 8 rules coexisting in a single lifelib by providing a list of rulestrings instead of a single string.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Lifelib

Post by simsim314 » February 25th, 2019, 10:07 am

calcyman wrote:For assembling large patterns, you're probably better off with lifelib. In fact, if you move your Caterloopillar construction script to lifelib, I imagine it would both run faster and handle larger constructions.
Oh very cool! Golly was the limit of caterloopillar at some point. My problem with this is the visualization of large pattern which is not suited for the JS viewer. I saw you used some other visualization which looked kinda interesting, I think it's general issue with large patterns, it's kinda hard to show see them. When you zoom our of caterloopillar you just get a line moving slowly.

How about making some sort of a studio where on the left side you have code and on the right side you have a visualization. I'm talking about rewriting golly but to make viewer + python scripting, that will automatically show the script results and will have something basic like zoom and move. Like in OpenScad if you familiar with the concept, it's a cool and simple way to make such tools.
calcyman wrote:That's the main point: all pattern editing operations (translation, rotation/reflection, boolean operations, convolutions, etc.)
In LifeAPI the main point was usability and simplicity. I mean most of LifeAPI stuff was documentation and several overloads for each function to make it extremely simple to use. I'm thinking that you have made some nice work there, much better than I did, but I think the toyishness that I wanted to have in LifeAPI is sort of lost. What is the best way in your opinion to combine the usability and the extreme user friendliness of LifeAPI (at least as an ideal) with your code? I mean LifeAPI is just a header, you simply drop it in your folder and start work with that. One way I was thinking is to have all the interior logic be replced by lifelib. The other thing is to truly combine forces and make the tool with the viewer and everything, so inside this tool - I would be able to add some function to simplify my life.

Another point is GPU. I've written some basic code in CUDA and for my current project I'll invest a lot of time in coding everything in CUDA because it makes the search work way faster. It would be nice to make a collective effort in this direction as well.

And finally the distributed approach is also interesting option. Is it possible to make general distributive projects and drop them into some queue such that when I need X1000 computational power for my search I drop it in your site, with some Iteration politics which divided between machines?
calcyman wrote:Why so? They have exactly the same performance, because all of the low-level code is implemented in C++ and assembly.
For example you didn't implemented iterators, so you still will need to make loops in python. I know loops are not such a big deal - yet still I would prefer to have an array of iterator representing bunch of CGOL stuff and then run with openmp. And this is another point - probably my main issue is my ability to edit and modify lifelib for my dynamic needs (once again how do I make it openmp friendly? for example).

I think there is still some work to be done until we can fully say just use this tool - there is everything in there. You did a lot of nice work to get us much closer there than ever before.
calcyman wrote:It's a header-only library, so it's best to have the lifelib repository in a subdirectory of your project.
Many thanks, I'll definitely run some searches in the near future using lifelib.

I also had this idea to use everything we have now but to modify it slightly in several ways:

My main application is iterating a lot on seemingly different patterns. So I thought

1. To use 8x8 and 64 bits. So that even if very small area is empty there will be no operations done on it. The
2. To have a global hash table, hardcoded into the library based on soup statistics. As it's only 8x8 the probability to hit it growth exponentially, and the performance doesn't suffer too much because we use 64 bits to their full capacity.

NOTE As for crypto currency - I've asked several people, and I'm working to promote the idea, just have nothing to say yet, but this is definitely on my radar.

User avatar
Saka
Posts: 3627
Joined: June 19th, 2015, 8:50 pm
Location: Indonesia
Contact:

Re: Lifelib

Post by Saka » February 25th, 2019, 10:21 am

Is it possible to run custom rules (ruleTables) in a JuPyter notebook? If so, how?

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » February 25th, 2019, 11:06 am

simsim314 wrote:And this is another point - probably my main issue is my ability to edit and modify lifelib for my dynamic needs (once again how do I make it openmp friendly? for example).
The lifetree data structure doesn't support concurrent modification, so if you want multiple threads to perform work at the same time, each thread should have its own lifetree. An easy way to copy a pattern from one lifetree to another is to create an empty pattern in the second lifetree and add the pattern to it.

Code: Select all

// Create a copy pat2 of the pattern pat1 and make it reside in lifetree lt2:
apg::pattern pat2(&lt2, "", "b3s23");
pat2 += pat1;
The overhead of moving a pattern between lifetrees is proportional to the number of nodes in the macrocell representation.
1. To use 8x8 and 64 bits. So that even if very small area is empty there will be no operations done on it. The
2. To have a global hash table, hardcoded into the library based on soup statistics. As it's only 8x8 the probability to hit it growth exponentially, and the performance doesn't suffer too much because we use 64 bits to their full capacity.
What you save in arithmetic operations, you lose in terms of bookkeeping and memory operations (including cache misses if you have a very large hash table). 8x8 is far too small on a modern CPU with vector instructions to be efficient; you'll be spending most of your time waiting for memory accesses.

The iterator "lifelib/upattern.h" (unhashed), used in apgsearch for everything other than custom ruletables/trees, uses the following combination of techniques:
  • A collection of overlapping 32x32 tiles (such that the 28x28 interiors are disjoint), arranged in a brick wall pattern so each tile has only 6 neighbours instead of 8;
  • Vectorised assembly routine to run two generations of a tile at a time and compute diffs to see whether a tile changed and which neighbours need updating;
  • Omission of tiles that haven't changed (thereby allowing it to completely skip over any stable P1/P2 tiles until they're impacted);
  • Ability to enable the automatic detection and removal of escaping gliders;
  • Ensuring that tiles are an exact multiple of 64 bytes in size, and aligned appropriately, to minimise the number of cache operations needed;
  • Allocating tiles in memory-contiguous blocks;
  • Containing fast pointers to the six neighbouring tiles, and falling back on a hashtable if a neighbour is unknown;
  • Caching tile populations for fast population determination, and falling back on __builtin_popcount() if the tile has changed since the last time the population count was determined;
  • A 64-element jump-table of function pointers for updating a tile based on an arbitrary subset of its 6 neighbours, so that 6 conditional branches can be reduced to just one (!!!).
Empirically, I found that 32x32 was faster than either 32x24 or 32x40 (on an AVX2 machine). If your machine supports AVX-512, apgsearch uses 32x48 tiles instead, because the trade-off between computation and memory access is different.

Tom Rokicki has a simpler algorithm (called list16x16) which is somewhat faster (on an AVX2 machine) for handling pure chaos (say, the first few generations of a large random torus), but otherwise upattern wins. That is to say, list16x16 has a higher cell-computations-per-second ratio, but upattern is faster precisely because it is able to omit a large number of unnecessary computations.

The hashlife implementation in lifelib (the usual "lifelib/pattern2.h") uses a hashed quadtree with 16x16 leaves. The same assembly implementation is used for running 32x32 blocks of cells (i.e. 2x2 blocks of leaves), but it runs them for up to 8 generations to compute the innermost 16x16 square.
Saka wrote:Is it possible to run custom rules (ruleTables) in a JuPyter notebook? If so, how?
Yes, by running:

Code: Select all

sess = lifelib.load_rules("path/to/your_rule.table")
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Lifelib

Post by simsim314 » February 25th, 2019, 6:28 pm

calcyman wrote:A collection of overlapping 32x32 tiles (such that the 28x28 interiors are disjoint)...uses a hashed quadtree with 16x16 leaves.
I wonder have you tried your tile algorithm for 16x16, and have you tried to use a global hash table based on million of soups? It's the best of all worlds, it uses hash and it's tile based and not tree based (which is faster and simpler).

In my opinion global hash + 16x16 tiles instead of 32x32 is the best "grey goo" manipulator, i.e. the most ideal algorithm for searches of any size.

And yes I meant 16x16 having 14x14 interior, just got confused.

------------

Unrelated I was playing with lifelib python, and it's great. I personally prefer in setu manipulation, but this is a nuance. Unfortunately I needed the tile algorithm which is not available in python. And the Life-Hash algorithm is not that fast for my purpose (I mean 100 times slower). Does the C++ supports tiles from the start? And how do I switch between the algorithms?

Also if I want to implement the algorithm posted above - using 16x16 tiles, where should I start? This should be an easy check. I can also add it as a branch in lifelib. All the benefits of the tiles you mentioned would be used out of the box, I only want to change the size and the logic to use 256 AVX a bit differently, without using anything outside the 16x16 square at all - no call to outside addresses. It's possible to compute in parallel all the tiles independently.

Also I think you're missing iterators, which are very useful for this types of searches. At least in python I prefer to use C++ iterators instead of python loops. Or this is some more general library and I missed it? My iterators know to take common area of iteration and make it ordered, so you will not cover the space several times.

EDIT Also for locating an object I'm using locators, which is a different algorithm than convolution which works best for small objects. For example locating glider. I'm using one object living cells as filter with dx, dy and AND operation for the other state. While I stop when nothing useful was found. Basically there is an optimal order for this thing, and most soups will be filtered after only three-four walkthroughs. There is a way to optimize the order as well.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » February 25th, 2019, 10:05 pm

simsim314 wrote:Also I think you're missing iterators, which are very useful for this types of searches. At least in python I prefer to use C++ iterators instead of python loops. Or this is some more general library and I missed it? My iterators know to take common area of iteration and make it ordered, so you will not cover the space several times.
Over what collection do you need to iterate?
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Lifelib

Post by simsim314 » February 26th, 2019, 7:49 pm

calcyman wrote:Over what collection do you need to iterate?
Say I have 4 gliders. Or 4 SLs I want to collide with glider.

wildmyron
Posts: 1542
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Lifelib

Post by wildmyron » March 1st, 2019, 5:41 am

Some buggy behaviour in Lifelib 2.1.18 (also present in 2.1.11) with the Generations genus. As before, I'm using the Python bindings in jupyter notebook.

First off, when exporting patterns with more than 3 states the RLE has cell states beyond the number of states in the rule.

This works:

Code: Select all

ltbb = lifelib.load_rules("g3b2s").lifetree(n_layers=2)
moon = ltbb.pattern('b2o$o2bo!')
m10 = moon[10]
m10.save("moon.rle")
m10.viewer(lv_config='#C [[ THEME 1 ]]')
but this results in an error from Lifeviewer - "Illegal state in pattern for generations"

Code: Select all

ltbb = lifelib.load_rules("g4b2s").lifetree(n_layers=3)
moon = ltbb.pattern('b2o$o2bo!')
m10 = moon[10]
m10.save("moon.rle")
m10.viewer(lv_config='#C [[ THEME 1 ]]')
and this is the contents of moon.rle (contains cells with state 6)

Code: Select all

x = 4, y = 4, rule = /2/4
.2A$A2FA$F2BF$B2.B!
From the little testing I've done, patterns are evolved correctly, and apgcodes are determined correctly, it's just the conversion to RLE that's problematic.

For 03467/25/6 lifelib is not correctly evolving the pattern. This pattern is a small p12 oscillator which doesn't escape it's 3x3 bounding box.

Code: Select all

lt = lifelib.load_rules("g6b25s03467").lifetree(n_layers=4)
p12 = lt.pattern('ACD$CDC$DCA!')
print(p12[1].population)
p12[1].save("p12.rle")
The population is reported to be 17 and p12.rle contains cells with state 14

Code: Select all

x = 5, y = 5, rule = B25/S03467/6
.2A$ANBE$ABDBA$.EBNA$2.2A!
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » March 1st, 2019, 5:54 am

Lifelib internally uses a different convention for Generations state numbers than Golly. It's supposed to handle these in RLE conversion, but maybe something isn't working as expected.

I'll write some test cases to cover RLE importing/exporting for Generations rules.
What do you do with ill crystallographers? Take them to the mono-clinic!

wildmyron
Posts: 1542
Joined: August 9th, 2013, 12:45 am
Location: Western Australia

Re: Lifelib

Post by wildmyron » March 14th, 2019, 11:10 pm

calcyman wrote:Lifelib internally uses a different convention for Generations state numbers than Golly. It's supposed to handle these in RLE conversion, but maybe something isn't working as expected.

I'll write some test cases to cover RLE importing/exporting for Generations rules.
The problem with interpretation of states seems to be more serious than just RLE conversion. This is still with 2.1.18, but there don't seem to have been any recent commits which modify this behaviour.

This snippet should show the population history of a domino initialised to states 0->5 in the rule 03467/25/6. Obviously undefined behaviour is expected for s>5 but I've included it because it seems to show the actual state correspondence. The output is the same with n_layers=3 and n_layers=4.

Input:

Code: Select all

import lifelib
lt = lifelib.load_rules("g6b25s03467").lifetree(n_layers=4)
for s in range(0,16):
    spark = lt.pattern()
    spark[0:2, 0:1] = s
    poplist = [spark[i].population for i in range(0,6)]
    print('s=%d: %s' % (s, poplist))
Output:

Code: Select all

s=0: [0, 0, 0, 0, 0, 0]
s=1: [2, 6, 12, 20, 32, 46]
s=2: [2, 0, 0, 0, 0, 0]
s=3: [2, 6, 10, 18, 30, 46]
s=4: [2, 2, 2, 2, 2, 2]
s=5: [2, 6, 12, 20, 32, 46]
s=6: [2, 2, 0, 0, 0, 0]
s=7: [2, 6, 12, 20, 32, 46]
s=8: [2, 2, 2, 2, 2, 2]
s=9: [2, 6, 12, 20, 32, 46]
s=10: [2, 2, 2, 0, 0, 0]
s=11: [2, 6, 12, 20, 32, 46]
s=12: [2, 2, 2, 2, 2, 2]
s=13: [2, 6, 12, 20, 32, 46]
s=14: [2, 2, 2, 2, 0, 0]
s=15: [2, 6, 12, 20, 32, 46]
Expected output for s = 0->5

Code: Select all

s=0: [0, 0, 0, 0, 0, 0]
s=1: [2, 6, 12, 20, 32, 46]
s=2: [2, 2, 2, 2, 0, 0]
s=3: [2, 2, 2, 0, 0, 0]
s=4: [2, 2, 0, 0, 0, 0]
s=5: [2, 0, 0, 0, 0, 0]
The 5S project (Smallest Spaceships Supporting Specific Speeds) is now maintained by AforAmpere. The latest collection is hosted on GitHub and contains well over 1,000,000 spaceships.

Semi-active here - recovering from a severe case of LWTDS.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » May 31st, 2019, 6:04 am

Chris Rowett has helpfully added a new feature to LifeViewer -- namely Ctrl+S -- in order to allow much tighter coupling between lifelib, Jupyter, and LifeViewer. In particular, if you create a pattern and view it, like so:
Screenshot from 2019-05-31 10-03-43.png
Screenshot from 2019-05-31 10-03-43.png (31.46 KiB) Viewed 15527 times
then there's a green message which mentions that you can use Ctrl+S to save edits. In particular, you can use LifeViewer's 'draw' tool to modify a pattern then press Ctrl+S to update the Python variable in-place. Consequently, it's no longer necessary to copy-and-paste between Jupyter and Golly in order to make minor edits to patterns.

I've tested on both Chromium and Mozilla Firefox, and it works as intended. The result is that you can freely mix visual editing with REPL scripting in a Jupyter notebook.

As usual, run pip install --upgrade python-lifelib to get the latest version, 2.2.18.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
testitemqlstudop
Posts: 1367
Joined: July 21st, 2016, 11:45 am
Location: in catagolue
Contact:

Re: Lifelib

Post by testitemqlstudop » June 7th, 2019, 3:05 am

In C++, is it possible to construct a pattern in arbitrary non-totalistic rules, like so:

Code: Select all

std::string seed2rule(uint64_t seed)
{
//rubbish
}

apg::pattern patternOfInterest(&lt, "o$bo$o!", seed2rule(seed));
where seed2rule iterates through a nontotalistic rulespace? I'm planning to use lifelib for a rule search program. No life libraries beside lifelib (LifeAPI, LifeCube, etc.) support NT, and I really don't want to use golly python.

User avatar
calcyman
Moderator
Posts: 2932
Joined: June 1st, 2009, 4:32 pm

Re: Lifelib

Post by calcyman » June 7th, 2019, 5:50 am

testitemqlstudop wrote:In C++, is it possible to construct a pattern in arbitrary non-totalistic rules, like so:

Code: Select all

std::string seed2rule(uint64_t seed)
{
//rubbish
}

apg::pattern patternOfInterest(&lt, "o$bo$o!", seed2rule(seed));
where seed2rule iterates through a nontotalistic rulespace? I'm planning to use lifelib for a rule search program. No life libraries beside lifelib (LifeAPI, LifeCube, etc.) support NT, and I really don't want to use golly python.
The design of lifelib has the unfortunate consequence that you can't change rules at runtime (beyond the <= 8 rules for which you've compiled lifelib).

However, it's certainly possible to hack lifelib to change the NT rule transitions during runtime, by overwriting the lookup tables. You'd need to immediately garbage-collect any lifetrees you're using, because they 'remember' how to evolve patterns.
What do you do with ill crystallographers? Take them to the mono-clinic!

User avatar
testitemqlstudop
Posts: 1367
Joined: July 21st, 2016, 11:45 am
Location: in catagolue
Contact:

Re: Lifelib

Post by testitemqlstudop » June 7th, 2019, 6:03 am

Thanks! I'll look into that, but it might be more efficient to write my own NT library...

User avatar
Scorbie
Posts: 1692
Joined: December 7th, 2013, 1:05 am

Re: Lifelib

Post by Scorbie » September 23rd, 2019, 5:35 am

Can one build only the cpp part of lifelib?? Would like to use the cpp part with Msys/mingw on windows...

User avatar
Scorbie
Posts: 1692
Joined: December 7th, 2013, 1:05 am

Re: Lifelib

Post by Scorbie » October 5th, 2019, 7:54 pm

Scorbie wrote:
September 23rd, 2019, 5:35 am
Can one build only the cpp part of lifelib?? Would like to use the cpp part with Msys/mingw on windows...
Now that I cloned the repo and read some of the source code and docs, I now get that the interaction between C++ and Python is not trivial.
@calcyman i would like to organize the repo with seperable parts (lifelib-cpp, lifelib-python) and make it into a python package so that running setup.py install <rule1> <rule2> ... would compile the rules.

Mainly to specify dependencies and not rely on Cygwin on Windows. (And distribute with any machine via wheels or compile in any machine with gcc or cl)
Do you think this would interfere with other language bindings, etc. with Wolfram Language bindings?

User avatar
muzik
Posts: 5614
Joined: January 28th, 2016, 2:47 pm
Location: Scotland

Re: Lifelib

Post by muzik » January 15th, 2020, 3:35 pm

Is there any benefit to keeping LtL support, given that it pointlessly duplicates the HROT rulespace?

GUYTU6J
Posts: 2200
Joined: August 5th, 2016, 10:27 am
Location: 拆哪!I repeat, CHINA! (a.k.a. 种花家)
Contact:

Re: Lifelib

Post by GUYTU6J » August 2nd, 2022, 12:13 am

Is installing Cygwin still mandatory when getting a copy of lifelib working on Windows? How to configure lifelib with WSL?

User avatar
pzq_alex
Posts: 792
Joined: May 1st, 2021, 9:00 pm
Location: tell me if you know

Re: Lifelib

Post by pzq_alex » August 3rd, 2022, 1:54 am

GUYTU6J wrote:
August 2nd, 2022, 12:13 am
Is installing Cygwin still mandatory when getting a copy of lifelib working on Windows? How to configure lifelib with WSL?
No! (sort of...) Here's how I managed to install python-lifelib on my win11 (with WSL):
  • Clone the python-lifelib repositry;
  • Run pip install numpy jupyter to install the packages numpy and jupyter with pip;
  • Inside the repositry, run the Python script install/install.py;
  • Now everything should be ready.
Note that this configures python-lifelib instead of lifelib, but this should work fine unless you need the C++ interface.
\sum_{n=1}^\infty H_n/n^2 = \zeta(3)

How much of current CA technology can I redevelop "on a desert island"?

User avatar
Coban
Posts: 26
Joined: June 26th, 2016, 3:50 pm
Location: NYC

Re: Lifelib

Post by Coban » August 9th, 2022, 2:40 pm

Is there an efficient way to use lifelib with full numpy arrays ?
I mean input/output a pattern as a array of 1s and 0s and ?
Nicolas

yoleo
Posts: 124
Joined: October 26th, 2021, 11:48 pm

Re: Lifelib

Post by yoleo » October 14th, 2022, 9:54 pm

Late reply: I found https://conwaylife.com/wiki/Tutorials/lifelib to be super helpful. Quoting something from APG in the tools channel of the discord:
x[array_of_coords] = array_of_states, where array_of_coords has shape (n, 2) and array_of_states has shape (n,).
A bit of googling then yields a chain of numpy commands that'll work:

Code: Select all

blinkerArray = np.array([[0,1,0],[0,1,0],[0,1,0]])
onCoords = np.array(np.where(blinkerArray == 1)).reshape(-1,2)
blinkerPat = lt.pattern('')
blinkerPat[onCoords] = onCoords.shape[0]*[1]
I can't speak to knowledgeably about efficiency or speed, though.

But the real reason why I wanted to post to this thread: I couldn't find a "hello world" equivalent for C++ Lifelib, so here's one.

Code: Select all

#include "pattern2.h"
#include <iostream>
#include <string>

int main(int argc, char *argv[]) {
    std::cout << "evolving a bun into a honeyfarm" << std::endl;
    apg::lifetree<uint32_t, 1> lt(1000);
    apg::pattern bun(&lt, "b2o$o2bo$3o", "b3s23");
    bun.advance("b3s23", 30).write_rle(std::cout);
    std::cout << "finished!" << std::endl;
}
Compiling on a Linux-based cloud machine with clang++-11, this works just fine. But if I try the same code on my MacBook Air, I get a boatload of errors. Seems like it's related to compiler versions...is there anyone who has gotten C++ Lifelib working on a Mac? If so, what compiler did you use?
Attachments
error_output.txt
(15.52 KiB) Downloaded 25 times
Last edited by yoleo on October 15th, 2022, 10:41 pm, edited 1 time in total.

Post Reply