Performing TDA on Lenia

Idea and package-selection

For the general pipeline I decided to work in python and find a way to efficiently compute cubical persistence for my collection of Lenia lifeforms. I decided to work with the package giotto-tda, which comes with all the basic functionalities that I needed for this project and can be used directly with my .vti-files.

Cubical Persistence

After loading in the .vti files (and reordering them to the correct shape) one can almost immediatly apply the CubicalPersistence() function provided by giotta-tda.

Cubical persistence

Note that periodic_dimensions are all set to True, since Lenia supports periodic boundary conditions.
Here the Cubical Persistence computation is carried out by the function 'compute_tda_per_frame()':
compute_tda_per_frame

The primary work is done by the fit_transform() function contained in giotto-tda's CubicalPeristence import. I also applied tqdm() to track the time for computation, as some lifeforms took up to 6 hours to compute, dependent on the number of frames and their resolution. For a given lifeform the results are then saved in a variable called 'diagrams'.
I furthermore included a functionality to save the respective diagrams after computation and to filter them according to an arbitrary minimal persistence.
save and filter

Life-Death-Diagrams

From these diagrams alone one can easily plot the persistence diagram, here given for an example lifeform.

Code Persistence Diagram

In this example it is already highly visible that some lifeforms contain a large number of low persistency features. In this specific case there were 795 features detected before filtering resulted in only 8 'true' features: Showcased here is the persistence diagram before filtering, as well as some metrics computed before and during filtration. Note that we used a fairly high minimal persistence of 200 here.
Persistence Diagram

Betti-numbers over time

One can then easily obtain the Betti-numbers for a given frame by summing over their occurances in the filtered diagram:

Betti Code


However, I am not only interested in the topology of a single frame, but particularly how it evolves over time. Therefore I am particularly interested in the change of Betti numbers over time, as well as a way to visualize it:
Betti over time Code


Which yields a fairly intuitive visualization of what is going on, as seen in this short sample set of three widely spaced frames of a single lifeform:
Betti over time example
In this particular case we observe a lifeform that seems to have a constant number of three voids (which as we will see in the case study section are hollow spheres), an increasing number of disconnected components and the formation of a loop.

Automated filtration value estimate through Otsu's method

Up to this point, I was mainly playing around with the functionalities of giotto-tda. However, I found it quite bothersome to manually choose minimal persistence values for every single lifeform, as fitting values did not seem to be consistent across different lifeforms. This is why I implemented an automatic functionality to determine a good minimal persistence using Otsu's method. The idea is to gather all persistence values from a diagram and use Otsu's algorithm to choose a fitting threshold to separate the low persistence population (probably noise) from the high persistence population (probably the true data signal) and then apply the obtained threshold as min_persistence value.

Code for Otsu's method


I furthermore included a helper function to see how the obtained threshold is distributed throughout all lifeforms:
Distribution of Otsu thresholds code


Which returns the following distribution:

Distribution of Otsu thresholds graph

Topological classification

It is noticeable that our lifeforms can already be grouped according to their ideal minimal persistence, or by how much noise they naturally contain. Most lifeforms have an Otsu threshold approximately between 100 and 160. At the same time there are a few lifeforms between 50 and 100 (34, 36, 38), a few having very small thresholds between 0 and 50, and a single outlier in lifeform 4, where the computed threshold is near the maximum value. For this particular lifeform however, a closer inspection reveals it to be actually really stable, even for small thresholds. This is due to a single change of Betti-2 by 1, while being otherwise almost completely static. This impacts the noise to signal ratio heavily due to how Otsu's method detects the thresholds. So it can be regarded as a technical outlier.

Another way to cluster lifeforms is by studying their different average Betti-numbers as a metric for their 0-, 1- and 2-dimensional complexity. This was realized with another helper function, as well as a function for plotting:

Code for computation of avg Betti numbers

Code for plotting avg Betti numbers


Which resulted in the following graph:
Graph of avg Betti numbers, log scaled

Including Betti-number evolution in animation

To get a better intuitive understanding for how the Betti numbers can be interpreted for a given lifeform, I decided to animate the Betti number evolution frame by frame by simply letting a vertical red line sweep through the Betti-number over time graph.
Code for making the Betti number animation
When synchronizing the frame rate with animations obtained in Paraview, I can then use video-editing software to synchronize the lifeforms evolution with the Betti number evolution:
Synchronized example
After describing the methodology, I will now showcase different examples of lifeforms and interpret the observations in the following section "Study of sample cases".
< >